Lithium is a lightweight, fast, flexible framework for PHP 5.3+. It is still in dev release state but is under active development. This post, as the title suggests, is to introduce you to this new framework using the typical blog tutorial.
Getting started
The first thing is to get the Lithium source code. You can either download the latest release from their download page or clone the git repository using -
- git clone code@rad-dev.org:lithium.git lithium
In both the cases, place the framework your webroot like /var/www/html or any equivalent location on your setup. Access the URL in browser and follow the instructions to setup the framework before we can actually begin with blog tutorial. When everything is setup correctly you should see a page like
Create database
Let’s first create a database. I have named my database as li_blog. Feel free to use the name of your choice. Now create the table required for our tutorial and populate it with some default data using the following SQL
- CREATE TABLE IF NOT EXISTS `posts` (
- `id` int(11) NOT NULL auto_increment,
- `title` varchar(255) NOT NULL,
- `body` text NOT NULL,
- `created` datetime default NULL,
- `modified` datetime default NULL,
- PRIMARY KEY (`id`)
- );
- INSERT INTO `posts` (`id`, `title`, `body`, `created`, `modified`) VALUES
- (1, 'First Post', 'This is post number 1', NOW(), NULL),
- (2, 'Second Post', 'This is post number two.\r\n\r\nSome content for this post', NOW(), NULL),
- (3, 'Third post', 'This is my third post.', NOW(), NULL),
- (4, 'Forth Post', 'This is the forth post.', NOW(), NULL),
- (5, 'Fifth Post', 'This is post number 5.', NOW(), NULL),
- (6, 'Sixth Post', 'My post number 6.', NOW(), NULL);
Routing
Open your app/config/route.php and replace the last section with the following. This is required in order to allow the pagination for index page to work
- /**
- * Finally, connect the default routes.
- */
- Router::connect('/{:controller}/{:action}/{:id:[0-9]+}.{:type}', array('id' => null));
- //Router::connect('/{:controller}/{:action}/{:id:[0-9]+}');
- Router::connect('/{:controller}/{:action}/page:{:page:[0-9]+}');
- Router::connect('/{:controller}/{:action}/page:{:page}/limit:{:limit}');
- Router::connect('/{:controller}/{:action}/{:args}');
Displaying list of posts
Now that we already have some default posts in our database, let’s begin with displaying the list of posts in index page. We will be showing 5 posts per page in descending order of creation date. For that lets begin with creating a model. Create a new file in your app/models and name it Post.php and add the following code to it
- namespace app\models;
- use \lithium\util\Validator;
- use \lithium\data\Connections;
- class Post extends \lithium\data\Model {
- public static function __init(array $options = array()) {
- parent::__init($options);
- $self = static::_instance();
- $self->_finders['count'] = function($self, $params, $chain) use (&$query, &$classes) {
- $db = Connections::get($self::meta('connection'));
- $records = $db->read('SELECT count(*) as count FROM posts', array('return' => 'array'));
- return $records[0]['count'];
- };
- }
- }
- ?>
Notice that we have written a custom finder to count the number of posts. This function will get executed whenever we call find(‘count’) on Post.
Now lets create a new file in app/controllers and name it PostsController.php Add the following code to the file
- namespace app\controllers;
- use app\models\Post;
- class PostsController extends \lithium\action\Controller {
- public function index() {
- $page = 1;
- $limit = 5;
- $order = 'created desc';
- if (isset($this->request->params['page'])) {
- $page = $this->request->params['page'];
- if (!empty($this->request->params['limit'])) {
- $limit = $this->request->params['limit'];
- }
- }
- $offset = ($page - 1) * $limit;
- $total = Post::find('count');
- $posts = Post::find('all', compact('conditions', 'limit', 'offset', 'order'))->to('array');
- $title = 'Home';
- return compact('posts', 'limit', 'page', 'total', 'title');
- }
- }
- ?>
Almost all code here is self explanatory. Note that every action must return the variables to make them available in views. Also, by default, find() returns the object of model. We are calling a method to(‘array’) for convenience.
Now that we have all the data required to display the list of posts, lets create a view for displaying it. Create a new fie in app/views/posts (create the directory posts first) and name it index.html.php. Note that Lithium follows some naming conventions and all the filenames and class names must be according to those conventions only. Add the following code to the newly created view file
- <h1>html->link($post['title'], 'posts/view/'.$post['id']); ?></h1>
- html->link('Edit', array('controller' => 'posts', 'action' => 'edit', 'args' => array($post['id']))); ?>
- <!-- Pagination section -->
- <div id="pagination">
- <p class="next floated">if ($total <= $limit || $page == 1) {
- //echo '</p>
- <ul>
- <li>Next Entries →';
- } else {
- echo $this->html->link('Next Entries →', array(
- 'controller' => 'posts', 'action' => 'index',
- 'page' => $page - 1, 'limit' => $limit
- ), array('escape' => false));} ?>
- <p class="prev">if ($total <= $limit || $page == ceil($total/$limit)) {
- //echo '← Previous Entries</p>
- ';
- } else {
- echo $this->html->link('← Previous Entries', array(
- 'controller' => 'posts', 'action' => 'index',
- 'page' => $page + 1, 'limit' => $limit
- ), array('escape' => false));
- }?></li>
- </ul>
- </div>
Lithium comes with default helpers for html and form rendering and are auto-loaded in rendering context. Just use them by referring as $this->html and $this->form.
Go to your browser and point it to http://localhost/lithium/posts and you should see a list of posts with pagination.
Adding new post
We will now proceed with adding a new post entry for our blog. In that process, we will also see how to add validations, use custom validations and use of save filter.
Open your PostControllers.php and add the following method to it after the existing index method.
- public function add() {
- if ($this->request->data) {
- // Create a post object and add the posted data to it
- $post = Post::create($this->request->data);
- if ($post->save()) {
- $this->redirect(array('action' => 'index'));
- }
- }
- if (empty($post)) {
- // Create an empty post object for use in form helper in view
- $post = Post::create();
- }
- $title = 'Add post';
- return compact('post', 'title');
- }
The POST data is made available to us through $this->request->data. We are simply checking whether the post data is available and if so save it by calling the save() method. Before the data gets saved in the database, I would like my data to be validated. For this, I first need to specify the validation rules in the Post model. So now, open the model file Post.php and add the following just after the class definition
- public $validates = array(
- 'title' => array(
- array('notEmpty', 'message' => 'Title cannot be empty'),
- array('isUniqueTitle', 'message' => 'Title must be unique'),
- ),
- 'body' => 'Please enter some content for this post',
- );
Here, notEmpty is the built-in validation rule and Lithium will check whether some title is entered or not. But isUniqueTitle is a custom validation rule as I want my post titles unique. Being a custom validation rule, I must write the implementation for this rule as well. Add the following code to __init() method just after count finder.
- Validator::add('isUniqueTitle', function ($value, $format, $options) {
- $conditions = array('title' => $value);
- // If editing the post, skip the current psot
- if (isset($options['values']['id'])) {
- $conditions[] = 'id != ' . $options['values']['id'];
- }
- // Lookup for posts with same title
- return !Post::find('first', array('conditions' => $conditions));
- });
I also want to update the created/modified fields according to the action I am performing. Add the following code in continuation to the above code
- Post::applyFilter('save', function($self, $params, $chain) {
- $post = $params['record'];
- if (!$post->id) {
- $post->created = date('Y-m-d H:i:s');
- } else {
- $post->modified = date('Y-m-d H:i:s');
- }
- $params['record'] = $post;
- return $chain->next($self, $params, $chain);
- });
Nothing very special in the above code except the return statement. We are calling next filter in the chain to make sure that all the filters run correctly. And now the form to add the data for post. Create a new file in app/views/posts and name it add.html.php. Add the following content to it
- <h2>Add new post</h2>
- $this->form->config(array('templates' => array('error' => '
- <div class="error">{:content}</div>
- ')));
- ?>
- form->create($post); ?>
- form->field('title');?>
- form->field('body', array('type' => 'textarea', 'rows' => 10));?>
- form->submit('Add Post'); ?>
- form->end(); ?>
Form helper displays the validation errors for every field on its own. It creates a DIV element below the form element but doesn’t apply any CSS class to it. But Lithium provides us a way to override any of the default element templates. This is exactly what I have done on line 3 in above code. If any one of you know the better way please let me know, I will update my code accordingly.
Again, point your browser to http://localhost/lithium/posts/add. You should see a form to enter post. Play with it to test validations. Finally you should be able to save the data in database.
Editing the post
Next comes the edit post entry. Lets dive into the code directly. Add the following code to PostsController.php after add()
- public function edit($id = null) {
- $id = (int)$id;
- $post = Post::find($id);
- if (empty($post)) {
- $this->redirect(array('controller' => 'posts', 'action' => 'index'));
- }
- if ($this->request->data) {
- if ($post->save($this->request->data)) {
- $this->redirect(array('controller' => 'posts', 'action' => 'index'));
- }
- }
- $title = 'Edit post';
- return compact('post', 'title');
- }
Simple, right? Just remember that Post::find() is must before we call save(). Otherwise it will always insert a new entry in database. The URL to access this page will be naturally http://localhost/lithium/posts/edit/1 where 1 is the id of the post to edit.
View is mostly similar to add page. Create a new file in app/views/posts and name it edit.html.php and add the following code to it
- form->create($post, array('method' => 'post')); ?>
- form->hidden('id'); ?>
- form->field('title');?>
- form->field('body', array('type' => 'textarea'));?>
- form->submit('Add Post'); ?>
- form->end(); ?>
All the validation rules will be automatically applicable for edit as well. For our custom validation rule we have already taken care of edit functionality by checking for post id in data. So, no change required in model.
Lets now proceed to the final action of our post controller, delete. In PostsController.php add the following code after edit() action
- public function delete($id = null) {
- $id = (int)$id;
- $post = Post::find($id);
- if (empty($post)) {
- $this->redirect(array('controller' => 'posts', 'action' => 'index'));
- }
- $post->delete();
- $this->redirect(array('controller' => 'posts', 'action' => 'index'));
- return;
- }
That’s it. No view required for delete. The above code will check whether the post with given id really exists and if it does, deletes the post and redirects to index action.
The whole code used in this tutorial is available in Github repository. For the sake of better display, I have slightly modified the default layout and the style.css in webroot/css. You can check out those files from repository.
This is just the beginning. A lot more can be added to this like adding comments for posts. This will follow in the next part. Till then try out this tutorial and don’t forget to share your experience with us.
Read Part 2 here


Thanks for the post Aditya. Very useful. I think a thorough comparison between CakePHP and Lithium would be really helpful. Is that something you guys might be interested in compiling and blogging about?
Neils – at this stage CakePHP rules when it comes to real world development but then Lithium is still 0.8 once they announce a stable 1.0 a comparison would be fair. Probably the single biggest thing holding me up from using Lithium is lack of relations in the model once they have that working…..
Are model relations definitely on the Roadmap?
Yes definitely. Check it out -> http://rad-dev.org/lithium/wiki/about/roadmap
Loved the tutorial mate, just what i was looking for.
Hey man, can you go through your code by chance and comb for bugs, I’m seeing massive build-time errors in the PHP (like absence of starting html” ) which would really be difficult for most people to understand
absence of starting >?php tags *
bah, I mean absence of <?php tags
Instead of
$self = static::_instance();you mean
$self = static::_object();since _instance requires >=1 arguments and _object not