Create an address book with PHP

This article first appeared in issue 228 of .net magazine – the world's best-selling magazine for web designers and developers.

MongoDB is a document database designed for performance and scale while remaining easy for developers to use. The term document database is often misunderstood – it’s not referring to PDF and DOC files, but a document database in this case means storing arrays. MongoDB is often grouped with other non-relational databases, called NoSQ.

MongoDB provides two features that can be considered groundbreaking. First, it works with native language types, so the database works for you rather than you having to conform to it. It feels so natural to simply save arrays or objects that you’ll quickly forget you are using a database. Second, the schema is totally flexible meaning that each record can contain different fields. When working on an address book application, it struck us that this flexible structure would be fabulous for dealing with all the various kinds of contacts!

I have address book entries for people who have three email addresses, but no postal address. Then there’s Auntie Flo, who has two postal addresses because she migrates with the sun – but she doesn’t have a mobile phone. This article goes through how to design the schema to use with MongoDB, and how we built a PHP application on top of it.

People and phone numbers

We can create people in our address book and associate different kinds of data with different people, then store this in MongoDB using PHP. As a simple first example let’s see an array of data created in PHP and how to save to MongoDB:

$mum = array("name" => array("first" => "mum"),"phone" => array(array("number" => "01234567890", "label" => "work"),array("number" => "07890123456", "label" => "mobile")));$mongo = new Mongo();$db = $mongo->addresses;$db->people->save($mum);

First we create the array of data, which should be fairly familiar even if PHP isn’t your native language. Then we create a connection to the MongoDB database by creating a new Mongo object.

The PHP libraries for handling MongoDB are robust and approachable, and have really excellent documentation; by default the new Mongo object will simply connect to the MongoDB server on localhost but we can pass additional parameters if we needed to configure this for another target database.

Databases and collections

Accessing the addresses property tells PHP to use that database on our MongoDB server, and we then save our data to the people collection. Collections in MongoDB are like tables in a relational database, a place where we keep similar records together.

Neither the addresses database nor the people collection needs to be created in advance; MongoDB is designed to handle ever-changing and evolving schemas so there is no need to set anything in stone. Do take care though, because if you mistype (or my favourite, mis-plural) a table name, there will be no error shown!

We can log into MongoDB directly and take a look at what we have in the database at this point as seen in the image below.

Here we can see the phone numbers displayed for the initial entry in our address book

Does the way the phone numbers are stored seem strange to you? We could of course have done this instead:

$mum = array("name" => array("first" => "mum"),"phone" => array("work" => "01234567890","mobile" => "07890123456"));

These items of data are just a series of phone numbers, they’re equivalent to one another so we just store them all as "number" as shown in the first example, and attach labels to them. It’s tempting to use this latter example because it does appear a bit simpler; however, in this case, we need to bear in mind how we will access the data.

If we store phone numbers as distinct fields, we will be looking for a phone number for a person, without knowing whether it’s a mobile or a work phone. The structure we have chosen makes it easy to look up a person by phone number, and provides the ability to add more detail if the need ever arises. So we have chosen to store a list of phone numbers for each person, with some information attached to say what kind of a number it is.

Here we can see the data for our imaginary address book entry, ranging from straightforward phone numbers to detailed geolocation information

Storing addresses

Address books were originally intended for addresses, so let’s take a look at how we might go about formatting these for a couple of different locations. For example, the entry for Bob Smith in our MongoDB looks like the one in the image above.

Bob has houses on both sides of the pond (one in the US, one in Europe), and you can see that we’ve just used the fields appropriate to how the addresses should be formatted in each country since MongoDB enables us to do so. This is one of the best things about MongoDB; we can just add whatever fields are relevant to each record and nest the data as deeply as makes sense in each case. If you saw my real address book with people’s phone numbers, addresses, and birthdays (not to mention their children’s birth dates), all scribbled across space intended for fax numbers, you would know why this solution appeals so much!

Data entry

Up to now, we’ve been hard-coding arrays of data, so let’s do something a bit nicer to create a form we can use to add people and various entries for them. We’ve been using PHP to process forms since forever but it’s a fairly important part of the system, so let’s take a look at the image shown on the left (‘Mum’s the word’). This is driven by a simple form processing system; the form itself is a basic template and it submits to a script that simply saves a new record:

if($_SERVER['REQUEST_METHOD'] == "POST") {$person = array();// sanitiseif(isset($_POST['first_name'])) {$person['name']['first'] = filter_input(INPUT_POST, "first_name",FILTER_SANITIZE_STRING);}if(isset($_POST['last_name'])) {$person['name']['last'] = filter_input(INPUT_POST, "last_name", FILTER_SANITIZE_STRING);}$mongo = new Mongo();$db = $mongo->addresses;$person['created'] = new MongoDate();$db->people->save($person);header("Location: list.php");} else {// show templateinclude("input.html");}

Find your friends

Putting data into the system is all very well, but we’ll still need to be able to find people by any one of a number of criteria. In order to look at different ways we might use to find people and their associated data, we’ll use MongoDB directly from the command line to query the data and see the results that are returned.

The MongoDB website is well-designed resource offering useful documentation and a good range of examples

Let’s begin with the simplest case and simply look for people whose last name is Smith. The find() method in MongoDB takes an array of criteria whether we are using it from the command line or from PHP itself. Here, we’re looking for the last field inside the name field, and since this is JavaScript we can just use a dot to use the nested property value as our criteria.

db.people.find( {'name.last' : 'smith' } );

This retrieves, in their entirety, the records we have for everyone with a name.last field set to smith in the database. In exactly the same way, we can pull fields from within addresses that match a particular value and are nested inside the record.

As well as matching specific values, MongoDB can accept queries that use a regular expression to match fragments of a value; for example try this:

db.people.find( { "address.street" : /61st/ } );

This will return all addresses with 61st somewhere in the address. street element. It’s a standard (preg) regular expression implementation so everything you already know about regular expressions will apply here too – making for some very powerful search capabilities.

We can search on multiple criteria by supplying multiple criteria into the array when we search, so by doing something like this:

db.people.find({"name.last" : "smith", "name.first": "bob"});

When we have multiple nested records, however, we need something a bit more specific to say that we’re looking for an address in a particular state and city. If we simply added criteria for address.state and, we would get people matching who had multiple addresses, where one matched on state and one matched on city. To get around this, we use the $elemMatch operator to say that within one address, multiple elements must match – for example we could look for people with NY state addresses that are actually in New York City by doing:

db.people.find( { address : { $elemMatch : { state : 'NY', city : 'new york' }}})

There are many other ways to pick out exactly the data that you need from MongoDB, and luckily its documentation is done really well with clear examples – you can find it all over at

Our address book’s form processing system enables us to create contacts and categorise them by a number of different criteria

Geographical locations

If you were looking closely, you may have spotted co-ordinates fields in the screenshot of Bob Smith’s data on page 99. These represent latitude and longitude, and MongoDB has some neat features for dealing with it. Within each collection, we can add one index to enforce sensible values for geographical data. For this application, I used this command:

db.people.ensureIndex( { 'address.coordinates' : '2d' });

The 2d tells MongoDB that this is geographical latitude/longitude data and it should therefore be two numbers, each in the range of +/-180. Looking at the data we posted above, we can see that the UK address gives us a spot in lovely Cumbria (see image below).

MongoDB has some great features for dealing with your contacts’ geolocation data such as latitude and longitude details

MongoDB knows that this is geographic data, so we can begin to make use of geo-specific features when we work with it. For example we’re able to use the $near operator in order to fetch entries near to a given latitude or longitude point:

db.people.find( { 'address.coordinates' : { $near : [52,-3] } } );

This returns records for all the addresses in the database, ordered by their distance from the point we gave.

We can improve on this search by looking for addresses within a given distance (distance is a bit of a woolly concept here because we’re working in latitude and longitude – the earth is not flat, and really the maths would make an excellent article of its own) to find entries which are nearby. We can use the $maxDistance operator, like this:

db.people.find( { 'address.coordinates' : { $near : [52,-3], $maxDistance: 50 } } );

Once again, this finds us Bob’s record; we could equally have used the limit() function to return a set number of records if we were using a bigger dataset, which would be useful if we were looking for the nearest five friends to visit, for example.

The Mongo extension for PHP really just enables us to access all of MongoDB’s functionality in a sane way, so to use the same techniques in our application ends up looking quite similar. The query above looks like this when we add it into our application:

$mongo = new Mongo();$db = $mongo->addresses;$criteria = array('address.coordinates' => array('$near' => array(53, -3),'$maxDistance' => 50));$list = $db->people->find($criteria);print_r(iterator_to_array($list));

We will have a MongoCursor to a collection in $list, which we can iterate over to get the results returned by our queries. For small data sets, you can also use the iterator_to_array() function to just give an array structure that you can use as you wish.

MongoDB and nested data

Hopefully this article has provided you with an understanding of how flexible data can be extremely handy to work with using MongoDB. We avoid separating out records only to recombine them each time we retrieve the data, yet we can still search deeply within those records in order to find things by various criteria.

The support for geospatial searching in MongoDB goes far beyond what is shown here, but all being well it has given you a taste of the kinds of things you can achieve with these tools.