Writing a simple blog with Zend Framework and mongoDB

Zend Form Mastery with Zend Config

This post came about after a recent experience starting to extend a personal website of mine. It’s a simple site that I started writing in the Zend Framework some years ago and which I work on, occasionally. With it I enjoy that complete creative freedom that often only comes with something that’s all your own. On that site, I’ve wanted to have a blog for quite some time and recently I have started learning about and working with NoSQL databases, including CouchDB and mongoDB.

What’s more, I’ve been building a simple fileserver with mongoDB and Zend Framework as a first test of putting the two together. As that project is going very well, I thought that I should apply what I’d done there to create an ultra-simple blog. So this post will document some of the process, giving you a simple introduction into combining the two technologies. This is not going to be too detailed, just a warm up, but I’m planning to write a couple of posts building upon this one.

What’s Mongo? If you need a great introduction to Mongo? Grab a copy of the Little MongoDB Book by Karl Seguin.

Post Requirements

What you’re going to need to follow along with this post is:

  • Working knowledge of the Zend Framework – version 1.1o or above – with the zf tool available
  • A working development environment with a web server, such as Apache 2
  • An installation of mongoDB
  • An open mind to try new things :-)

Background on Mongo

But first a quick bit of background on mongoDB. According to the official site, MongoDB is:

MongoDB bridges the gap between key-value stores (which are fast and highly scalable) and traditional
RDBMS systems (which provide rich queries and deep functionality). MongoDB (from “humongous”) is a scalable, high-performance, open source, document-oriented database.

It’s written in C++ and sports the following features:

  • Document-oriented storage
  • Full index support
  • Replication and High-Availability
  • Auto-Sharding
  • Querying
  • Fast In-Place updates
  • Map/Reduce and
  • GridFS

All in all, this makes it a good system if you’re looking for an alternative from traditional RDBMS’s.

PHP Libraries

There are a number of high-quality, freely available, PHP libraries, including the PHP extension, Zend_NoSQL_Mongo and Shanty-Mongo. For this post, I’ve settled on Shanty-Mongo as it integrates very quickly with Zend Framework and is pretty feature rich. I’ll not get in to too much detail about it here, as you can find out lots more on the website.

Setting up the Base Project

As the key focus of the project is more about mongoDB rather than Zend Framework, I’ll assume a lot of prior knowledge of how to setup and manage the project component. However to assist getting everything up and running quickly, run the following commands to get the base project directory ready to go.

zf create project mini-blog
zf create config
zf enable layout
zf create module posts
zf create controller Index 1 posts
zf create controller Create 1 posts
zf create controller Delete 1 posts
zf create controller Update 1 posts
zf create controller View 1 posts

Adding Mongo support to your project

After that grab yourself a copy of Shanty-Mongo from Github. Simply run the following command:

git clone git://github.com/coen-hyde/Shanty-Mongo.git

Then add the library/shanty directory, in the uncompressed file, to the library directory of your project. Then, in your application.ini, add the library to the project’s namespace in the production section with:

autoloaderNamespaces[] = "Shanty_"

When that’s done, you’re ready to start using it. In addition to this, add autoloaderNamespaces[] = “Common_”. We will need this for our classes.

Modelling with Mongo

As we covered earlier, Mongo is a document-oriented storage system. So to use it right, you have to think in terms of documents, not rows, tuples, tables and schemas in the like of MySQL or Oracle. This makes it simpler, I believe, to work with Object-Oriented development languages such as PHP. So I’ve created a document class below that we’ll use for our posts. Have  a look at it below and then we’ll cover how it works.

// a simple class to model a post
class Post extends Shanty_Mongo_Document
{
protected static $_db = 'forum';
protected static $_collection = 'posts';
protected static $_requirements = array(
'<span style="font-family: monospace;">author' =&gt; 'Required',</span>

<code> 'title' =&gt; 'Required',
'body' =&gt; 'Required',
'createdDate' =&gt; 'Required',
'publishedDate' =&gt; 'Required',
'status' =&gt; array('Validator:InArray' =&gt; array('draft', 'published'),
));
}

The _db property links the document to the database that it will be stored in. The collection is the name of the collection of documents that this one will be a member of. The _requirements array is the method of enforcing document validation constraints. For our post, we're enforcing that the post has an author, title, body, created date, published dateand status. This will help us keep proper document integrity.

Setting up the Post Module

Once the document class is setup, then it's time to flesh out the Posts controller.

Adding Posts

In the add document action after we've validated and processed the form, we'll have available the details for our post. So then we'll create a new Post document, store the information in it and save it. Sounds simple right? It is. Have a look at the code snippet below.

public function createAction()
{
$blogPost = new Common_Blog_Post();
$blogPost->author = $this->_request->getParam('author');
$blogPost->title = $this->_request->getParam('title');
$blogPost->body = $this->_request->getParam('body');
$blogPost->createdDate = $this->_request->getParam('createDate');
$blogPost->publishedDate = $this->_request->getParam('publishedDate');
$blogPost->status = $this->_request->getParam('status');
$blogPost->save();
}

Now this assumes that you've already setup the code to build, populate and process a form, which I'll assume is based on Zend_Form along with Zend_Config components. I'm glossing over this for this post given that we're not specifically focusing in that area of the framework. So please bear with me in that respect for now.

Updating Posts

Ok, so it's great that we've saved a post, but what about updating one. Not that hard honestly. It's not that much different from adding them. Firstly, you need to instantiate a post if you can find it in the database, which we'll do based on it's title, in the database. Then, once you have the document, update the properties of it as required, then save it. That's it. So let's have a look at some sample code.

public function updatePost() {
// attempt to open a post if we can find it
$blogPost = Common_Blog_Post::fetchOne(
array('title' => $this->_request->getParam('title'))
);
// we found the post, so let's update the properties
$blogPost->author = $this->_request->getParam('author');
$blogPost->title = $this->_request->getParam('title');
$blogPost->body = $this->_request->getParam('body');
$blogPost->createdDate = $this->_request->getParam('createDate');
$blogPost->publishedDate = $this->_request->getParam('publishedDate');
$blogPost->status = $this->_request->getParam('status);
$blogPost->save();
}
The fetchOne method, which is inherited from the base Shanty_Mongo_Document class, will return a document if it finds one. This really helps us keep this simple. There are a variety of other methods, but we'll leave that for later posts.

Deleting Posts

Ok, so we've covered creating and updating posts. What about removing posts. As with the updatePost action, the deletePost action will use the findOne method to attempt to find and load an existing post, based on the post title. If we do find it, then we run the delete method on it. It should be noted that when we take a document out of one database, any connected databases will remove the document when the next replication process occurs. We don't need to write any extra code to make this happen. What a relief. So what's the code?

public function deletePost() {
// attempt to open a post if we can find it
$blogPost = Common_Blog_Post::fetchOne(
array('title' => $this->_request->getParam('title')));
// we found the post, so let's delete it
$blogPost->delete();
}

Viewing Posts

Well, not much left to go now. So let's look at the list item - viewing a list of posts. For this, we're going to use the 'all' method. This will return a Shanty_Mongo_Iterator_Cursor object that allows us to iterate over a collection of our posts. Nice and simple. What we'll do in our viewPosts controller is to assign the object as a view variable and then iterate over it in our view template. Have a look at the code below for a sample.

public function viewAction() {
// attempt to open a post if we can find it
$blogPosts = Common_Blog_Post::all(<span style="font-family: monospace;">);
// assign the object as a view variable
$this-&gt;view-&gt;posts = $blogPosts;
}

Now in the view template:

<?php foreach ($posts as $post) : ?>
<a href='/posts/<?php print $post->title; ?>'><?php print $post->title; ?></a>
<p><?php print $post->body; ?></p>
<!-- print the rest of the post information -->
<?php endforeach; ?>

It really is that simple.

Winding Up

Now this article should have given you a introductory idea of just how easy it is to integrate MongoDb and the Zend Framework, specifically with the Shanty-Mongo library. I appreciate that there are a lot of things that I've not covered here, like the forms, proper security and more thorough validation. I've also not gone in to a lot of detail about the wide variety of features that are available with MongoDB, such as storing documents and exactly how replication works. But I hope that I've whet your appetite for learning more.

Stay tuned. In the next post, I'm going to get in to a bit more detail about storing files and some of the deeper mechanics of MongoDB and the Shanty-Mongo library. After that we'll get in to some of the fun of GridFS.

Further Reading

If you liked what you read and would like to see more, please retweet it, or give it a like on facebook or even give it some digg love.

till next time,

Matt

Frameworks MongoDB Software Development Zend Framework
  • Brown

    Nice tutorial. One question I have is how do i use Zend_Paginator with this?. The array and iterator adapters did not work for me. Is there a better way besides first loping through the cursor to create your own array?

    • matthew

      Hi Brown and thanks for your comment. I had the same problem myself, but given the limited time I had when I wrote the post, I decided to go with the simpler option of only looping through the results. I’m planning to look at the library more closely during January to see about getting it to work with iterators. That’d definitely make it simpler for sure.

  • Eric

    Hi,
    Nice post. Quite unsecure blog btw.

    ++

    • Matthew

      Hi Eric,

      Yes that’s true, it is. It was just aimed at the content aspect without any consideration for other areas. Maybe we should post a follow up to it?

      Thanks for replying too,

      Matt

  • Pingback: Zend Framework, a truly RAD framework! | Malt Blue

  • arian

    Hi Eric,

    I was wondering if there a way to make “protected static $_db = ‘forum’;” part dynamic.

    database name comes dynamically and i want to pass it into model before the connection is made.

    thanks for the tutorial,

    arian

  • Pingback: Zend Framework and mongoDB – Getting Started Quickly! | Malt Blue

  • Steve

    class Post extends Shanty_Mongo_Document

    $blogPost = new Common_Blog_Post();

    Who is Common_Blog_Post ?

    • http://www.maltblue.com/ matt

      Steve,

      I’m not sure what you mean by who is? What exactly do you mean?

      Matt

    • http://www.matthewsetter.com/ maltblue

      Sorry to not be clearer on this one @steve but Common_Blog_Post was a class in my Common_ library.

  • AndreaDiMario

    Hi, i’m new of zend and i have some problems. I’ve put the library on zend directory: library/Shanty/

    Then, i’ve added in application.ini: autoloaderNamespaces[] = “Shanty_”

    a class models/User.php with:

    class User extends Shanty_Mongo_Document {

    protected static $_db=’apps’;

    protected static $_collection=’user’; }

    and in controllers/UserController.php:

    public function viewAction() {

    $user = new User(); }

    But, i’ve this error:

    class User extends Shanty_Mongo_Document { protected static $_db=’apps’; protected static $_collection=’user’; }

    Fatal error: Class ‘User’ not found in /var/www/zendtest/application/controllers/UserController.php on line 19

    Could you give me some help on it?

    Thanks for your time. Best regards.

    • http://www.thedreamersmanifesto.com/ thedreamersmanifesto

      @AndreaDiMario Hi there and thanks for reading the post. It’s only a little thing that you’re missing. Instead of calling the class User, call it Application_Model_User. Give that a go and let me know if you’re still having troubles.

      • http://www.thedreamersmanifesto.com/ thedreamersmanifesto

        @AndreaDiMario When you’ve got it up and running, I’d love to hear what you think of the Shanty Mongo library.

      • AndreaDiMario

        @the dreamers manifesto@AndreaDiMario Hi, thanks for your reply, i’ve tried this and other possible solutions without success. It strange because it write me back the class as if i’ve done an echo and then the error.

        • http://www.matthewsetter.com/ maltblue

          @AndreaDiMario Andrea, sorry to take so long to get back to you (@thedreamersmanifesto) is me btw. Can you email me a copy of your code? That way I can have a look over it and try and replicate the issue.

  • AlessioDeZotti

    Hi there, I’m playin’ around with zend framework 1.11 and mongo, but I got stuck on a problem i’ve posted to stackoverflow too. Unluckily, I’ve received no answer at all, and I’m asking this to you since this looks the only good ref for making zend and mongo work together, and could be updated with this additional “step”.

    This is my question in detail: http://stackoverflow.com/questions/9044363/shanty-mongo-and-zend-framework-1-11.

    Looks like i need to configure a sort of connection, even if I’m working in localhost with no user / pass.

    Could you please help?

    Thanks!

    Alessio

  • dhruvin

    Hi there, I have implemented a simple blog application similar to this application of yours, in there i am able to add new entries , delete entries and update them as well,
     
    but my problem is i am not able to populate any single entry in zend form for editing purpose,
     
    i have tried ,
     
    $blogPost = Application_Model_Post::fetchOne(array(‘Email’ => $email));
    $form->populate($blogPost);
     
    $blogPost = Application_Model_Post::find($email));
    $form->populate($blogPost);
    etc..
    but it gives me, Catchable fatal error: Argument 1 passed to Zend_Form::populate() must be an array, object given,
    what am i doing wrong here can you help me , much appreciated..

    • http://www.matthewsetter.com/ maltblue

      @dhruvin

    • http://www.matthewsetter.com/ maltblue

      @dhruvin I’m not sure as I sit here. However I’ll go back through the code and post a better answer Friday for you.

      • http://www.matthewsetter.com/ maltblue

        @dhruvin sorry about not getting back to you as promised. Will sort that out shortly.