I came accross MongoDB a few months ago and it seemed like a perfect fit for many of the projects I am working. Extremely fast inserts, map-reduce for complex queries, and most importantly, scaling is a breeze.
Since I am a Zend Framework guy I created a simple base model class for MongoDB. It is a very simple wrapper, but is effective for what I need. I usually create model classes for each “Collection” just like I would create models for each table in MySQL. Each model class extends from the new MongoDB base class and allows a low level “active directory” type access to MongoDB documents.
Get The Source Code Here
Example: We want to create a new document for every visitor that comes into a website that we are tracking. We store those documents in the “visitor” collection.
The first thing we do is create a model for the visitor collection.
class Model_Visitor extends Mongodb_ModelBase {
// If you don't specify the collection name explicitly,
// it will default to the name of the class minus the "Model_" part.
protected static $_collectionName = "visitor";
}
Then we can create new documents for that collection
$newVisitor = new Model_Visitor(); $newVisitor->ipAddress = '5.5.5.5'; $newVisitor->save();
You can also query for visitors using static methods:
$oneVisitor = Model_Visitor::findOne();
$allVisitors = Model_Visitor::find();
$someVisitors = Model_Visitor::find(array('ipAddress'=>'5.5.5.5'));
Use dot notation for nested values. These two commands do the same thing:
$myVisitor->{'referrer.url'} = 'google.com';
$myVisitor->referrer = array('url'=>'google.com');
I thought I would share this with the rest of the world in case someone needed it.
NOTE: The base class makes use of late static binding, which requires PHP 5.3
Jakub Petrykowski / November 16th, 2010 13:00
Hi. Tried your library, works well – thanks for sharing it!
One potential glitch:
when I invoke $myModel–save() it shows a PHP notice because $options is undefined in your base class code:
Notice: Undefined variable: options in C:\Dev\eclipse_php_workspace\rotg\library\mongo\Mongo_ModelBase.php on line 286
public static function insert($data, $safe = false){
static::init();
return static::$_collection->insert($data, $options);
}
ANyway, data is saved to DB. perhaps error reporting was off when you published.
is $options meant to be derived from $safe?
Anyway, thanks for this, will help me developing my app quickly!
Jakub
Clint / November 16th, 2010 17:48
Thanks for the heads up, Jakub. I’ve fixed the issue and pushed it to the git repository. I’m glad you are using the class. Let me know if you have any more issues.
Jakub Petrykowski / November 19th, 2010 21:43
Hi Clint,
One more thing that tricked me. I am confused and I don’t know if I understand what’s going on… but issue might be corrupting data, so it’s important.
I wrote a simple controller that created a model instance and referenced another model’s instance:
public function someAction() {
…
$item = new Model_Item(); // Model_Item inherits from your base class
$item->userid = $user->id; // $user is an instance of Model_User, based on your class, too
$item->content = $request->content;
…
$item->save();
All seems to be working fine. This was all within the controller.
But then I moved some of the request-to-model mapping code into Model_Item, so controller is “thinner”.
So I created this method in Model_Item:
public function mapFromRequest($request, $user) {
$this->userid = $user->id;
$this->content = $request->content;
…
Problem is that $user->id when called WITHIN the Model_Item class yields DIFFERENT result than $user->id when called within the controller. I am scratching my head how it’s possible.
$user is a serialized-then-unserialized instance of Model_User. User collection has a standard MongoDB _id field.
$user as seen in model or controller is identical (var_dump says so at least…)
But weird thing happens with $user->id:
$user->id as visible in model yields:
object(MongoId)#38 (0) {
}
[if I echo it, it yields the same id hex value as the value below of course]
$user->id as visible in controller yields:
string(24) “4ce2d6414403873411000000″
The latter looks as if .toString() from static __get() in base class was called, and the former reference just doesn’t make that call for some reason.
I don’t know why this happens, but this is very counterintuitive.
Any ideas what’s wrong? How is the fact that I moved the code from controller to model changed behavior of a simple ‘accessor’ call? Is it my code that’s causing this, or is it caused by your class?
Thanks -
jakub
Jakub Petrykowski / November 23rd, 2010 17:22
How do I achieve .skip(10) with the model?
I see that find(…) has sort and limit, but what about skip (to do pagination etc.)?
Oh and an update on my last question: I war using $model->id which is a protected field, so inside a class it shows one thing (MongoId object), but outside it’s invisible, so __get() getter is fired and returns something else (getter was returning MongoId->__toString()).
So I just started to reference _id everywhere, which always uses the getter, and I’m done.
Thanks -
Jakub
Vijaya Raghavan / October 11th, 2011 18:30
Hi Clint,
Thanks for publishing your work. I was trying to build a sample Zend application with your Mongo_ModelBase and here is what I found:
I cannot extend Mongo_ModelBase to model more than one collection. That is because the variable $_collection is static and comes in the way of being able to use the models simultaneously. For example, no matter which collection I try to query, the results are always from one of them.
Please let me know if you think I am doing something wrong.
Thanks for your help.
Rgds,
Vijay
karim / October 14th, 2011 19:59
Thanks for sharing. I have a problem with your idea of a model class representing a table/collection. Surely a model is just a class that provides methods for manipulating a business entity which may or may not map to a db table? E.g the model may communicate with many db tables or maybe not any at all!
Clint / October 14th, 2011 20:01
@Vijaya – In order for this to work, you need to have late static binding, which is only in effect on PHP 5.3, then you won’t have the issues you are having. Sorry to do that to you
Clint / October 14th, 2011 20:07
@karim – It’s just one way of doing things. In general, I would say your definition of a model is correct. Either way, it is sometimes nice to have a layer in zend framework between your models and tables (zend_db_table). You could use this library for something similar.
Manuel / April 25th, 2012 1:39
Hi Clint, I would like to know if you have more information, how to implement Mondo DB with Zend Framework. I’m working in my final project in the University and i would like to implemnt both. I will be glad if you can help me with this. Thanks a lot!!!
Clint / April 27th, 2012 20:13
@Manuel, I don’t have too much more info, but I have some links I can email you if you are having trouble finding more info. I will email you personally.
Ankit / May 9th, 2012 10:53
Hi Clint,
I am currently using this class extensively with ZF and it is awesome. I want to find a way to reuse the connections established in the class as persistent connections and not close the connection at the end of executing an action when the variables go out of scope.
Clint / May 9th, 2012 14:15
@Ankit – Because I am using late static binding on that class, it will reuse the same connection for all models. Is that what you mean by persisting?
Anant / May 21st, 2012 11:31
Hi,
How would I achieve something like :
db.users.find( { $or : [ { a : 1 } , { b : 2 } ] } )
I tried using :
::find(array($or),array(“licensekey”=>$licensekey,”username”=>$username)); which I perosnally feel is wrong .. Can you please guide !
Thanks
Clint / June 5th, 2012 16:52
@Anant – Sorry for the delay. I was on an awesome vacation.
To do a find like that you would use something more along these lines:
yourmodel::find(array(‘$or’=>array(‘a’=>1, ‘b’=>2)));
Let me know if that works for you. I have an updated class I need to upload that is slightly better (but not much)
Good Luck!
Anant / June 7th, 2012 6:14
Hi Clint,
Thanks for the reply. I used your method for “or” which prompted me with error “$or requires nonempty array”.
Further digging on net I found a solution for the same, which works fine for me. So i have to do something like :
yourmodel::find(array(‘$or’=>array(array(‘a’=>1),array(‘b’=>2))));
Hope, it helps you in your updated class.
Thanks,
Anant
It works fine for me.