I’ve recently been toying with the Lithium framework, a fork of some code produced by some of the CakePHP devs that was spawned into its own being. I have few grievances with CakePHP and used it for a couple of smaller projects a couple years ago. Yet, as PHP has matured, so has the language and there are a ton of features in PHP5 that make it difficult to fully appreciate a framework dedicated to backward compatibility as in the case of CakePHP.
This isn’t a slight on CakePHP at all. It is just a difference between where I want to be programming and where CakePHP is as a framework. If anything, CakePHP is right where it needs to be – catering towards the strength of a giant PHP4 and PHP5 installed-base with a strong and stable code base.
Lithium is bleeding edge, a little rough and presents PHP5 in near-full-glory. Being familiar with CakePHP, it makes it nice to find some familiar concepts in Lithium while I have the opportunity flex some of PHP5′s new features. But enough about me and Lithium, let’s get on with extending the tutorial.
Note: I’m still feeling around Lithium and many of the concepts so there may be some inefficiencies or redundancies in the code. Also, this is an extension of the demo, as such, there is virtually nothing in the way of security implemented. Remember, always wear protection!
The current (as of this writing) blog tutorial ends right where the fun begins. The original tutorial shows you how to set up the index and add methods in the PostsController. In each part, you have the opportunity to do some basic interfacing with MongoDb by doing a basic find and insert query respectively.
But we want to do more. How about we create the ability to see the post all by its lonesome and add a view ( as in view-a-single-post, not view as in MVC) method to the PostsController.php.
public function view($id=null) {
// we are going to view a page.
if(!is_null($id)) {
$post = Post::find('first', array('_id'=>$id));
return compact('post');
}
}
Basically, when an id is passed on the URL, we pass the id into the database query and look for the matching post in the database. In this case, we want to pass the MongoDB object ID ‘_id’ in the URL. (Note: this can be changed to some other field or combination but you’ll need to devise a method to parse the query parameter to find the right post). We end the method by returning the results as an array to make them available in the View.
Next, we’re going to create a new file in /views/posts/ folder called view.html.php
<article>
<h1><?=$this->html->link($post->title,'/posts/view/'.$post->_id); ?></h1>
<p><?=$post->body; ?></p>
</article>
This is just a simple view that shows us the title and body of the post. Alright! Now we have a method and a view associated with the new method. Did we forget something?… How about: how are we going to get to the individual post if we don’t really know what the unique ID is?
Easy peasy. Now open up /views/posts/index.html.php and change
<h1><?=$post->title ?></h1>
To:
<h1><?=$this->html->link($post->title,'/posts/view/'.$post->_id); ?></h1>
When you loaded the index page, you ran a foreach to display each post. Although present, you didn’t use all of the available variables passed to your template. All we are doing is creating a link to our new method, view, passing the unique MongoId on the end of the URL.
Go ahead and save your changes. We should be in business! Go to your post controller at http://mysite.com/posts/ to view the index page. You should see that each title you see is a link. If you mouse-over the link you’ll see a URL like
http://mysite.com/posts/view/4d01a6cc5096344818020000
The long string on the end will be different of course. But each blog post will show a unique id. Click on the link and it will take you to view your post.
Now we’re going to get a little crazy. We want to add some comments to our blog posts. Notice that when we has the add method that there weren’t any comment fields in the document we sent to the database. That’s fine. we’re ok – MongoDb is essentially schema-less and we need to think of a blog post as a document, not as some octopus-like series of relationships. Remember, KISS – “keep it simple, stupid”.
Let’s go back to our whiz-bang PostsController.php file. Plug the following code into your class:
public function comment($id=null) {
// check to make sure the request is set
if(!is_null($this->request->data)) {
$data = &$this->request->data;
//we'll set up our query
$query = array(
'$push'=> array('comment'=>array(
'title'=>$data['title'],
'contact'=>$data['contact'],
'body'=>$data['body']
)
)
);
//set up the conditions
$conditions = array('_id'=>$id);
// execute the qeury
$result = Post::update($query, $conditions, array('atomic' => false));
}
// you'll want to add checks, but for simplicity, we'll just send them back to the individual post
$this->redirect("/posts/view/$id/");
}
}
Now this is a little bit more complicated. In the code above, we define the query as an array of keys and values. Be very careful here. ‘$push’ is a native MongoDb command. Make sure you are using single quotes (a literal string) around it so PHP doesn’t look for a variable named $push. The MongoDb command $push essentially helps us do two things: if ‘comment’ is a defined field in our database, it updates the field by appending the new information to the old information. If the field is not defined, it defines the field and adds the data. If we don’t use the ‘$push’ command and just pass the query, shortened to the data being added, it will just overwrite what is already there after each comment.
As we move down through the code, you see that we set up the conditions to look for a matching MongoId. We then invoke the update. Notice that the query comes first, the conditions second, and we have a third item there that is extremely important. We set ‘atomic’ to false. If we do not do this, Mongo will treat our query as a raw update; that $push command will become a key added to the document rather than the command telling Mongo how to deal with the rest of the array. And we’re finally finished in the controller. But we don’t need to show a view specific to this method. We can just send them back to view the post they commented on.
Go ahead and go back to /views/posts/view.html.php in your editor. We’re going to do a couple quick edits. We are going to add a loop to view the comments after the blog post. The second thing we’ll do is add the form to post a new comment. After the closing article tag, paste in the following.
<div class="comments">
<h2>Comments</h2>
<?php if(isset($post->comment)): ?>
<?php foreach($post->comment as $comment): ?>
<div class="comment">
<h3><?= $comment->title; ?></h3>
<p><?= $comment->contact; ?></p>
<p><?= $comment->body; ?></p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="commentform">
<h2>Add a Comment</h2>
<?=$this->form->create(null, array('action'=>'comment/'.$post->_id)); ?>
<?=$this->form->field('title');?>
<?=$this->form->field('contact');?>
<?=$this->form->field('body', array('type' => 'textarea'));?>
<?=$this->form->submit('Add Comment'); ?>
<?=$this->form->end(); ?>
</div>
This form and display is pretty straight forward. Because our view model already grabs a document by the MongoId, we have the full document and all of the comments. We use the HTML helper to help us create a quick and dirty form. On the back side, we grab all of the form fields from $this->request->data.
At this point, you should be able to go to your site and see your posts. You can click on the title of a post and view the post. The post page should show you the post, comments and a form field to add comments. When you add a comment, you should be returned back to view the post and see your new comment added.
Not bad for quick and dirty. Sure, it needs some work, but it should get you along a little further in your study of Lithium.