YiiMongoDbSuite Manual 1.3.6
Copyright © 2011 CleverIT |
Available under the terms of license: GNU Free Documentation License 2.1 |
Generated: 28.01.2011 |
Table of Contents
- 1. Introduction
- 2. Changelog
- 3. Setup
- 4. Basic Usage
- 5. Querying
- 6. The Advanced Stuff
- 7. Gii Support
- 8. Special Topics
- Table of Contents
1. Introduction - 2. Changelog
Next »
1. Introduction
Author | Dariusz Górecki |
---|
This extension is an almost complete, ActiveRecord like support for MongoDB in Yii
It originally started as a fork of MongoRecord extension written by tyohan, to fix some major bugs, and add full featured suite for MongoDB developers.
Limitations:
- The main limitations are only those present in MongoDB itself, like the 4MB single document data limit. But That's not a big deal either.
- In it's current incarnation, This extension does NOT work with the OR operator. When we get it working we will remove this line and add an example.
Requirements:
- Yii 1.1.5 is required
- MongoDB latest stable is recommended. Untested with older versions.
Known bugs
- Remember, this is not complete yet. So at this stage, it can have some ;]
- If you find any please let me know
- As said before, it does not work with the OR operators
Resources
- Project page on GitHub
- MongoDB documentation
- On-Line version of this documentation
- PHP MongoDB Driver docs
- Standard Yii ActiveRecord Documentation
Contribution needed!
- I'm not English native speaker, need someone who can correct/rewrite/write my documentation and/or PHPDoc's in code
- Any help would be great :)
- Contact me: darek.krk on a gmail dot com or via GitHub PM
Big thanks goes to:
- tyohan: for first inspirations and idea of extension
- luckysmack: for big help with testing and documentation
- Jose Martinez and Philippe Gaultier, for implementing and sharing GridFS support
- Nagy Attila Gábor, for big help with new functionality and testing
- Table of Contents
2. Changelog - 1. Introduction
« Previous - 3. Setup
Next »
2. Changelog
Author | Dariusz Górecki |
---|
This is the 1.3.6 release
New features in 1.3.6
- Few annoying bugs fixed
- Documentation corrects
- Added
EMongoPartialDocument
class, that supports full-featured partial loading of documents from DB - Added fixtures manager, that can replace the Yii default one, and work with Mongo model
- Ability to do massive partial updates of multiple documents
New features in 1.3.5
- A lot of BUGs fixed, improved code readability, better in-code documentation, better exception handling
- Added support for PHP Mongo driver versions below 1.0.5
- Ability to do Extreme Efficent document partial updates, that make use of MongoDB
$set
operator/feature - Support for partial loading of documents from DB
- Improved EEmbeddedArraysBehavior class implementation, with cache support
- Added Gii MongoCRUD light template, that will produce one index view instead of two admin/index views pair
New features in 1.3.4
- Added "Soft" Documents support, soft documents do not have fixed attribute lists, for more info see soft models section
- Added support for global temporary folder used by GridFS
- Added missing deleteByPk method to
EmongoDocument
- Added support for generating Mongo models from existing SQL tables, see Gii section
New features in 1.3.3
- Ability to set FSync and Safe flag on different scopes, see the Write queries flags documentation part
- Ability to use efficient Cursors instead of raw array, returned by the findAll* methods see Use Cursor Flag documentation part
New features in 1.3.2
- Fixed bug that find* methods don't accept criteria in array format
- Lazy loading/creating of embedded documents
- Little performance boost, save embedded documents configs in static array
New features in 1.3.1
- Fixed few major bugs in EMongoCriteria class
- Bugfixes to criteria object had forced to change the criteria object creation from array, please see the updated doc for it
- Fixed bug that in massive assign way, embedded documents always used only safe attributes
- Setup of PHPUnit testing framework
- Written test cases for EMongoDB class and EMongoCriteria class
New features in 1.3
- MongoDB GridFS feature support, thanks to work of: Jose Martinez and Philippe Gaultier
New features in 1.2.3
- Gii CRUD generator, now generates advanced search form in admin (like in regular Yii CRUD generator)
- Search form now supports comparsion operators ( > | < | >= | <= | <> | != | == | = )
- In v1.2.2 all search attributes where treated as a string regex, now you can test numbers to, just juse comparsion operators, example:
- search for '1234' will try to find a string matching regexp /1234/i (this will not work if field is numeric)
- search for '= 1234' will try to find numberic value 1234
- search for '>= 1234' will try to find any record with field greater or equals numeric value 1234
New features in 1.2.2
- Magic method search() delivered with every EMongoDocument object
- Gii CRUD generator now uses search method, to provide text search in admin view (by default using case-insensitive regexp)
- EMongoDocument search() method has one parameter:
- $caseSensitive true|false, default to false, whathever to use case-sensitive string comparsion
New features in 1.2.1
- Support for generating CRUD for EMongoDocument models!
- Few minor bug fixes
New features in 1.2
- Support for using any other than _id field as a Primary Key
- Better names scopes handling, support for default scope
- Support to have different models in single collection! see Multimodel collection topic
- Better memory management, now there is only one collection object instance, per model
New features in 1.1
- automated efficient index definition for collections, per model
New features in 1.0:
- Named scopes just like in AR
- Very easy to use criteria object, you don't have to create complex MongoDB query arrays!
- Better exeption handling
- A lot more PHPDocs in code
Features:
- Support of using Class::model()->find / findAll / count / countByAttributes and other Yii ActiveRecord syntax
- Support of schema-less documents with Yii standard rules and validation features
- Endless Embedded/Nested document support (Embedded documents are their own Model class with their own rules and other methods.
- (almost, limited only by MongoDB 4MB limit of single document) endless document embedding/nesting
- Ready to go out-of-box EFFICIENT DataProvider, witch use native php db driver sort, limit and offset features for returning results!
- Records and embedded documents inherit from CModel, so you can use every class witch can handle of CModel (ie: Gii form generator)
- relation support idea/concept/example
- Table of Contents
3. Setup - 2. Changelog
« Previous - 4. Basic Usage
Next »
3. Setup
Author | Dariusz Górecki |
---|
In your main configuration file, witch is by default: protected/config/main.php
config file.
Add the following to the file:
'import' => array( // ... 'ext.YiiMongoDbSuite.*', ), 'components' => array( // ... 'mongodb' => array( 'class' => 'EMongoDB', 'connectionString' => 'mongodb://localhost', 'dbName' => 'myDatabaseName', 'fsyncFlag' => false, 'safeFlag' => false, 'useCursor' => false, ), // ... ),
connectionString
: 'localhost' should be changed to the ip or hostname of your host being connected to. For example if connecting to a server it might be'connectionString' => 'mongodb://username@xxx.xx.xx.xx'
where xx.xx.xx.xx is the ip (or hostname) of your webserver or host.dbName
: is the name you want the collections to be stored in. The database name.fsyncFlag
andsafeFlag
- see the Write Queries Flags Section, state of this flags has massive impact on behavior of this extension, PLEASE read the linked chapter!useCursor
flag see Use cursor special topic- For more info see the MongoDB connection page on php.net.
That's all you have to do for setup. You can use it very much like the active record. Short example:
$client = new Client; $client->first_name='something'; $client->save(); $clients = Client::model->findAll();
- Table of Contents
4. Basic Usage - 3. Setup
« Previous - 4.1. Simple Model (Document in collection)
Next »
4. Basic Usage
Author | Dariusz Górecki |
---|
This section shows some basic, quick-start usage examples, follow to the next chapters.
EMongoDocument
andEMongoEmbeddedDocument
extends standardCModel
class, you can do with them all things that can be done with standardCModel
objects!
- 4. Basic Usage
4.1. Simple Model (Document in collection) - 4. Basic Usage
« Previous - 4.2. Simple Embeded Document Model
Next »
4.1. Simple Model (Document in collection)
Author | Dariusz Górecki |
---|
For basic use, just declare simple model, under yours application models directory,
we will show simple User model that will represent documents stored in users
MongoDB collection.
class User extends EMongoDocument // Notice: We extend EMongoDocument class instead of CActiveRecord { public $personal_no; public $login; public $first_name; public $last_name; public $email; /** * This method have to be defined in every Model * @return string MongoDB collection name, witch will be used to store documents of this model */ public function getCollectionName() { return 'users'; } // We can define rules for fields, just like in normal CModel/CActiveRecord classes public function rules() { return array( array('login, email, personal_no', 'required'), array('personal_no', 'numeric', 'integerOnly' => true), array('email', 'email'), ); } // the same with attribute names public function attributeNames() { return array( 'email' => 'E-Mail Address', ); } /** * This method have to be defined in every model, like with normal CActiveRecord */ public static function model($className=__CLASS__) { return parent::model($classname); } }
Now we can star using our model, just as normal Yii ActiveRecord!
$user = new User(); $user->personal_no = 1234; $user->login = 'somelogin'; $user->email = 'email@example.com'; $user->save(); // This will store document with user data into MongoDB collection
- 4. Basic Usage
4.2. Simple Embeded Document Model - 4.1. Simple Model (Document in collection)
« Previous - 4.3. Embedded Documents
Next »
4.2. Simple Embeded Document Model
Author | Dariusz Górecki |
---|
To fully understand this example please refer to Embedded Documents Models Section
To use embedded documents you need to do two steps:
- First create model for embedded document (we will add address as embedded document to User model from previous example ):
/** * Note that we extending EMongoEmbeddedDocument, not regular EMongoDocument class * this is for performance reasons, embedded documents do not need to have, all * connection, and collection management stuff, this is explained in [Embedded Documents Models Section][basic.embeddedDocuments] * * NOTE: You may define regular EMongoDocument as an embedded in another one, if you really want to, it will still work */ class UserAddress extends EMongoEmbeddedDocument { public $apartment; public $house; public $street; public $city; public $zip; // We may define rules for embedded document too public function rules() { return array( array('apartment, house, street, city, zip', 'required'), // ... ); } // And attribute names too public function attributeNames() { /* ... */ } // NOTE: for embedded documents we do not define static model method! // we do not define getCollectionName method either. }
- Next we have to define
embeddedDocuments()
method in model that will contain embedded document
Add embeddedDocument()
method to previous User
model example:
class User extends EMongoDocument { // ... /** * This method should return simple array that will define field names for embedded * documents, and class to use for them */ public function embeddedDocuments() { return array( // property field name => class name to use for this embedded document 'address' => 'UserAddress', ); } // ... }
Now, the fun part starts!
We can now do things like:
$user = new User(); $user->address->city = 'New York'; $user->address->street = 'Some street name'; // This will save user to users collection, with UserAddress embedded document set, // and this handle with validation of embedded documents too! $user->save(); // After that: $user = User::model()->find(); // Models will be automatically populated with embedded documents that they contain, // so we can do: echo $user->address->city;
- 4. Basic Usage
4.3. Embedded Documents - 4.2. Simple Embeded Document Model
« Previous - 4.4. Arrays
Next »
4.3. Embedded Documents
Author | Dariusz Górecki |
---|
Basic informations about embedded documents
Must know:
- For performance reasons all models of embedded documents should extend from
EMongoEmbeddedDocument
class - But the above is not a must, things will still work if you define as embedded child classes of regular
EMongoDocument
- You can define as many embedded documents as you wish
- Every embedded document can contain embedded documents!
- The only limit for this mechanism is that serialized version (in raw array format) of whole document, must not extend the 4 MB size
- For documents bigger than 4 MB see the GridFS Section
- Main difference between
EMongoDocument
and EMongoEmbeddedDocument` is that:- EMongoDocument extends from EMongoEmbeddedDocument
- EMongoDocument is equipped with all methods needed to save its contents into a MongoDB collection
- You cannot call ie
save()
method on a EMongoEmbeddedDocument
Defining embedded documents within document
This applies to EMongoDocument and EMongoEmbeddedDocument
Just define the embeddedDocuments()
method in yours model class, it should return array of
simple key => value pairs.
- Array values are class names that will be used to instantinate embedded documents
- Array keys are treated as property names of given embedded document class
example:
// ... // within model class public function embeddedDocuments() { return array( 'address' => 'UserAddress', 'some_other_field_name' => 'AnyEMongoEmbeddedDocumentChildClass', ); } // this will give you access to propeties of model: $model->address->embeddedExampleField; $model->some_other_field_name->embeddedExampleField;
How to force save of embedded document into collection
- First we need to get the mongo collection object ie:
$collection = SomeModelClass::model()->getCollection();
$collection = SomeModelClass::model()->getDb()->collectionName;
$collection = Yii::app()->getComponent('mongodb')->getConnection()->collectionName;
- When we have our collection model, we now can force save of embedded document as a regular root document:
$collection->save($ourEmbeddedModel->toArray());
- 4. Basic Usage
4.4. Arrays - 4.3. Embedded Documents
« Previous - 4.4.1. Simple Arrays
Next »
4.4. Arrays
Author | Dariusz Górecki |
---|
You can in extreme simple way store any arrays in MongoDB, next sections will describe how to get it done.
- 4.4. Arrays
4.4.1. Simple Arrays - 4.4. Arrays
« Previous - 4.4.2. Array of Embedded Documents
Next »
4.4.1. Simple Arrays
Author | Dariusz Górecki |
---|
Just define a property in yours model, and store an array in it! YES This is that simple
Example:
class User extends EMongoDocument { // ... public $myArray; // ... } $myArray = array('our', 'simple', 'example', 'array'); $user = new User(); $user->myArray = $myArray; $user->save(); // that's it! // in any time after when you get yours model form DB $user = User::model()->find(); echo $user->myArray[1]; // will return 'simple'
- 4.4. Arrays
4.4.2. Array of Embedded Documents - 4.4.1. Simple Arrays
« Previous - 5. Querying
Next »
4.4.2. Array of Embedded Documents
Author | Dariusz Górecki |
---|
There is no way (that I know) where I can easily provide mechanism for this, you have to write Your own
This is how I accomplish it for now.
Within suite package you should have and extra
folder that contains (not only)
EEmbeddedArraysBehavior
class, we can use it to store an arrays of embedded documents.
Example:
class User extends EMongoDocument { // ... // Add property for storing array public $addresses; // Add EEmbeddedArraysBehavior public function behaviors() { return array( 'embeddedArrays' => array( 'class'=>'ext.YiiMongoDbSuite.extra.EEmbeddedArraysBehavior', 'arrayPropertyName'=>'addresses', // name of property, that will be used as an array 'arrayDocClassName'=>'ClientAddress' // class name of embedded documents in array ), ); } // ... }
Now you can use property arrays as an array of embedded documents:
$user = new User(); $user->addresses[0] = new ClientAddress(); $user->addresses[0]->city = 'New York'; $user->save(); // Behavior will automatically handle of validation, saving etc. // OR: $user = User::model()->find(); foreach($user->addresses as $userAddress) { echo $userAddress->city; }
- Table of Contents
5. Querying - 4.4.2. Array of Embedded Documents
« Previous - 5.1. Simple queries
Next »
5. Querying
Author | Dariusz Górecki |
---|
This is one of the things that makes this extension great. It's very easy to query for the objects you want.
This section covers all possibilities of querying the DB for results.
Basically this covers usage of EMongoCriteria
object.
- 5. Querying
5.1. Simple queries - 5. Querying
« Previous - 5.2. The EMongoCriteria Object
Next »
5.1. Simple queries
Author | Dariusz Górecki |
---|
You can use methods that you have used to with standard Yii CActiveRecord
's
- To find only one first matched document simply call
$model = ModelClass::model()->find()
- To find document that has some arguments set call
$model = ModelClass::model()->findByAttributes(array('attributeName'=>'attributeValue', 'attribute2'=>'otherValue'))
- You can search models by theyre primary key
$model = ModelClass::model()->findByPk(new MongoID(/* ... */))
To understand PK queries refer to [Primary Keys Section][advanced.primaryKeys]
- All of above methods have the 'All' version, ie:
findAll()
,findAllByAttributes
- They may return an array of models, or models cursor, we'll cover the cursor later
This is almost the same behavior like standard Yii ActiveRecord models, refer to them for a basic idea
- 5. Querying
5.2. The EMongoCriteria Object - 5.1. Simple queries
« Previous - 6. The Advanced Stuff
Next »
5.2. The EMongoCriteria Object
Author | Dariusz Górecki |
---|
By default MongoDB requires to build advanced query arrays if you want advanced search for documents in DB.
The EMongoCriteria object simplifies process of building this query arrays.
First of all create the object instance: $criteria = new EMongoCriteria();
You may define query criteria in three simple ways:
- Simple 'equlas' condition, that is equivalent of SQL:
WHERE fieldName = 'fieldValue'
:$criteria->fieldName = 'fieldValue';
- By field name call ie. SQL:
WHERE fieldName > 1234
:$criteria->fieldName('>', 1234);
- By addCond method ie. SQL:
WHERE fieldName <= 1234
:$criteria->addCond('fieldName', '<=', 1234);
You can add multiple conditions, using any of the above techniques, they will be mixed together.
In addition to above the criteria object has additional special methods.
limit($value)
that takes as parameter the query results limit, same as SQLLIMIT
directiveoffset($value)
that takes as parameter the query results offset to get, same as SQLLIMIT
directive second parametersort($fieldName, $sortDirection)
this will be covered shortlyselect($array)
this will be covered shortly
If you want to add simple criteria, that will use field name that is a method name from the above list, you can use the addCond method ie.
$criteria->addCond('limit', '==', 1234);
sort EMongoCriteria method
The sort method takes two parameters
- first is a field name, to use for sorting
- second is a sort direction, witch may be one of:
EMongoCriteria::SORT_ASC
for ascending sorting (positive value 1)EMongoCriteria::SORT_DESC
for descending sorting (negative value -1)
You can call sort method as many times as you wish, the sorting criteria will be mixed together.
select EMongoCriteria method
The select method takes only one argument, array of field names.
By default MongoDB will return all fields from of document, use this method to tell mongo that you want only specified field list.
- MongoDB regardless of anything, will always return the _id field within result sets
- You can call sort method multiple times, all calls will be merged using array_merge function
List of supported operators
List consist of operators and after |
available shortcut (if any)
Operators are case-insensitive
- 'greater' | >
- 'greaterEq' | >=
- 'less' | <
- 'lessEq' | <=
- 'notEq' | !=, <>
- 'in' |
- 'notIn' |
- 'all' |
- 'size' |
- 'exists' |
- 'notExists' |
- 'type' | // BSON type see mongodb docs for this
- 'mod' | %
- 'equals' | ==
- 'elemMatch' |
the $or operator in newer versions of mongodb does NOT work with this extension yet. We will add it to the list above when it is fixed.
Newer versions of mongodb will work, just not the $or operator.
For examples and use for how to use these operators effectively, use the MongoDB Operators Documentation here.
Examples:
// first you must create a new criteria object $criteria = new EMongoCriteria; // find the single user with the personal_number == 12345 $criteria->personal_number('==', 12345); // OR like this: $criteria->personal_number = 12345; $user = User::model->find($criteria); // find all users in New York. This will search in the embedded document of UserAddress $criteria->address->city('==', 'New York'); // Or $criteria->address->city = 'New York'; $users = User::model()->findAll($criteria); // Ok now try this. Only active users, only show at most 10 users, and sort by first name, descending, and offset by 20 (pagination): // note the sort syntax. it must have an array value and use the => syntax. $criteria->status('==', 1)->limit(10)->sort(array('firstName' => EMongoCriteria::SORT_DESC))->offset(20); $users = User::model()->findAll($criteria); // A more advanced case. All users with a personal_number evenly divisible by 10, sorted by first name ascending, limit 10 users, offset by 25 users (pagination), and remove any address fields from the returned result. $criteria->personal_number('%', array(10, 0)) // modulo => personal_number % 10 == 0 ->sort(array('firstName' => EMongoCriteria::SORT_ASC)) ->limit(10) ->offset(25); $users = User::model()->findAll($criteria);
Regexp / SQL LIKE replacement
You can use native PHP Mongo driver class MongoRegex, to query:
// Create criteria $criteria = new EMongoCriteria; // Find all records witch have first name starring on a, b and c, case insensitive search $criteria->first_name = new MongoRegex('/[abc].*/i'); $clients = Client::model()->findAll($criteria); // see phpdoc for MongoRegex class for more examples
Creating criteria object from an array:
// Example criteria $array = array( 'conditions'=>array( // field name => operator definition 'FieldName1'=>array('greaterEq' => 10), // Or 'FieldName1'=>array('>=' => 10) 'FieldName2'=>array('in' => array(1, 2, 3)), 'FieldName3'=>array('exists'), ), 'limit'=>10, 'offset'=>25, 'sort'=>array('fieldName1' => EMongoCriteria::SORT_ASC, 'fieldName4' => EMongoCriteria::SORT_DESC), ); $criteria = new EMongoCriteria($array); // or $clients = ClientModel::model()->findAll($array);
- Table of Contents
6. The Advanced Stuff - 5.2. The EMongoCriteria Object
« Previous - 6.1. Indexing
Next »
6. The Advanced Stuff
Author | Dariusz Górecki |
---|
YiiMongoDbSuite enables you to do even more fun things!
- 6. The Advanced Stuff
6.1. Indexing - 6. The Advanced Stuff
« Previous - 6.2. Primary Keys
Next »
6.1. Indexing
Author | Dariusz Górecki |
---|
Now you can define indexes for yours collections in easy way!
Suite will check for existing of indexes only once, at the first class use, (per script-calls) if it not find any of declared indexes it will create them
Only thing you need is to define the indexes()
method in yours model class, see the example:
class Client extends EMongoDocument { // .... public function indexes() { return array( // index name is not important, you may write whatever you want, just must be unique 'index1_name'=>array( // key array holds list of fields for index // you may define multiple keys for index and multikey indexes // each key must have a sorting direction SORT_ASC or SORT_DESC 'key'=>array( 'field_name'=>EMongoCriteria::SORT_ASC 'field_name.embeded_field'=>EMongoCriteria::SORT_DESC ), // unique, if indexed field must be unique, define a unique key 'unique'=>true, ), ); } // .... }
If you whant to disable index existing checking on every page load,
because of perfomance reasons (recomended for production)
put $this->ensureIndexes = false;
into yours init()
method in model class.
class Client extends EMongoDocument { // .... public function init() { $this->ensureIndexes = false; } // .... }
- 6. The Advanced Stuff
6.2. Primary Keys - 6.1. Indexing
« Previous - 6.3. Named Scopes
Next »
6.2. Primary Keys
Author | Dariusz Górecki |
---|
By default YiiMongoDbSuite will always use the _id
field as a collection primary key.
MongoDB itself guarantee that
_id
field will be unique across collection.
The _id
field
The _id
field in a short is, be default an instance of MongoID
class.
If you will do echo $model->_id
you will see something like: '4ced75e1eb4ae8ca44000000'
This is basically, the textual representation of auto-generated by MongoDB _id field.
But be aware that MongoID is not a string!
This will not find anything! $model->findByPk('4ced75e1eb4ae8ca44000000');
To find an model with specified ID use:
$model->findByPk(new MongoID('4ced75e1eb4ae8ca44000000'));
You can put any value into an
_id
fieldMongoDB will auto populate _id field by MongoID objects only when it is set to
NULL
Own defined Primary Key for collection
You can define a own field to be used as a Primary Key for yours collection, see example:
class Client extends EMongoDocument { // ... // Define primaryKey method: public function primaryKey() { return 'personal_number'; // Model field name, by default the _id field is used } // Now you can: // Client::model()->findByPk(1234); // personal_number == 1234 // ... }
- 6. The Advanced Stuff
6.3. Named Scopes - 6.2. Primary Keys
« Previous - 6.4. Relations
Next »
6.3. Named Scopes
Author | Dariusz Górecki |
---|
Now you can use AR style named scopes just define scopes method in yours model:
class Client extends EMongoDocument { // (...) public function scopes() { return array( 'scopeName'=>array(/* Array for criteria object creation see Criteria from array topic */), ); } public function defaultScope() { return array( 'conditions'=>array( 'active'=>array('==', true), ), ); } // (...) } // now You can: // this will find only clients that are active (default scope) and clients matching 'scopeName' $all = Client::model()->scopeName()->findAll();
Parameterized Named Scopes
class Client extends EMongoDocument { // (...) public function byStatus($status) { $criteria = $this->getDbCriteria(); $criteria->status = $status; $this->setDbCriteria($criteria); return $this; } // (...) } // now You can: // this will find only clients that have status set to parameter value: $all = Client::model()->byStatus(1)->findAll();
You can chain multiple named scopes
example:
Client::model()->active()->byStatus(1)->findAll();
- 6. The Advanced Stuff
6.4. Relations - 6.3. Named Scopes
« Previous - 6.5. Data Provider Object
Next »
6.4. Relations
Author | Dariusz Górecki |
---|
- In NoSQL World Relations are not so obvious as in RDBMS
- Because NoSQL databases are designed for performance, there is no need of defining something more complex than correct use of find() method and indexing for fetching related records
- This is just an idea/concept/example you can do things in yours preferred way! this is schema-less world, think different!
- MongoDB Documentation has a clean examples and how-to's for handling relations, please read them for better understanding of relations in NoSQL world
HAS_ONE relation
// just define method in yours model class (assume we have client collection, and address collection in client model public function address() { return Address::model()->findByAttributes(array('attribute_with_client_id'=>$this->primaryKey())); }
BELONGS_TO relation
// define in address model public function client() { return Client::model()->findByPk($this->attribute_with_client_id); }
HAS_MANY relation
// assume we have clients and orders collection // define in client: public function orders() { return Client::model()->findAllByAttributes(array('client_id'=>$this->primaryKey())); }
MANY_MANY relation
As simple as defining reverse HAS_MANY relation in orders model. You can view the Example models for details.
- 6. The Advanced Stuff
6.5. Data Provider Object - 6.4. Relations
« Previous - 6.6. Write Queries Flags
Next »
6.5. Data Provider Object
Author | Dariusz Górecki |
---|
// basic dataprovider returns whole collection (with efficient pagination support out-of-box) $dp = new EMongoDocumentDataProvider('modelClassNameOrInstance'); // data provider with query $dp = new EMongoDocumentDataProvider('modelClassNameOrInstance', array( 'conditions'=>/* query array goes here */ )); // data provider, enable sorting (needs to be set explicit) $dp = new EMongoDocumentDataProvider('modelClassNameOrInstance', array( /* standard config array */ 'conditions'=>array(/* query array goes here */) 'sort'=>array( 'attributes'=>array( // list of sortable attributes ) ), ));
- 6. The Advanced Stuff
6.6. Write Queries Flags - 6.5. Data Provider Object
« Previous - 6.7. Document Partial Updates
Next »
6.6. Write Queries Flags
Author | Dariusz Górecki |
---|
Quote from PHP Manual:
"safe"
Can be a boolean or integer, defaults to FALSE. If FALSE, the program continues executing without waiting for a database response. If TRUE, the program will wait for the database response and throw a MongoCursorException if the write operation did not succeed.
If you are using replication and the master has changed, using "safe" will make the driver disconnect from the master, throw and exception, and attempt to find a new master on the next operation (your application must decide whether or not to retry the operation on the new master).
If you do not use "safe" with a replica set and the master changes, there will be no way for the driver to know about the change so it will continuously and silently fail to write.
If safe is an integer, will replicate the insert to that many machines before returning success (or throw an exception if the replication times out, see wtimeout). This overrides the w variable set on the collection.
"fsync"
Boolean, defaults to FALSE. Forces the insert to be synced to disk before returning success. If TRUE, a safe insert is implied and will override setting safe to FALSE.
We now can set value for write queries flags FSync and/or Safe on a different scopes:
- Global scope, by setting it in EMongoDB component class itself (required)
- Model scope, bu using
ModelClass::model()->setFsyncFlag($flagValue)
orModelClass::model()->setSafeFlag($flagValue)
- Single Model object by using
$singleModelObject->sefFsyncFlag($flag)
or$singleModelObject->sefSafeFlag($flag)
State of write queries flags is connected
Setting FSync to
TRUE
, will set Safe flag toTRUE
automatically
Now, we may want all DB operations to be synced to disk, and we set global FSync flag in MongoDB component config, but we feagure out that, for ie. massive temporary models like DB logging objects are not important, and for performance reasons, we whant to disable FSync for this models only.
We may do so: LogModelClass::model()->setFsyncFlag(false);
From now on, all LogModelClass objects will be written to DB without waiting to filesystem sync.
For user comfort, FSync and Safe flags may be set just for single model instance by, $singleModelInstance->setFsyncFlag(false)
,
this will not affect any other model instance.
- 6. The Advanced Stuff
6.7. Document Partial Updates - 6.6. Write Queries Flags
« Previous - 6.8. Massive Partial Updates
Next »
6.7. Document Partial Updates
Author | Dariusz Górecki |
---|
Since the v1.3.5
You can do partial updates of documents.
Normally this extension, when using
EmongoDocument::save()
will always replace all contents of document in DB with the actual values of fields in YoursEMongoDocument
instance (including all embedded documents etc.)
You can pass to EMongoDocument::update()
method a list of attributes for update, this will use an extreme efficient
Mongo $set
operator to update only listed fields, see example:
$model = ModelClass::model()->find(); // Find some document to work with // change some fields $model->field1 = 1; $model->field2 = 'value'; // Optional: run validation, of changed fields: $model->validate(array('field1', 'field2')); // Use partial update $model->update(array('field1', 'field2'), true /* <- second parameter indicates to use partial update mechanism */);
And thats it, this will only update those 2 fields, rest in DB will not be touched.
- 6. The Advanced Stuff
6.8. Massive Partial Updates - 6.7. Document Partial Updates
« Previous - 6.9. GridFS
Next »
6.8. Massive Partial Updates
Author | Philippe Gaultier |
---|
Since the v1.3.6
You can perform partial updates of multiple documents.
// prepare modifiers $modifier = new EMongoModifier(); // replace field1 value with 'new value' $modifier->addModifier('field1', 'set', 'new value'); // increment field2 value by 1 $modifier->addModifier('field2', 'inc', 1); // prepare search to find documents $criteria = new EMongoCriteria(); $criteria->addCond('field3','==', 'filtered value'); // update all matched documents using the modifiers $status = ModelClass::model()->updateAll($modifier, $criteria);
And thats it, this will only update those 2 fields (force value of field1
and increment value of field2
)
for all the documents having field3 == 'filtered value'
, everything else in the db will remain untouched.
Available modifiers are :
- inc
- set
- unset
- push
- pushAll
- addToSet
- pop
- pull
- pullAll
- rename
You can find detailed explanation about usage of those modifiers on the original MongoDb documentation.
EMongoModifier can be defined during creation :
// prepare modifiers $modifier = new EMongoModifier( array( 'fieldName1'=>array('inc' => $incValue), 'fieldName2'=>array('set' => $targetValue), 'fieldName3'=>array('unset' => 1), 'fieldName4'=>array('push' => $pushedValue), 'fieldName5'=>array('pushAll' => array($pushedValue1, $pushedValue2)), 'fieldName6'=>array('addToSet' => $addedValue), 'fieldName7'=>array('pop' => 1), 'fieldName8'=>array('pop' => -1), 'fieldName9'=>array('pull' => $removedValue), 'fieldName10'=>array('pullAll' => array($removedValue1, $removedValue2)), 'fieldName11'=>array('rename' => $newFieldName), ) );
, during execution
$modifier = new EMongoModifier(); $modifier->addCond($fieldName1, 'inc', $incValue), $modifier->addCond($fieldName2, 'set', $targetValue), $modifier->addCond($fieldName3, 'unset', 1), $modifier->addCond($fieldName4, 'push', $pushedValue), $modifier->addCond($fieldName5, 'pushAll', array($pushedValue1, $pushedValue2)), $modifier->addCond($fieldName6, 'addToSet', $addedValue), $modifier->addCond($fieldName7, 'pop', 1), $modifier->addCond($fieldName8, 'pop', -1), $modifier->addCond($fieldName9, 'pull', $removedValue), $modifier->addCond($fieldName10, 'pullAll', array($removedValue1, $removedValue2)), $modifier->addCond($fieldName11, 'rename', $newFieldName),
or using the two methods
- 6. The Advanced Stuff
6.9. GridFS - 6.8. Massive Partial Updates
« Previous - 6.10. Soft Documents Models
Next »
6.9. GridFS
Author | Dariusz Górecki |
---|
You can use MongoDB GridFS feature to save big files within MongoDB collections.
The first thing you need to do, it to define model for yours GridFS collection, see example:
class Image extends EMongoGridFS { /** * This field is optional, but: * from PHP MongoDB driver manual: * * 'You should be able to use any files created by MongoGridFS with any other drivers, and vice versa. * However, some drivers expect that all metadata associated with a file be in a "metadata" field. * If you're going to be using other languages, it's a good idea to wrap info you might want them * to see in a "metadata" field.' * * @var array $metadata array of additional info/metadata about a file */ public $metadata = array(); // this method should return the collection name for storing files public function getCollectionName() { return 'images'; } // normal rules method, if you use metadata field, set it as a 'safe' attribute public function rules() { return array( array('filename, metadata','safe'), array('filename','required'), ); } /** * Just like normal ActiveRecord/EMongoDocument classes */ public static function model($className=__CLASS__) { return parent::model($className); } }
Every EMongoGridFS has a special field "filename", that is mandatory!
EMongoGridFS
has a feature that holds files in temporary folder, between operations, it can be set on global level in EMongoDB class, by setting propertygridFStemporaryFolder
to some dirname, or on model/object level by usinggridFStemporaryFolder()
setter
This part of documentation is not yet complete, for now, follow usage examples:
Yii::import('ext.YiiMongoDbSuite.examples.MongoImage'); //create a new image $image = new MongoImage(); $image->filename = '/var/www/myImage.JPG'; $image->metadata = array('value1'=>1, 'value2'=>2); $res = $image->save(); if($res !== true) echo 'error saving file'; //find image $image = MongoImage::model()->find(); if(!($image instanceof MongoImage)) echo 'error finding object'; //findall images $images = MongoImage::model()->findAll(); if(!is_array($images)) echo 'error on findall'; //delete images $image = MongoImage::model()->find(); if($image instanceof MongoImage) { $result = $image->delete(); if($result !== true) echo 'delete notok'; } else echo 'no image found to delete'; //deleteAll images $result = MongoImage::model()->deleteAll(); if(is_array($result)===true) { echo ' isarray'; if(isset($result['err']) === true ) echo ' error deleting images:'.$result['err']; else echo ' elements deleted:'.$result['n']; } //deletebyPk image $image = MongoImage::model()->find(); if($image instanceof MongoImage) { $result = MongoImage::model()->deleteByPk($image->_id); if($result !== true) echo 'error'; } else echo 'no image found to delete by pk'; //insert image and update $image = new MongoImage(); $image->filename = '/var/www/myImage.JPG'; $image->metadata = array('value1'=>1, 'value2'=>2); $res = $image->save(); $image->filename = '/var/www/myImageUpdated.JPG'; $image->metadata = array('value1'=>3, 'value2'=>4); $res = $image->save(); if($res !== true) echo 'error updating'; //MongoGridFSFile functions $image = MongoImage::model()->find(); //getBytes function $bytes = $image->getBytes(); //getFilename $filename = $image->getFilename(); //getSize $size = $image->getSize(); //write $image->write();
- 6. The Advanced Stuff
6.10. Soft Documents Models - 6.9. GridFS
« Previous - 7. Gii Support
Next »
6.10. Soft Documents Models
Author | Dariusz Górecki |
---|
Since the 1.3.4 version you can have models, that do not have fixed attribute list.
The only thing to get started is to define model that extends from EMongoSoftDocument
Example:
class MixedModel extends EMongoSoftDocument { // You still can define a field(s), that always will be defined // like in normal EMongoDocument, but this is optional public $field; // As always define the getCollectionName() and model() methods ! public function getCollectionName() { return 'mixed_collection'; } public static function model($className=__CLASS__) { return parent::model($className); } }
And thats it! Now you have model class that will handle of any field lists!
You still can use all features that are present in
EMongoDocument
because 'EMongoSoftDocument` extends from it.
Usage:
You need to init every soft attribute that you want to add to model, by using
$model->initSoftAttribute($attributeName)
.
$model = new MixedModel(); $model->initSoftAttribute('field1'); $model->initSoftAttributes(array('field2', 'field3')); $model->field = 'regularField'; $model->field1 = 'value'; // } $model->field2 = 'value2'; // } soft attributes, only in this model instance $model->field3 = 'value3'; // } $model->save(); // Finder will init and populate all soft attributes with values found in particular document automatically $newModel = MixedModel::model()->find(); echo $newModel->field3; // will print 'value3'
- Table of Contents
7. Gii Support - 6.10. Soft Documents Models
« Previous - 8. Special Topics
Next »
7. Gii Support
Author | Dariusz Górecki |
---|
Model generation
Since version 1.3.4 you can generate mongo document models based on existing SQL tables, just enable gii generators
provided within package (see below) and use MongoModel
generator.
CRUD Generation
Now you can generate CRUD for yours MongoDB Documents! just you have to add generator path to yours Gii config
By default generated models will use mongo
_id
field as a primary key (using MongoId class) this generator will use different field as a primary key, if you will overrideprimaryKey()
method to return something different than '_id'. Note that generator cannot handle with multifield primary key,
// in yours main.php config file: 'modules'=>array( // ... 'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'yours_password_to_gii', // add additional generator path 'generatorPaths'=>array( 'ext.YiiMongoDbSuite.gii' ), ), // ... ), // Now login to Gii and start using Mongocrud generator !
Forms generation
- This is a good news, Gii can generate the worst part of developer job, forms for mongo records ;]
- When generating a form from mongo record, comment out embedded docs array, or you'll see error about array/object creation
- For embedded docs just generate forms in separed way for each one
- Table of Contents
8. Special Topics - 7. Gii Support
« Previous - 8.1. Multimodel Collections
Next »
8. Special Topics
Author | Dariusz Górecki |
---|
This section covers some special topics, that need to bye mentioned
Behaviors
- You can use existing CActiveRecordBehaviors as long as they do not mess up with CActiveRecord explicit stuff (ie. behaviors that add relation handling will fail to work with EMongoDocuments)
- Behaviors may extend from EMongoDocumentBehavior class, standard AR behavior class can't use extra events witch are available here (beforeEmbeddedDocsInit, afterEmbeddedDocsInit, beforeToArray and afterToArray)
Performance
- By default all save/update/delete operations performed by internal command sets the FSYNC flag to TRUE, this means, all operations will have to wait for a disk sync!
- FSYNC in most cases is a good way and do not have massive impact on performance
- You may want to disable FSYNC flag when doing massive imports/updates/models, because it will be horrible slow!
- Fsync can be disabled by setting up fsyncField in EMongoDB class
Use Cursor Flag
Now we can set that all records returned by findAll* methods will return EMongoCursor instead of raw array, EMongoCursor implements Iterator interface and can be used with ie. foreach loop.
EMongoCursor will instantiate objects in lazy-loading way, only on first use.
By default useCursor flag is set to FALSE to keep backwards compatibility, note: - Cursor does not implement ArrayAccess interface, because offset access to cursor is very ineffective, and pointless - You can set useCursor flag in different scopes - globally in EMongoDB class, by setting $useCursor variable - Per model: ModelClass::model()->setUseCursor(true); - And per model instance: $modelInstance->setUseCursor(true);
Massive hand operations
// Example mass insert (you will want to disable fsyncField for this: for($i=0; $i<1000; $i++) { $c = new Client; $c->personal_number = $i; $c->validate(); // You can omit this if you want $c->getCollection()->insert($c->toArray()); }
- 8. Special Topics
8.1. Multimodel Collections - 8. Special Topics
« Previous
8.1. Multimodel Collections
Author | Dariusz Górecki |
---|
You can have different models in single collection, example:
class Client extends EMongoDocument { public $first_name; public $second_name; // define property for finding type public $type; // and some const for better remember const NORMAL_CLIENT = 0; const BUSINESS_CLIENT = 1; public function getCollectionName() { return 'clients'; } /** * We can override the instantiate method to return correct models */ protected function instantiate($attributes) { if($attributes['type'] == self::NORMAL_CLIENT) $model = new NormalClient(null); // We have to set scenario to null, it will be set, by populateRecord, later else if($attributes['type'] == self::BUSINESS_CLIENT) $model = new BusinessClient(null); $model->initEmbeddedDocuments(); // We have to do it manually here! $model->setAttributes($attributes, false); return $model; } public static function model($className=__CLASS__) { return parent::model($className); } } class NormalClient extends Client { public $additionalField; public function defaultScope() { return array( 'conditions'=>array('type'=>array('==' => self::NORMAL_CLIENT)), ); } public function beforeSave() { if(parent::beforeSave()) { $this->type = self::NORMAL_CLIENT; return true; } else return false } public static function model($className=__CLASS__) { return parent::model($className); } } class BusinessClient extends Client { public $taxNo; public function defaultScope() { return array( 'conditions'=>array('type'=>array('==' => self::BUSINESS_CLIENT)), ); } public function beforeSave() { if(parent::beforeSave()) { $this->type = self::BUSINESS_CLIENT; return true; } else return false } public static function model($className=__CLASS__) { return parent::model($className); } }
Now we can:
// now you can: $bclients = BuissnessClient::model()->findAll(); $clients = NormalClient::model()->findAll(); $allClients = Client::model()->findAll(); // but they're kept in single collection ! and can be indexed with single field etc.