A CodeIgniter active record library for MongoDB

This evening I started work on an active record inspired library for interacting with MongoDB via a CodeIgniter library.

So far I’ve got searching and inserting working. Updating and deleting are going to take more time because they’re a bit trickier.

What this means is you can easily work with a database collection like so:

$this->mongo_db->where_gte('age', 18)->where(array('country' => 'UK', 'can_drink' => TRUE))->get('people');

Or:

$this->mongo_db->get_where('posts', array('title' => 'Hello, World!');

Or just:

$this->mongo_db->get('sales');

I’ve created a repository on Bitbucket located at http://bitbucket.org/alexbilbie/codeigniter-mongo-library. If you have a look at the README file there is an overview of what is there so far and also what needs doing (and some maybes!).

Hope people find it useful.

MongoDB + CodeIgniter 101 (Part 1)

So I’ve decided that I want to properly document my MongoDB exploration and I may as well help others to learn with me.

Big disclaimer: I’m no MongoDB expert, I really am starting from scratch so if I get something wrong or you know of a better a way I’d love to hear from you =)

What I’m going to do here in part 1 is install MongoDB, install the PECL PHP extension and just play around a bit.

So lets get going.

Installing MongoDB

First we need to install Mongo (if you haven’t already). I’m working on a Ubuntu 9.04 server so I added the 10gen repository to my Aptitude repo list.

nano /etc/apt/sources.list

Add the following to a new line

deb http://downloads.mongodb.org/distros/ubuntu 9.4 10gen

(Take a look here to see some alternative Ubuntu + Debain sources http://www.mongodb.org/display/DOCS/Ubuntu+and+Debian+packages)

Save the file (Control + X)

Then run

apt-get update
apt-get install mongodb-stable

Wait a few minutes and MongoDB is installed! For reference, the Mongo database path is located at /var/lib/mongodb/ and the configuration script is at /etc/mongodb.conf

Installing MongoDB PECL extension

Install the PHP developer package (if you haven’t already)

apt-get install php5-dev

Then run

pecl install mongo

Working directory setup

Set up your working directory and install the latest stable CodeIgniter snapshot

cd /public_html
mkdir mongo101
cd mongo101
svn co http://svn.ellislab.com/CodeIgniter/trunk/
mv trunk/* ./
rm -r trunk

Lets go!

Open your favourite text editor (mine is Coda) and setup a new project etc etc.

Open /application/config/config.php and change the $config[‘base_url’] to wherever your install is.

Open /application/config/route.php and change the default controller to blog -$route[‘default_controller’] = “blog”;

Delete the welcome.php controller in the application/contollers folder

Delete the welcome.php view in the application/views folder

Create a new controller called blog.php

<?php

class Blog extends Controller {

 function Blog()
 {
   parent::Controller();
 }

 function index()
 {
 }

}

In your index function write

function index()
{
 // Connect to Mongo
 $connection = new Mongo('localhost:27017');

 // Select a database
 $db = $connection->blog;

 // Select a collection
 $posts = $db->posts;
}

Save the file and go to http://your-server/index.php/blog

If you’ve got a blank screen then it means you’ve successfully connected to Mongo, selected a database and selected a collection within the database.

Right, time for some concepts. So in Mongo a database is as it sounds, a database, and a collection is like a table in MySQL in that it holds rows (except rows are documents in Mongo terms). The PHP Mongo extension is to some extent an object relationship mapper which means we could have quite easily gone $posts = $connection->blog->posts instead.

Now that we’ve covered the basics and we know everything is working lets turn this into something a bit more useful.

Edit the Blog (constructor) function in your controller.

function Blog()
{
parent::Controller();
$this->load->helper('url');
// Connect to Mongo
$this->connection = new Mongo('localhost:27017');
// Select a database
$this->db = $this->connection->blog;
// Select a collection
$this->posts = $this->db->posts;
}

Here we’re making the posts collection globally available so we don’t have to repeat ourselves in every function with the connection code.

Add a new function to your controller:

function add()
{
  $data = array();

  // If we've submitted the form...
  if($this->input->post('add'))
  {
  // Build the document
  $document = array(
    'id' => ($this->posts->count() + 1),
    'title' => $this->input->post('title'),
    'body' => $this->input->post('body'),
    'author' => $this->input->post('author'),
    'date' => time()
  );

  // Insert the document
  $this->posts->insert($document);

  $data['inserted'] = TRUE;
  }

  // Load the form
  $this->load->view('add', $data);
}

When the form is submitted we build an array and then insert that into our collection. You’ll see to generate the ID we asked Mongo to return the number of existing documents in the collection and added one to it.

Create a new view file add.php

<!DOCTYPE html>
<html>
 <head>
 <title>MongoDB 101 (Part 1)</title>
 </head>
 <body>

 <h1>Add a new post</h1>

 <?php
 if($inserted):
 ?>
 <p><strong>Post inserted successfully!</strong></p>
 <?php
 endif;
 ?>

 <form method="post">

 <p>
 <label for="title">Title</label><br/>
 <input type="text" name="title" value="" />
 </p>

 <p>
 <label for="body">Body</label><br/>
 <textarea name="body"></textarea>
 </p>

 <p>
 <label for="author">Author</label><br/>
 <input type="text" name="author" value="" />
 </p>

 <p>
 <input type="submit" name="add" value="Add" />
 </p>

 </form>

 <p><a href="<?php echo siteurl(array('blog', 'add')); ?>">Add a post</a></p>
 <p><a href="<?php echo siteurl(array('blog', 'index')); ?>">View all posts</a></p>

 </body>
</html>

Now go to http://your-server/index.php/blog/add and you should be able to add a new post (you’ll see a success message if it worked). Just add the one post for now.

Add another function to your blog.php controller:

function view()
 {
 $this->load->helper('date');
 $this->load->helper('typography');

 // Load the document
 $document = $this->posts->findOne();

 $data = array(
 'title' => $document['title'],
 'body' => $document['body'],
 'author' => $document['author'],
 'date' => $document['date']
 );

 $this->load->view('view', $data);
}

What is happening here is we are asking Mongo to return just one document (which is returned as an array). We then pass this onto a new view.php file

<!DOCTYPE html>
<html>
 <head>
 <title>MongoDB 101 (Part 1)</title>
 </head>
 <body>

 <h1><?php echo $title; ?></h1>

 <?php echo auto_typography( $body ); ?>

 <p><em>Posted by <?php echo $author; ?> on <?php echo unix_to_human( $date ); ?></em></p>

 <p><a href="<?php echo siteurl(array('blog', 'add')); ?>">Add a post</a></p>
 <p><a href="<?php echo siteurl(array('blog', 'index')); ?>">View all posts</a></p>
 </body>
</html>

Now go to http://your-server/index.php/blog/view and you’ll see the post you just added.

Let’s adapt the view function so it can return a specific blog post.

function view()
 {
 $this->load->helper('date');
 $this->load->helper('typography');

 // Load the document
 $document = $this->posts->findOne(array('id' => (int)$this->uri->segment(3)));

 $data = array(
 'id' => $document['id'],
 'title' => $document['title'],
 'body' => $document['body'],
 'author' => $document['author'],
 'date' => $document['date']
 );

 $this->load->view('view', $data);
}

Here we added a query array into the findOne function. Note we typecasted the uri segment as an integer, this is because internally Mongo stores documents as BSON (binary JSON) and as our id field was is an integer we need make sure we’re being strict and querying it as an integer.

If you now go to http://your-server/index.php/blog/view/1 you should see your blog post.

To round this brief introduction to MongoDB off we’re going to edit the original index controller function to list the most recent five blog posts in date descending order.

function index()
 {
 $this->load->helper('date');
 $this->load->helper('typography');

 // Find 5 documents
 $documents = $this->posts->find()->limit(5);

 // Sort them by time descending
 $documents = $documents->sort(array('date' => -1));

 $data = array(
 'documents' => array()
 );

 // While we have results
 while($documents->hasNext())
 {
 // Get the next result
 $document = $documents->getNext();

 $data['documents'][] = array(
 'id' => $document['id'],
 'title' => $document['title'],
 'body' => $document['body'],
 'author' => $document['author'],
 'date' => $document['date']
 );
 }

 $this->load->view('index', $data);
}

Some thing to note here, up until the point you start iterating over results (the while loop) you can play with the query to your hearts content. Also when you sort things, to sort ascending replace ASC with 1, and DESC with -1.

This is the index.php view file:

<!DOCTYPE html>
<html>
 <head>
 <title>MongoDB 101 (Part 1)</title>
 </head>
 <body>

 <?php
 if(count($documents) > 0):
 foreach($documents as $document => $vars):
 ?>

 <h1><a href="<?php echo site_url(array('blog','view', $vars['id'])); ?>"><?php echo $vars['title']; ?></a></h1>

 <?php echo auto_typography( $vars['body'] ); ?>

 <p><em>Posted by <?php echo $vars['author']; ?> on <?php echo unix_to_human( $vars['date'] ); ?></em></p>

 <hr />

 <?php
 endforeach;
 endif;
 ?>

 <p><a href="<?php echo site_url(array('blog', 'add')); ?>">Add a post</a></p>
 <p><a href="<?php echo site_url(array('blog', 'index')); ?>">View all posts</a></p>
 </body>
</html>

Awesome

So to wrap-up we’ve installed MongoDB and the PHP extension for it. Then we’ve created a basic blog using the CodeIgniter work. Time for a beer!

For more information on the PHP Mongo functions see http://uk.php.net/manual/en/book.mongo.php

For more information on MongoDB see http://www.mongodb.org/

To download my code go to http://alexbilbie.com/mongo101.zip

Busy bees

The last fortnight has been incredibly busy:

Nick and I finished off the client side design for “Posters at Lincoln” which was well received by MARCOMMS and will be soft launching in the next few weeks.

Preview of Posters at Lincoln site
Preview of Posters at Lincoln site

Posters at Lincoln will be the first Lincoln site to use the new “common web design” that I designed. The CWD is going to be rolled out to other sites over the coming months including Enterprise@Lincoln, Blogs, Network Access (LUNA), Wireless Access, Pay for Print, Print From My PC and the Library Portal. We’ve set up a blog at thecwd.dev.lincoln.ac.uk to show off some of the new sites we’re making.

A preview of the "common web design"
A preview of the "common web design"

I’ve started laying down some of the ground work for the new APIs that we’re developing for use in new applications. So far we’ve got the starts of an authentication API (which can be interacted with via OAuth, SAML or Shibboleth), a basic library API which interacts with Horizon, and Nick has been working on an API that integrates with the Virtual Project Office system.

In my spare time I’ve developed an OAuth server for the CodeIgniter framework which I’m currently integrating with Phil Sturgeon‘s CodeIngiter REST server which we’re using as the basis for the APIs mentioned above.

Apparently our open API is giving our customers unprecenteded control over their own lives allowing them to seize control of their destinies. So please shut it down.
Apparently our open API is giving our customers unprecenteded control over their own lives allowing them to seize control of their destinies. So please shut it down.

Some how I’ve also managed to attend a couple of lectures and submit an Artificial Intelligence assignment in there as well!

All in all I’m thoroughly enjoying my new job in the Online Services Team, however I will admit to struggling to not work outside of work hours – some of the stuff we’re working on / have planned is just so much fun!