purekid/mongodm

MongoDB ORM that includes support for references,embed and multilevel inheritance.

1.5.0 2017-03-02 03:20 UTC

README

SensioLabsInsight Build Status Latest Stable Version Total Downloads License

Introduction

Mongodm is a MongoDB ORM that includes support for references,embed and even multilevel inheritance.

Features

  • ORM
  • Simple and flexible
  • Support for embed
  • Support for references (lazy loaded)
  • Support for multilevel inheritance
  • Support for local collection operations

Requirements

  • PHP 5.3 or greater (Tested for 5.5,5.6,7.0,7.1)
  • Mongodb 1.3 or greater
  • PHP Mongo extension

Installation

1. Setup in composer.json:

	{
		"require": {
		    "purekid/mongodm": "dev-master"
		}
	}

2. Install by composer:

$ php composer.phar update

Setup Database

Database config file (By default it locates at /vendor/purekid/mongodm/config.php)

	return array(
        'default' => array(
    		'connection' => array(
    			'hostnames' => 'localhost',
    			'database'  => 'default',
    // 			'username'  => '',
    // 			'password'  => '',
    		)
    	),
    	'production' => array(
    		'connection' => array(
    			'hostnames' => 'localhost',
    			'database'  => 'production',
    			'options' => array('replicaSet' => 'rs0')
    		)
    	)
    );

Authentication

Authentication information is passed in via the options array. If you do not specifiy authSource, then the PHP Mongo Driver will choose the "admin" database.

$config =  array( 'connection' => array(
      'hostnames' => '<host>:<port>',
      'database'  => '<databasename>',
      'options'  => [ "connectTimeoutMS" => 500 , "username" => "admin", "password" => "<password>", "authSource" => "admin"] )
  );

Setup database in application

1.You can set up configuration using the MongoDB::setConfigBlock method.


\Purekid\Mongodm\MongoDB::setConfigBlock('default', array(
    'connection' => array(
        'hostnames' => 'localhost',
        'database'  => 'default',
        'options'  => array()
    )
));

// 
\Purekid\Mongodm\MongoDB::setConfigBlock('auth', array(
    'connection' => array(
        'hostnames' => 'localhost',
        'database'  => 'authDB',
        'options'  => array()
    )
));

2.Or you can duplicate a config file into your project, then define a global constanct 'MONGODM_CONFIG' with it's location.


//in a global initialization place

define('MONGODM_CONFIG',__DIR__."/../config/mongodm.php");

Choose config section with APPLICATION_ENV

Which config section Mongodm use ? Mongodm choose 'default' section by default.

You have two ways to specify section :

1.'$config' attribute in Model , you can find this attribute in example below.

2.With environment constanct 'APPLICATION_ENV' ,this constanct can be set by webserver,your code or shell environment. In this case,you should set $config='default' or don't declare $config in your own model class.

Create a model and enjoy it

    
    use Purekid\Mongodm\Model;
    
    class User extends Model 
    {
    
        static $collection = "user";
        
        /** use specific config section **/
        public static $config = 'testing';
        
        /** specific definition for attributes, not necessary! **/
        protected static $attrs = array(
                
             // 1 to 1 reference
            'book_fav' => array('model'=>'Purekid\Mongodm\Test\Model\Book','type'=> Model::DATA_TYPE_REFERENCE),
             // 1 to many references
            'books' => array('model'=>'Purekid\Mongodm\Test\Model\Book','type'=> Model::DATA_TYPE_REFERENCES),
            // you can define default value for attribute
            'age' => array('default'=>16,'type'=> Model::DATA_TYPE_INTEGER),
            'money' => array('default'=>20.0,'type'=> Model::DATA_TYPE_DOUBLE),
            'hobbies' => array('default'=>array('love'),'type'=> Model::DATA_TYPE_ARRAY),
            'born_time' => array('type'=> Model::DATA_TYPE_TIMESTAMP),
            'family'=>array('type'=> Model::DATA_TYPE_OBJECT),
            'pet_fav' => array('model'=>'Purekid\Mongodm\Test\Model\Pet','type'=> Model::DATA_TYPE_EMBED),
            'pets' => array('model'=>'Purekid\Mongodm\Test\Model\Pet','type'=> Model::DATA_TYPE_EMBEDS),
                
        );

        public function setFirstName($name) {
        	$name = ucfirst(strtolower($name));
        	$this->__setter('firstName', $name);
        }

        public function getLastName($name) {
        	$name = $this->__getter('name');
        	return strtoupper($name);
        }
    
    }

Types supported for model attributes


    [
	'mixed',  // mixed type 
	'string',     
	'reference',  // 1 : 1 reference
	'references', // 1 : many references
	'embed', 
	'embeds', 
	'integer',  
	'int',  // alias of 'integer'
	'double',     // float 
	'timestamp',  // store as MongoTimestamp in Mongodb
	'date',  // store as DateTime
	'boolean',    // true or false
	'array',    
	'object'
    ];

    const DATA_TYPE_ARRAY      = 'array';
    
    const DATA_TYPE_BOOL       = 'bool';
    const DATA_TYPE_BOOLEAN    = 'boolean';

    const DATA_TYPE_DATE       = 'date';

    const DATA_TYPE_DBL        = 'dbl';
    const DATA_TYPE_DOUBLE     = 'double';
    const DATA_TYPE_FLT        = 'flt';
    const DATA_TYPE_FLOAT      = 'float';

    const DATA_TYPE_EMBED      = 'embed';
    const DATA_TYPE_EMBEDS     = 'embeds';

    const DATA_TYPE_INT        = 'int';
    const DATA_TYPE_INTEGER    = 'integer';

    const DATA_TYPE_MIXED      = 'mixed';

    const DATA_TYPE_REFERENCE  = 'reference';
    const DATA_TYPE_REFERENCES = 'references';

    const DATA_TYPE_STR        = 'str';
    const DATA_TYPE_STRING     = 'string';

    const DATA_TYPE_TIMESTAMP  = 'timestamp';

    const DATA_TYPE_OBJ        = 'obj';
    const DATA_TYPE_OBJECT     = 'object';
    

If you put a object instance into a Model attribute and this attribute is undefined in $attrs of Model class,the data of attribute will be omitted when Model saving.

    
    $object = new \stdClass();  
    $object->name = 'ooobject';
    
    $user = new User();
    $user->name = 'michael';
    $user->myobject = $object;    // this attribute will be omitted when saving to DB 
    $user->save();

Model CRUD

Create

	$user = new User();
	$user->name = "Michael";
	$user->age = 18;
	$user->save();

Create with initial value

	$user = new User( array('name'=>"John") );
	$user->age = 20;
	$user->save();

Create using set method

	$user->setLastName('Jones'); // Alias of $user->lastName = 'Jones';
	$user->setFirstName('John'); // Implements setFirstName() method

Set and get values

You can set/get values via variable $user->name = "John" or by method $user->getName().

Set using variable or method

 	// no "set" method exists
	$user->lastName = 'Jones';
	$user->setLastName('Jones');

	// "set" method exists implements setFirstName()
	$user->firstName = 'jOhn'; // "John"
	$user->setFirstName('jOhn'); // "John"

Get using variable or method

 	// "get" method exists implements getLastName()
	print $user->lastName; // "JONES"
	print $user->getLastName(); // "JONES"

	// no "get" method
	print $user->firstName; // "John"
	print $user->setFirstName('John'); // "John"

Update

	$user->age = 19;

Update attributes by array

	$user->update( array('age'=>18,'hobbies'=>array('music','game') ) ); 
	$user->save();

Unset attributes

	$user->unset('age');
	$user->unset( array('age','hobbies') );
	//or
	unset($user->age);

Retrieve single record

	$user = User::one( array('name'=>"michael" ) );

retrieve one record by MongoId

	$id = "517c850641da6da0ab000004";
	$id = new \MongoId('517c850641da6da0ab000004'); //another way
	$user = User::id( $id );

Retrieve records

Retrieve records that name is 'Michael' and acount of owned books equals 2

	$params = array( 'name'=>'Michael','books'=>array('$size'=>2) );
	$users = User::find($params);     // $users is instance of Collection
	echo $users->count();

Retrieve all records

	$users = User::all();

Count records

	$count = User::count(array('age'=>16));

Delete record

	$user = User::one();
	$user->delete();	

Relationship - Reference

Lazyload a 1:1 relationship record

	$book = new Book();
	$book->name = "My Love";
	$book->price = 15;
	$book->save();

	// !!!remember you must save book before!!!
	$user->book_fav = $book;
	$user->save();

	// now you can do this
	$user = User::one( array('name'=>"michael" ) );
	echo $user->book_fav->name;

Lazyload 1:many relationship records

	$user = User::one();
	$id = $user->getId();

	$book1 = new Book();
	$book1->name = "book1";
	$book1->save();
	
	$book2 = new Book();
	$book2->name = "book2";
	$book2->save();

	$user->books = array($book1,$book2);
	//also you can
	$user->books = Collection::make(array($book1,$book2));
	$user->save();

	//somewhere , load these books
	$user = User::id($id);
	$books = $user->books;      // $books is a instance of Collection

Relationship - Embed

Single Embed

	$pet = new Pet();
	$pet->name = "putty";

	$user->pet_fav = $pet;
	$user->save();

	// now you can do this
	$user = User::one( array('name'=>"michael" ) );
	echo $user->pet_fav->name;

Embeds

	$user = User::one();
	$id = $user->getId();
	
	$pet_dog = new Pet();
	$pet_dog->name = "puppy";
	$pet_dog->save();
	
	$pet_cat = new Pet();
	$pet_cat->name = "kitty";
	$pet_cat->save();

	$user->pets = array($pet_cat,$pet_dog);
	//also you can
	$user->pets = Collection::make(array($pet_cat,$pet_dog));
	$user->save();

	$user = User::id($id);
	$pets = $user->pets;     

Collection

$users is instance of Collection

	$users = User::find(  array( 'name'=>'Michael','books'=>array('$size'=>2) ) );    
	$users_other = User::find(  array( 'name'=>'John','books'=>array('$size'=>2) ) );   

Save

    $users->save() ;  // foreach($users as $user) { $user->save(); }

Delete

    $users->delete() ;  // foreach($users as $user) { $user->delete(); }

Count

	$users->count();  
	$users->isEmpty();

Iteration

	foreach($users as $user) { }  
	
	// OR use Closure 
	
	$users->each(function($user){
	
	})

Sort

	//sort by age desc
	$users->sortBy(function($user){
	    return $user->age;
	});
	
	//sort by name asc
	$users->sortBy(function($user){
	    return $user->name;
	} , true);
	
	//reverse collection items
	$users->reverse();

Slice and Take

	$users->slice(0,1);
	$users->take(2);

Map

	$func = function($user){
		  		if( $user->age >= 18 ){
		    		$user->is_adult = true;
	        	}
	            return $user;
			};
	
	$users->map($func)->save();   
	

Filter

	$func = function($user){
	        	if( $user->age >= 18 ){
	    			return true;
	    		}
			}

	$adults = $users->filter($func); // $adults is a new collection

Determine a record exists in the collection by object instance

	$john = User::one(array("name"=>"John"));
	
	$users->has($john) 

Determine a record exists in the collection by numeric index

	$users->has(0) 

Determine a record exists in the collection by MongoID

	$users->has('518c6a242d12d3db0c000007') 

Get a record by numeric index

	$users->get(0) 

Get a record by MongoID

	$users->get('518c6a242d12d3db0c000007') 

Remove a record by numeric index

	$users->remove(0)  

Remove a record by MongoID

	$users->remove('518c6a242d12d3db0c000007') 

Add a single record to collection

	$bob = new User( array("name"=>"Bob"));
	$bob->save();
	$users->add($bob);

Add records to collection

	$bob = new User( array("name"=>"Bob"));
	$bob->save();
	$lisa = new User( array("name"=>"Lisa"));
	$lisa->save();
	
	$users->add( array($bob,$lisa) ); 

Merge two collection

	$users->add($users_other);  // the collection $users_other appends to end of $users 

Export data to a array

	$users->toArray();

Inheritance

Define multilevel inheritable models:

	use Purekid\Mongodm\Model;
	namespace Demo;
	
	class Human extends Model{
	
		static $collection = "human";
		
		protected static $attrs = array(
			'name' => array('default'=>'anonym','type'=>'string'),
			'age' => array('type'=>'integer'),
			'gender' => array('type'=>'string'),
			'dad' =>  array('type'=>'reference','model'=>'Demo\Human'),
			'mum' =>  array('type'=>'reference','model'=>'Demo\Human'),
			'friends' => array('type'=>'references','model'=>'Demo\Human'),
		)
	
	}

	class Student extends Human{
	
		protected static $attrs = array(
			'grade' => array('type'=>'string'),
			'classmates' => array('type'=>'references','model'=>'Demo\Student'),
		)
		
	}

Use:

	$bob = new Student( array('name'=>'Bob','age'=> 17 ,'gender'=>'male' ) );
	$bob->save();
	
	$john = new Student( array('name'=>'John','age'=> 16 ,'gender'=>'male' ) );
	$john->save();
	
	$lily = new Student( array('name'=>'Lily','age'=> 16 ,'gender'=>'female' ) );
	$lily->save();
	
	$lisa = new Human( array('name'=>'Lisa','age'=>41 ,'gender'=>'female' ) );
	$lisa->save();
	
	$david = new Human( array('name'=>'David','age'=>42 ,'gender'=>'male') );
	$david->save();
	
	$bob->dad = $david;
	$bob->mum = $lisa;
	$bob->classmates = array( $john, $lily );
	$bob->save();

Retrieve and check value:

	$bob = Student::one( array("name"=>"Bob") );
	
	echo $bob->dad->name;    // David
	
	$classmates = $bob->classmates;
	
	echo $classmates->count(); // 2
    
	var_dump($classmates->get(0)); // john	

Retrieve subclass

Retrieve all Human records , queries without '_type' because of it's a toplevel class.

    $humans = Human::all();

Retrieve all Student records , queries with { "_type":"Student" } because of it's a subclass.

    $students = Student::all();

Retrieve subclass without _type

To retrieve a record without the _type criteria (i.e. { "_type":"Student" }) set:

class Student extends \Purekid\Mongodm\Model
{
    protected static $useType = false;

    protected static $collection = 'Student';
}

Make sure to set a collection otherwise you will get results with every _type.

Other static methods in Model

	User::drop() // Drop collection 
	User::ensureIndex()  // Add index for collection

Model Hooks

The following hooks are available:

__init()

Executed after the constructor has finished

__preInsert()

Executed before saving a new record

__postInsert()

Executed after saving a new record

__preUpdate()

Executed before saving an existing record

__postUpdate()

Executed after saving an existing record

__preSave()

Executed before saving a record

__postSave()

Executed after saving a record

__preDelete()

Executed before deleting a record

__postDelete()

Executed after deleting a record

Special thanks to

mikelbring Paul Hrimiuc