Jonathan H. Wage http://www.jwage.com Doctrine, Symfony, PHP Wed, 25 Aug 2010 13:25:57 +0000 en hourly 1 http://wordpress.org/?v=3.0 Blending the Doctrine ORM and MongoDB ODM http://www.jwage.com/2010/08/25/blending-the-doctrine-orm-and-mongodb-odm/ http://www.jwage.com/2010/08/25/blending-the-doctrine-orm-and-mongodb-odm/#comments Wed, 25 Aug 2010 04:54:06 +0000 jwage http://www.jwage.com/?p=218

Since the start of the [Doctrine MongoDB Object Document Mapper](http://www.doctrine-project.org/projects/mongodb_odm) project people have asked how it can be integrated with the [ORM](http://www.doctrine-project.org/projects/orm). This blog post demonstrates how you can integrate the two transparently, maintaining a clean domain model.

This example will have a `Product` that is stored in MongoDB and the `Order` stored in a MySQL database.

## Defining our Document and Entity

First lets define our `Product` document:

[php]
namespace Documents;

/** @Document */
class Product
{
/** @Id */
private $id;

/** @String */
private $title;

public function getId()
{
return $this->id;
}

public function getTitle()
{
return $this->title;
}

public function setTitle($title)
{
$this->title = $title;
}
}

Next create the `Order` entity that has a `$product` and `$productId` property linking it to the `Product` that is stored with MongoDB:

[php]
namespace Entities;

use Documents\Product;

/**
* @Entity
* @Table(name=”orders”)
* @HasLifecycleCallbacks
*/
class Order
{
/**
* @Id @Column(type=”integer”)
* @GeneratedValue(strategy=”AUTO”)
*/
private $id;

/**
* @Column(type=”string”)
*/
private $productId;

/**
* @var Documents\Product
*/
private $product;

public function getId()
{
return $this->id;
}

public function getProductId()
{
return $this->productId;
}

public function setProduct(Product $product)
{
$this->productId = $product->getId();
$this->product = $product;
}

public function getProduct()
{
return $this->product;
}
}

## Event Subscriber

Now we need to setup an event subscriber that will set the `$product` property of all `Order` instances to a reference to the document product so it can be lazily loaded when it is accessed the first time. So first register a new event subscriber:

[php]
$eventManager = $em->getEventManager();
$eventManager->addEventListener(
array(\Doctrine\ORM\Events::postLoad), new MyEventSubscriber($dm)
);

So now we need to define a class named `MyEventSubscriber` and pass a dependency to the `DocumentManager`. It will have a `postLoad()` method that sets the product document reference:

[php]
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\Event\LifecycleEventArgs;

class MyEventSubscriber
{
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}

public function postLoad(LifecycleEventArgs $eventArgs)
{
$order = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
$productReflProp = $em->getClassMetadata(‘Entities\Order’)
->reflClass->getProperty(‘product’);
$productReflProp->setAccessible(true);
$productReflProp->setValue(
$order, $this->dm->getReference(‘Documents\Product’, $order->getProductId())
);
}
}

The `postLoad` method will be invoked after an ORM entity is loaded from the database. This allows us to use the `DocumentManager` to set the `$product` property with a reference to the `Product` document with the product id we previously stored.

First create a new `Product`:

[php]
$product = new \Documents\Product();
$product->setTitle(‘Test Product’);
$dm->persist($product);
$dm->flush();

Now create a new `Order` and link it to a `Product` in MySQL:

[php]
$order = new \Entities\Order();
$order->setProduct($product);
$em->persist($order);
$em->flush();

Later we can retrieve the entity and lazily load the reference to the document in MongoDB:

[php]
$order = $em->find(‘Order’, $order->getId());

// Instance of an uninitialized product proxy
$product = $order->getProduct();

// Initializes proxy and queries the database
echo “Order Title: ” . $product->getTitle();

If you were to print the `$order` you would see that we got back regular PHP objects:

[php]
print_r($order);

The above would output the following:

Order Object
(
[id:Entities\Order:private] => 53
[productId:Entities\Order:private] => 4c74a1868ead0ed7a9000000
[product:Entities\Order:private] => Proxies\DocumentsProductProxy Object
(
[__isInitialized__] => 1
[id:Documents\Product:private] => 4c74a1868ead0ed7a9000000
[title:Documents\Product:private] => Test Product
)

)

That is it! It is not a very abstract example right now but it demonstrates how to utilize the events to do some very interesting things with the Doctrine persistence libraries! I hope that now someone will inspired to create an extension that offers an abstract solution for blending the ORM and ODM together!

]]>
http://www.jwage.com/2010/08/25/blending-the-doctrine-orm-and-mongodb-odm/feed/ 7
New Doctrine MongoDB ODM Documentation http://www.jwage.com/2010/08/19/new-doctrine-mongodb-odm-documentation/ http://www.jwage.com/2010/08/19/new-doctrine-mongodb-odm-documentation/#comments Thu, 19 Aug 2010 18:03:17 +0000 jwage http://www.jwage.com/?p=212

Yesterday I added a few new chapters to the [Doctrine MongoDB ODM Documentation](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/en):

* [Capped Collections](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/reference/capped-collections/en)
* [Indexes](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/reference/indexes/en)
* [Geospatial Queries](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/reference/geospatial-queries/en)
* [Storing Trees/Hierarchical Data](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/reference/trees/en)

I hope it is helpful to someone! :)

]]>
http://www.jwage.com/2010/08/19/new-doctrine-mongodb-odm-documentation/feed/ 1
Doctrine Annotations Library http://www.jwage.com/2010/08/02/doctrine-annotations-library/ http://www.jwage.com/2010/08/02/doctrine-annotations-library/#comments Mon, 02 Aug 2010 21:48:51 +0000 jwage http://www.jwage.com/?p=174

The Doctrine Annotations library was born from a need in the Doctrine2 ORM to allow the mapping information to be specified inside the doc-blocks of classes, properties and methods. The library is independent and can be used in your own libraries to implement doc block annotations.

## Setup and Configuration

To use the annotations library is simple, you just need to create a new `AnnotationReader`
instance:

[php]
$reader = new \Doctrine\Common\Annotations\AnnotationReader();

## Usage

Using the library API is simple. Imagine you had some annotation classes that looked like the following:

[php]
namespace MyCompany\Annotations;

class Foo extends \Doctrine\Common\Annotations\Annotation
{
public $bar;
}

class Bar extends \Doctrine\Common\Annotations\Annotation
{
public $foo;
}

Now to use the annotations you would just need to do the following:

[php]
/**
* @MyCompany\Annotations\Foo(bar=”test”)
* @MyCompany\Annotations\Bar(foo=”test”)
*/
class User
{
}

Now we can write a script to get the annotations above:

[php]
$reflClass = new ReflectionClass(‘User’);
$classAnnotations = $reader->getClassAnnotations($reflClass);
echo $classAnnotations['MyCompany\Annotations\Foo']->bar; // prints foo
echo $classAnnotations['MyCompany\Annotations\Foo']->foo; // prints bar

You have a complete API for retrieving annotation class instances from a class, property
or method docblock:

* getClassAnnotations(ReflectionClass $class)
* getClassAnnotation(ReflectionClass $class, $annotation)
* getPropertyAnnotations(ReflectionProperty $property)
* getPropertyAnnotation(ReflectionProperty $property, $annotation)
* getMethodAnnotations(ReflectionMethod $method)
* getMethodAnnotation(ReflectionMethod $method, $annotation)

Read the full [documentation](http://www.doctrine-project.org/projects/common/2.0/docs/reference/annotations/en) to learn more about how to use the Doctrine annotations library!

]]>
http://www.jwage.com/2010/08/02/doctrine-annotations-library/feed/ 2
Array dereferencing in PHP trunk http://www.jwage.com/2010/07/31/array-dereferencing-in-php-trunk/ http://www.jwage.com/2010/07/31/array-dereferencing-in-php-trunk/#comments Sat, 31 Jul 2010 22:29:48 +0000 jwage http://www.jwage.com/?p=162

Today I was reading an [article](http://schlueters.de/blog/archives/138-Features-in-PHP-trunk-Array-dereferencing.html) about array dereferencing in PHP trunk. It is an awesome new feature added to PHP! Imagine you had some code like this:

[php]
class Foo
{
public function bar()
{
}
}

function func()
{
return new Foo();
}

Previous to this addition in PHP you had to do something like this:

[php]
$foo = func();
$foo->bar();

Now it is possible to just call bar() directly without having to set the return of func() to a variable temporarily:

[php]
func()->bar();

You can also now access arrays when they are the return of a method:

[php]
function foo()
{
return array(1, 2, 3);
}
echo foo()[2]; // prints 3

This greatly improves the syntax of PHP and I am very happy to see this committed!

]]>
http://www.jwage.com/2010/07/31/array-dereferencing-in-php-trunk/feed/ 4
Doctrine MongoDB ODM Schema Migrations http://www.jwage.com/2010/07/30/doctrine-mongodb-odm-schema-migrations/ http://www.jwage.com/2010/07/30/doctrine-mongodb-odm-schema-migrations/#comments Fri, 30 Jul 2010 20:38:03 +0000 jwage http://www.jwage.com/?p=147

[MongoDB](http://www.mongodb.org) is a schema-less database so as your domain model changes in Doctrine, you’ll have newer documents with different fields than older documents. Since we don’t have a way to rename a field internally in MongoDB, [yet](http://jira.mongodb.org/browse/SERVER-394), the only other option is to fetch all the documents and rename it in your application and update the document. This could take a really long time depending on how big your database is and will require downtime.

Doctrine provides ways for you to “eventually” migrate all your documents at application run-time. You have several options for working with database schema changes and this blog post will try and demonstrate them!

## Renaming a Field

Imagine you have a document in your domain named Person and it looked like this:

[php]
/** @Document */
class Person
{
/** @Id */
public $id;

/** @String */
public $name;
}

Then imagine later you decide you want to rename $name to $fullName like this:

[php]
/** @Document */
class Person
{
/** @Id */
public $id;

/** @String */
public $fullName;
}

All documents from now on will be created with a fullName property but you’ll still have old documents with the name field. You can use the @AlsoLoad annotation here to also load another fields value in that property if it exists:

[php]
/** @Document */
class Person
{
/** @Id */
public $id;

/**
* @String
* @AlsoLoad(“name”)
*/
public $fullName;
}

## Transforming Data

Another situation might be you want to load the name and fullName fields in to individual first and last name fields. We can handle this using the @AlsoLoad annotation on a method:

[php]
/** @Document */
class Person
{
/** @Id */
public $id;

/** @String */
public $firstName;

/** @String */
public $lastName;

/** @AlsoLoad({“name”, “fullName”}) */
public function populateFirstAndLastName($fullName)
{
$e = explode(‘ ‘, $fullName);
$this->firstName = $e[0];
$this->lastName = $e[1];
}
}

So when a document has a field named name, or fullName it will execute the populateFirstAndLastName() method to handle the change when the document is loaded.

## Moving Fields

You also have a few other options for dealing with changes in your model:

* @PostLoad – execute code after all fields have been loaded.
* @PrePersist – execute code before your document gets saved.
* @NotSaved – load values into fields without saving them again.

Imagine you have some address fields on a Person document:

[php]
/** @Document(collection=”people”) */
class Person
{
/** @Id */
public $id;

/** @String */
public $name;

/** @String */
public $street;

/** @String */
public $city;
}

Then later you want to store a persons address in another object as an embedded document:

[php]
/** @EmbeddedDocument */
class Address
{
/** @String */
public $street;

/** @String */
public $city;

public function __construct($street, $city)
{
$this->street = $street;
$this->city = $city;
}
}

/**
* @Document(collection=”people”)
* @HasLifecycleCallbacks
*/
class Person
{
/** @Id */
public $id;

/** @String */
public $name;

/** @NotSaved */
public $street;

/** @NotSaved */
public $city;

/** @EmbedOne(targetDocument=”Address”) */
public $address;

/** @PostLoad */
public function postLoad()
{
if ($this->street !== null || $this->city !== null)
{
$this->address = new Address($this->street, $this->city);
}
}
}

The above will change the document each time it is loaded. If you want to change it permanently in the database you can do it when the document is being updated:

[php]
/**
* @Document(collection=”people”)
* @HasLifecycleCallbacks
*/
class Person
{
// …

/** @PreUpdate */
public function preUpdate()
{
if ($this->street !== null || $this->city !== null)
{
$this->address = new Address($this->street, $this->city);
}
}
}

]]>
http://www.jwage.com/2010/07/30/doctrine-mongodb-odm-schema-migrations/feed/ 1
Inheritance and Mapped Super Classes in Doctrine http://www.jwage.com/2010/07/28/inheritance-and-mapped-super-classes-in-doctrine/ http://www.jwage.com/2010/07/28/inheritance-and-mapped-super-classes-in-doctrine/#comments Wed, 28 Jul 2010 15:57:13 +0000 jwage http://www.jwage.com/?p=108

[Single Collection Inheritance](http://www.doctrine-project.org/projects/mongodb_odm/1.0/docs/reference/inheritance-mapping/en#single-collection-inheritance) in the [Doctrine MongoDB ODM](http://www.doctrine-project.org/projects/mongodb_odm) allows you to map multiple classes in an inheritance hierarchy to a single collection in [MongoDB](http://www.mongodb.org). An example might be in a CMS where you have several different content types like the base Node, Page and BlogPost which all extends an abstract ContentType class.

First define the ContentType class that is a @MappedSuperclass:

[php]
/**
* @MappedSuperclass
* @HasLifecycleCallbacks
*/
abstract class ContentType
{
/** @Id */
protected $id;

/** @Date */
protected $createdAt;

/** @Date */
protected $updatedAt;

public function getId()
{
return $this->id;
}

/** @PreUpdate */
public function preUpdate()
{
$this->updatedAt = new DateTime();
}

/** @PrePersist */
public function prePersist()
{
$this->createdAt = new DateTime();
$this->updatedAt = new DateTime();
}
}

Now we can define our base Node content type:

[php]
/**
* @Document(collection=”pages”)
* @InheritanceType(“SINGLE_COLLECTION”)
* @DiscriminatorField(fieldName=”type”)
* @DiscriminatorMap({
* “node”=”Node”,
* “page”=”Page”,
* “blog_post”=”BlogPost”
* })
*/
class Node extends ContentType
{
/** @String */
protected $title;

public function getTitle()
{
return $this->title;
}

public function setTitle($title)
{
$this->title = $title;
}
}

This gives us our base content type node functionality that we can extend to create the Page document which adds a body field for the page:

[php]
/** @Document */
class Page extends Node
{
/** @String */
protected $body;

public function getBody()
{
return $this->body;
}

public function setBody($body)
{
$this->body = $body;
}
}

And the BlogPost document is a custom type of page that adds some additional fields related to blog posts like an excerpt and tags:

[php]
/** @Document */
class BlogPost extends Page
{
/** @Collection */
private $tags = array();

/** @String */
private $excerpt;

public function getExcerpt()
{
return $this->excerpt;
}

public function setExcerpt($excerpt)
{
$this->excerpt = $excerpt;
}

public function addTag($tag)
{
$this->tags[] = $tag;
}

public function removeTag($tag)
{
$key = array_search($tag, $this->tags);
if ($key !== false) {
unset($this->tags[$key]);
}
}

public function getTags()
{
return $this->tags;
}
}

You can easily add new content types by mapping a document class that extends the base Node. All your documents will be stored in a single collection and a discriminator field will be used to discriminate which class created each document.

Now we can use our document classes and create new instances and persist them. Here is an example where we create a new blog post:

[php]
$post = new BlogPost();
$post->setTitle(‘Test’);
$post->setExcerpt(‘Testing’);
$post->setBody(‘w00t’);
$post->addTag(‘test’);
$dm->persist($post);
$dm->flush();

The above would result in a document like the following in MongoDB:

Array
(
[_id] => 4c4f38978ead0ef23f000000
[createdAt] => MongoDate Object
(
[sec] => 1280260247
[usec] => 0
)

[updatedAt] => MongoDate Object
(
[sec] => 1280260247
[usec] => 0
)

[title] => Test
[body] => w00t
[tags] => Array
(
[0] => test
)

[excerpt] => Testing
[type] => blog_post
)

]]>
http://www.jwage.com/2010/07/28/inheritance-and-mapped-super-classes-in-doctrine/feed/ 1
Setting Entity/Document references without hitting the database http://www.jwage.com/2010/07/28/setting-entitydocument-references-without-hitting-the-database/ http://www.jwage.com/2010/07/28/setting-entitydocument-references-without-hitting-the-database/#comments Wed, 28 Jul 2010 14:33:38 +0000 jwage http://www.jwage.com/?p=98 setProfileId($profileId); $user->save(); The problem here is that the profile_id is set in the object but the reference to a Profile instance is not set. The next option is to actually set the object [...]]]>

In [Doctrine 1](http://www.doctrine-project.org/projects/orm/1.2/docs/en) when you want to specify a reference you have two options, you can set the foreign key manually:

[php]
$user->setProfileId($profileId);
$user->save();

The problem here is that the profile_id is set in the object but the reference to a Profile instance is not set. The next option is to actually set the object reference:

[php]
$profile = Doctrine_Core::getTable(‘Profile’)->find($profileId);
$user->setProfile($profile);
$user->save();

Here the reference is set properly but the downside to this approach is that it requires us to load the entire Profile object just to set the reference. It is silly! Thanks to the [Doctrine2 ORM](http://www.doctrine-project.org/projects/orm) and [MongoDB ODM](http://www.doctrine-project.org/projects/mongodb_odm) you have the ability to retrieve a reference to an object without having to hit the database. Here is an example:

[php]
$profile = $em->getReference(‘Profile’, $profileId);
$user->setProfile($profile);
$em->flush();

Or the same thing with the MongoDB ODM:

[php]
$profile = $dm->getReference(‘Profile’, $profileId);
$user->setProfile($profile);
$dm->flush();

]]>
http://www.jwage.com/2010/07/28/setting-entitydocument-references-without-hitting-the-database/feed/ 1
Storing Files with MongoDB GridFS http://www.jwage.com/2010/07/27/storing-files-with-mongodb-gridfs/ http://www.jwage.com/2010/07/27/storing-files-with-mongodb-gridfs/#comments Tue, 27 Jul 2010 23:31:29 +0000 jwage http://www.jwage.com/?p=124

The [PHP MongoDB](http://www.php.net/mongo) extension provides a nice and convenient way to store files in chunks of data with the [MongoDB GridFS](http://us.php.net/manual/en/class.mongogridfs.php). It uses two database collections, one to store the metadata for the file, and another to store the contents of the file. The contents are stored in chunks to avoid going over the maximum allowed size of a MongoDB document.

You can easily setup a Document that is stored using the MongoDB GridFS by using the @File annotation:

[php]
namespace Documents;

/** @Document(collection=”files”) */
class Image
{
/** @Id */
private $id;

/** @String */
private $name;

/** @File */
private $file;

private function getId()
{
return $id;
}

private function setName($name)
{
$this->name = $name;
}

private function getName()
{
return $this->name;
}

private function getFile()
{
return $this->file;
}

private function setFile($file)
{
$this->file = $file;
}
}

Notice the $file property with @File annotation, it tells the Document that it is is to be stored using the MongoGridFS and the [MongoGridFSFile](http://www.php.net/MongoGridFSFile) instance is placed in the $file property for you.

Now you can create a new Image setting the path to a file and persist it:

[php]
$image = new Image();
$image->setName(‘Test image’);
$image->setFile(‘/path/to/image.png’);

$dm->persist($image);
$dm->flush();

Then later you can retrieve that image and render it:

[php]
$image = $dm->createQuery(‘Documents\Image’)
->field(‘name’)
->equals(‘Test image’)
->getSingleResult();

header(‘Content-type: image/png;’);
echo $image->getFile()->getBytes();

You can of course make references to this Image document from another document.
Imagine you had a Profile document and you wanted every Profile to have a profile
image:

[php]
namespace Documents;

/** @Document(collection=”profiles”) */
class Profile
{
/** @Id */
private $id;

/** @String */
private $name;

/** @ReferenceOne(targetDocument=”Documents\Image”) */
private $image;

private function getId()
{
return $this->id;
}

private function getName()
{
return $this->name;
}

private function setName($name)
{
$this->name = $name;
}

private function getImage()
{
return $this->image;
}

private function setImage(Image $image)
{
$this->image = $image;
}
}

Now you can create a new Profile and give it an Image:

[php]
$image = new Image();
$image->setName(‘Test image’);
$image->setFile(‘/path/to/image.png’);

$profile = new Profile();
$profile->setName(‘Jonathan H. Wage’);
$profile->setImage($image);

$dm->persist($profile);
$dm->flush();

If you want to query for the Profile and load the Image reference in a query
you can use:

[php]
$profile = $dm->createQuery(‘Profile’)
->field(‘name’)->equals(‘Jonathan H. Wage’)
->getSingleResult();

$image = $profile->getImage();

header(‘Content-type: image/png;’);
echo $image->getFile()->getBytes();

]]>
http://www.jwage.com/2010/07/27/storing-files-with-mongodb-gridfs/feed/ 1
Multiple levels of Embedded Documents in MongoDB http://www.jwage.com/2010/07/27/multiple-levels-of-embedded-documents-in-mongodb/ http://www.jwage.com/2010/07/27/multiple-levels-of-embedded-documents-in-mongodb/#comments Tue, 27 Jul 2010 17:04:32 +0000 jwage http://www.jwage.com/?p=89

One of the greatest things about [MongoDB](http://www.mongodb.org) is the fact that it is schema-less. It makes for a very flexible domain model persistence layer. For example it is possible to have multiple levels of embedded documents. A useful example might be where you have many profiles and each profile has many addresses. In the [Doctrine](http://www.doctrine-project.org) MongoDB [ODM](http://www.doctrine-project.org/projects/odm) mapping this is trivial.

First create your top level `User` document:

[php]
/** @Document(collection=”users”) */
class User
{
/** @Id */
private $id;

/** @String */
private $username;

/** @EmbedMany(targetDocument=”Profile”) */
private $profiles = array();

public function setUsername($username)
{
$this->username = $username;
}

public function addProfile(Profile $profile)
{
$this->profiles[] = $profile;
}
}

As you can see we embed another document class named Profile so lets define that as an embedded document:

[php]
/** @EmbeddedDocument */
class Profile
{
/** @String */
private $name;

/** @EmbedMany(targetDocument=”Address”) */
private $addresses = array();

public function setName($name)
{
$this->name = $name;
}

public function addAddress(Address $address)
{
$this->addresses[] = $address;
}
}

Finally, we’ve embedded a document in Profile named Address so lets define it:

[php]
/** @EmbeddedDocument */
class Address
{
/** @String */
private $number;

/** @String */
private $street;

/** @String */
private $city;

/** @String */
private $state;

/** @String */
private $zipcode;

public function setNumber($number)
{
$this->number = $number;
}

public function setStreet($street)
{
$this->street = $street;
}

public function setCity($city)
{
$this->city = $city;
}

public function setState($state)
{
$this->state = $state;
}

public function setZipcode($zipcode)
{
$this->zipcode = $zipcode;
}
}

Now you can start working with the PHP objects just like you would if no persistence layer was present at all and persist the objects transparently when you are ready to have the state of the objects managed by Doctrine:

[php]
$user = new User();
$user->setUsername(‘jwage’);

$profile = new Profile();
$profile->setName(‘Profile #1′);

$user->addProfile($profile);

$address = new Address();
$address->setNumber(’6512′);
$address->setStreet(‘Mercomatic’);
$address->setCity(‘Nashville’);
$address->setState(‘Tennessee’);
$address->setZipcode(’37209′);

$profile->addAddress($address);

$profile = new Profile();
$profile->setName(‘Profile #2′);

$user->addProfile($profile);

$address = new Address();
$address->setNumber(’475′);
$address->setStreet(‘Buckhead Ave’);
$address->setCity(‘Atlanta’);
$address->setState(‘Georgia’);
$address->setZipcode(’30303′);

$profile->addAddress($address);

$dm->persist($user);
$dm->flush();

The above would result in an array being stored in MongoDB like the following:

Array
(
[_id] => MongoId Object
(
)
[username] => jwage
[profiles] => Array
(
[0] => Array
(
[name] => Profile #1
[addresses] => Array
(
[0] => Array
(
[number] => 6512
[street] => Mercomatic
[city] => Nashville
[state] => Tennessee
[zipcode] => 37209
)
)
)
[1] => Array
(
[name] => Profile #2
[addresses] => Array
(
[0] => Array
(
[number] => 475
[street] => Buckhead Ave
[city] => Atlanta
[state] => Georgia
[zipcode] => 30303
)
)
)
)
)

We can then later retrieve the documents from MongoDB and our object domain model will be reconstructed as you have mapped it:

[php]
$user = $dm->findOne(‘User’, array(‘username’ => ‘jwage’));

You can see the complete working script for this blog post as a gist on [github](http://gist.github.com/492509).

]]>
http://www.jwage.com/2010/07/27/multiple-levels-of-embedded-documents-in-mongodb/feed/ 6
Using YQL to get geo location information for an IP address http://www.jwage.com/2010/07/27/using-yql-to-get-geo-location-information-for-an-ip-address/ http://www.jwage.com/2010/07/27/using-yql-to-get-geo-location-information-for-an-ip-address/#comments Tue, 27 Jul 2010 04:36:57 +0000 jwage http://www.jwage.com/?p=71

The [Yahoo! YQL API](http://developer.yahoo.com/yql/) has the ability to provide geo location information for IP addresses. Using this in PHP is dead simple. Here is a barebones example demonstrating how you can use PHP and YQL for this:

[php]
$ipAddress = ’76.22.200.69′;
$query = sprintf(“select * from ip.location where ip=’%s’”, $ipAddress);
$queryUrl = “http://query.yahooapis.com/v1/public/yql?q=” . urlencode($query).”&format=json&env=”.urlencode(“store://datatables.org/alltableswithkeys”);
$json = file_get_contents($queryUrl);
$data = json_decode($json);
print_r($data);

The above code would output the following:

stdClass Object
(
[query] => stdClass Object
(
[count] => 1
[created] => 2010-07-27T04:27:20Z
[lang] => en-US
[results] => stdClass Object
(
[Response] => stdClass Object
(
[Ip] => 76.22.200.69
[Status] => OK
[CountryCode] => US
[CountryName] => United States
[RegionCode] => 47
[RegionName] => Tennessee
[City] => Nashville
[ZipPostalCode] => 37205
[Latitude] => 36.1121
[Longitude] => -86.863
[Timezone] => 0
[Gmtoffset] => 0
[Dstoffset] => 0
)

)

)

)

I took this a step further and built a little abstraction layer on top of this functionality. Now, retrieving geo location information for IP addresses using the YQL API is easier than ever using the PHP YQL Geo Locator library. You can get the code on [github](http://github.com/jwage/php-yql-geo-locator). Continue reading to learn how to get started!

First, clone the git repository:

$ git clone git://github.com/jwage/php-yql-geo-locator.git

Now you need to setup your code to use the library:

[php]
use GeoLocator\Locator;
use GeoLocator\Location;
use GeoLocator\GoogleMapImage;

require ‘php-yql-geo-locator/lib/GeoLocator/Location.php’;
require ‘php-yql-geo-locator/lib/GeoLocator/Locator.php’;
require ‘php-yql-geo-locator/lib/GeoLocator/GoogleMapImage.php’;

After setting everything up you are ready to start working with geo locations
using the locator API:

* getGeoLocation($ip)
* getGoogleMapImageForIps(array $ips)
* getGoogleMapImageForIp($ip)

Here is an example using the getGeoLocation() method:

[php]
$geoLocation = $locator->getGeoLocation(’76.22.200.69′);

It returns an instance of GeoLocator\Location and has a simple public API for retrieving the geo location information for the IP address:

[php]
echo $geoLocation->getLatitude();

You can also export all the information to a PHP array using the toArray() method:

[php]
print_r($geoLocation->toArray());

It would result in an array that looks like this:

Array
(
[ip] => 76.22.200.69
[countryCode] => US
[countryName] => United States
[regionCode] => 47
[regionName] => Tennessee
[city] => Nashville
[zipPostalCode] => 37205
[latitude] => 36.1121
[longitude] => -86.863
[timezone] => 0
[gmtOffset] => 0
[dstOffset] => 0
)

Get a google map that plots multiple IP addresses:

[php]
$image = $locator->getGoogleMapImageForIps(array(
’76.22.200.69′,
’74.125.65.106′
));

The above method returns an instance of GoogleMapImage and has the following API:

* setWidth($width)
* setHeight($height)
* setMaptype($maptype)
* setSensor($sensor)
* setZoom($zoom)
* addLocation(Location $location)
* getUrl()
* getHTMLImageTag()
* __toString()

Now you can just echo the $image to get the HTML image:

[php]
echo $image;

The above would result in an image tag that looks like the following:

]]>
http://www.jwage.com/2010/07/27/using-yql-to-get-geo-location-information-for-an-ip-address/feed/ 2