MongoObject Overview
Today I released MongoObject package. It is a library that simplifies storing and fetching of object to/from Mongo database.
It is available on github: https://github.com/dintel/mongo-object
And it is also available on packagist: https://packagist.org/packages/dintel/mongo-object
Installation
Using composer:
$ composer require dintel/mongo-object
Features
Most important features of MongoObject:
- Provides base Object class that you can derive and easily save into Mongo database
- Objects have schema, but underlying data in Mongo can diverge from it
- Provides Mapper class that allows loading objects from Mongo database
- Provides MapperFactory class that can be used as a service factory in Zend Framework 2
Object class
Object is a base class that should be used for all classes that should have an ability to be easily saved into Mongo database. Let’s look on following simple example to understand how classes should be derived from Object:
<?php
namespace MongoObjectExample;
use MongoObject\Object;
use MongoCollection;
class User extends Object
public function __construct(array $data, MongoCollection $collection)
{
$schema = [
'_id' => ['type' => Object::TYPE_ID, 'null' => false],
'login' => ['type' => Object::TYPE_STRING, 'null' => false],
'name' => ['type' => Object::TYPE_STRING, 'null' => false],
'email' => ['type' => Object::TYPE_STRING, 'null' => false],
'password' => ['type' => Object::TYPE_STRING, 'null' => false, 'hidden' => true],
'active' => ['type' => Object::TYPE_BOOL, 'null' => false],
'created' => ['type' => Object::TYPE_DATE, 'null' => false],
];
$defaults = [
'active' => true,
'created' => new MongoDate(),
];
parent::__construct($schema, $data + $defaults, $collection, __NAMESPACE__);
}
public function checkPassword($password)
{
return hash('sha256', $password) === $this->_data['password'];
}
}
In this example class User is derived from Object and defines schema with following fields:
_id
- MongoId - Internal ID that is assigned by Mongologin
- string that must not be nullname
- string that must not be nullemail
- string that must not be nullpassword
- string that is not accessible as propertyactive
- boolean that must not be nullcreated
- MongoDate that must not be null
In User constructor we pass object’s schema to Object constructor. This schema
is used by Object __get
and __set
methods to check whether properties are
defined and ensure that property value type matches what is defined in
schema. Schema is an associative list in which keys are names of fields and
values are arrays in which following keys has to be defined:
- type - one of
Object::TYPE_ID
,Object::TYPE_BOOL
,Object::TYPE_INT
,Object::TYPE_DOUBLE
,Object::TYPE_STRING
,Object::TYPE_ARRAY
,Object::TYPE_DATE
,Object::TYPE_REFERENCE
. - null - (optional) true if null value allowed, false otherwise (true by default).
- hidden - (optional) true if field must not be accessible using
__set
and__get
methods, false otherwise (false by default).
Object stores actual data in $this->data
array. If some values are missing there or there is excessive data in it, it is left intacct. This allows changing structure of object as simple as just changing schema and adding default value.
Object has following methods implemented:
__construct(array $schema, array $data, MongoCollection $collection, $modelsNamespace = null)
- constructor__isset($name)
- allows checking whether property is defined in schema__set($name, $value)
- sets value of property if it is defined in schema and is not hidden__get($name)
- returns value of property if is defined in schema and is not hiddensave()
- saves object into it’s collectiondelete()
- deletes object from it’s collection, unless it’s _id value is not setisNew()
- checks whether _id value is nullgetDBRef()
- return Mongo DBRef pointing to this object (or null of object does not have _id)refresh()
- reload object data from Mongo (only if _id value is not null)jsonSerialize()
- returns array of data with _id field renamed to id (used for serializing objects into JSON)mergeData()
- merge properties values from another arrayfetchDBRef($collectionName, $typeName, $dbref)
- protected helper function for fetching objects using their Mongo DBRef
Most of methods are very simple and do not need additional explanations, except constructor and fetchDBRef. Last parameter of constructor is a namespace in which models (in this case classes that derive from Object class) are defined. MongoObject assumes that all these classes are defined in single namespace and it should be supplied for fetchDBRef function to work correctly. This is because fetchDBRef function may fetch object of class other than one which calls it. In this case it will construct object of that type, but it has to know namespace in which it can be found. In case modelsNamespace is not defined, typeName that is passed to fetchDBRef must be class name including namespace.
Mapper overview
Mapper class simplifies fetching objects from Mongo database. It provides methods to fetch objects, count them (without actually fetching) and even creating new objects (from classes that are derived from Object). For Mapper to work, a few requirements must be met:
- Classes that are derived from Object must be defined in one single namespace.
- Classes that are derived from Object must have constructor with following signature -
__construct(array $data, MongoCollection $collection)
Mapper methods
Mapper has following method defined:
__construct(MongoDB $mongodb, $modelsNamespace = null)
- constructorfindObject($table, $type, $id)
- find object of type$type
in Mongo collection$table
with_id
$id.
findObjectByProp($table, $type, $name, $value)
- find object of type$type
in Mongo collection$table
which has field named$name
and it’s value is$value
.fetchObjects($table, $type, array $selector = [], array $order = null)
- find all objects of type$type
in Mongo collection$table
using Mongo select query$selector
and ordered by$order
.countObjects($table, $query = array())
- count objects that match Mongo select query$query
in collection$table
.newObject($table, $type, $data)
- construct new object of type$type
with property values$data
that will be stored in collection$table
.
When Mapper is constructed it gets 2 parameters. First parameter is MongoDB in which all objects will be stored. Second is name of PHP namespace in which all class that are derived from Object are defined. Second parameter is needed for convenience - when you later call methods of Mapper, everywhere you have to supply $type
parameter you can supply only class name without namespace in which it is declared.
Take into account that all classes that are used with Mapper must have constructor defined with following signature: __construct(array $data, MongoCollection $collection)
. Another way to ensure that your model classes (classes that derive from Object) are OK to work with Mapper is to implement MongoObject\MongoObject
interface.
Using MapperFactory in ZF2
MongoObject provides MapperFactory class that is able to construct and return object of Mapper when using Zend Framework 2 Service Manager. To use it you have to do 2 things:
- Add MapperFactory factory configuration to ZF2 Service Manager configuration.
- Add mongoObjectMapper configuration to your ZF2 configuration.
Add MapperFactory to ZF2 Service Manager
For this you have to add in your application configuration (or module
configuration) new factory. Here is an example of ZF2 configuration that defines
only one factory that is called mongo:
<?php
return array(
'service_manager' => array(
'factories' => array(
'mongo' => 'MongoObject\\MapperFactory',
),
),
);
Add mongoObjectMapper configuration to ZF2 configuration
Second step is to add mongoObjectMapper
configuration key to your ZF2 configuration. This key is used to fetch parameters for establishing connection to Mongo database. Here is an example that is used by phpunit tests of MongoObject:
<?php
return array(
'service_manager' => array(
'factories' => array(
'mongo' => 'MongoObject\\MapperFactory',
),
),
'mongoObjectMapper' => array(
'uri' => 'mongodb://localhost:27017',
'options' => [],
'database' => 'test',
'modelsNamespace' => 'MongoObjectTest',
),
);
As you can see mongoObjectMapper
has following keys:
uri
- should contain URI of Mongo database. If omitted then default value of'mongodb://localhost:27017'
is used.options
- additional options that are passed to MongoClient when establishing connection to Mongo.database
- database in Mongo against which Mapper would work.modelsNamespace
- passed to Mapper constructor, see previous section for details.
Once you have everything in place you can use $mapper = $this->getServiceLocator()->get('mongo');
in your ZF2 controller to get Mapper object ready for use.