Coder Social home page Coder Social logo

mongodb-php-odm's Introduction

MongoDb PHP ODM

(for lack of a better title)

Overview

MongoDb PHP ODM is a simple object wrapper for the Mongo PHP driver classes which makes using Mongo in your PHP application more like ORM, but without the suck. It is designed for use with Kohana 3 but will also integrate easily with any PHP application with almost no additional effort.

Design Principles

  • Don’t become overly complex. There are some features that could be implemented, but if they are not it is because they would increase the complexity too much, sacrificing elegance and usability.
  • Keep Mongo schema-less. I like schema-less. Requiring schema’s in your model definitions is not schema-less. This library let’s you be schema-less.
  • Follow Mongo driver convention. In many ways, using this library will be pretty similar to using the official driver.
  • Do not add unnecessary overhead. Sure, it is not as fast as using the driver directly, but I’m quite sure you won’t notice the difference.
  • Don’t Repeat Yourself. Very little code will be repeated while using this library.
  • Reduce the number of database requests/updates. Prevent redundant requests and multiple updates to the same document.
  • Choose your favorite design pattern. This library now supports using Mongo_Collection directly as a convenience wrapper, or choose between two ActiveRecord like patterns that resemble either the Table Data Gateway pattern or the Row Data Gateway pattern

Basic Idea

The basic idea is to boil the usage of the Mongo PHP library down to three classes:

  • Mongo_Database
  • Mongo_Collection
  • Mongo_Document

Mongo_Database

This class encapsulates the functionality of the Mongo connection and the MongoDb class into one since in most cases an application will interface with only one database. Interfacing with more than one database is still possible, but they will not share a single connection. Features afforded by this class:

  • Configurations are described with names such as ‘default’. Kohana loads this configuration in the typical Kohana fashion, other frameworks can simply specify it as an argument to Mongo_Database::instance('default',array(...)) to set the default configuration.
  • Database names are never used in your code other than when supplying the configuration the first time. Easily deploy the same application on different database names.
  • When used with Kohana, all important database operations can easily have profiling enabled. Profiling output mimics Mongo shell syntax for easy copy/paste debugging in the shell.
  • Lazy database connections.

Mongo_Collection

This class accomplishes the following:

  • Allows query results to be accessed as an iterator of models rather than arrays when used in conjunction with Mongo_Document.
  • Combines the functionality of MongoCollection, MongoGridFS and MongoCursor into one base class.
  • Allows query building by aggregating query parameters, cursor options, requested fields, etc..
  • When used with Kohana, all database requests can easily be profiled.
  • Debugging can be made easier by using the __toString() method to get a string representing the full query, again mimicking the syntax of the MongoDb shell.

Mongo_Document

This class objectifies a document (or row) in the database. Extend the base class for each of your data models, typically one per collection and enjoy some very simple CRUD operations with niceties like:

  • before_* and after_* methods for you to implement for transparently added functionality such as updating timestamps, validation, etc..
  • helpers for all of the Mongo update operations which are aggregated until the next save()
  • aliased field names so you can use short field names in the database and long names in the code. Take it or leave it.
  • auto-loading of referenced documents. E.g. echo $post->user->email
  • lazy-loading of documents and referenced documents. E.g. the above is two requests, this would only be one: echo $post->user->id

See the wiki for more detail.

More Information

@copyright Copyright © 2013 Colin Mollenhour (http://colin.mollenhour.com) This project is licensed under the “New BSD” license (see LICENSE.txt).

Contributors

  • @panrafal
  • @sergeyklay
  • @Xobb
  • @sebicas
  • @kanema
  • @semalead
  • @bbedwell
  • @kohenkatz

mongodb-php-odm's People

Contributors

bbedwell avatar colinmollenhour avatar kanema avatar kohenkatz avatar lucasmichot avatar panrafal avatar paulchubatyy avatar sebicas avatar sergeyklay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mongodb-php-odm's Issues

Remote MongoDB Configuration

Great work on the MongoDB Module!! It's way superior that Mango! Thanks!

Just one sugestion:

I been trying all day to configure the module to work with a remote MongoHQ instance, but I couldn't do it. And have to leave it running in localhost

I would be nice if the in the provided config file you could also include:

  • server ( and port )
  • username
  • password

Since I tried with:

return array(
'mongotest' => array(
'database' => 'db',
'server' => 'localhost',
'username' => 'myusername',
'password' => 'mypassword',
'profiling' => FALSE
)
);

And it didn't work... so if we can have a template it would be great...

I am new with Mongo, so please apologies if my issue is kind of silly!

Thanks,

@sebicas

mongo.cmd INI settings

Class Mongo_Collection should rely on mongo.cmd INI settings instead of $ (lines 238 to 251) :

We should have :
ini_get('mongo.cmd') instead of '$'
ini_get('mongo.cmd') . 'where' instead of '$where'
ini_get('mongo.cmd') . 'or' instead of '$or'

How to ensureindex for custom keys?

Hey Colin, thanks for the great module. Been testing it so far and was about to transfer my mysql db over. Sorta got stumped at the ensureIndex() part. How do I implement this for custom document keys? Also, is there any way to access the native driver methods while using your module? Like batchInsert() etc. ?

Capped collections

Hi!

How can I get new collection by Mongo_Database::__get($name) with custom options?

Obviously, when creating the collection we can point explicitly:

$collection = $db->command_safe(
  array(
    "create" => $collection_name
    "size"   => $collection_size,
    "capped" => $capped,
    "max"    => $max_size
  )
);

Based on the official documentation of the parameters have the following meaning:

capped

If the collection should be a fixed size;

size

If the collection is fixed size, its size in bytes;

max

If the collection is fixed size, the maximum number of elements to store in
the collection;

autoIndexId

If capped is TRUE you can specify FALSE to disable the automatic index
created on the _id field. Before MongoDB 2.2, the default value for
autoIndexId was false;

But I frankly do not yet quite understand how to use Mongo_Database::__get($name) to get (create) the new collection with the specified parameters where the object is an instance of the Mongo_Colleaction class...

Best regards,

having problem searching through id

I have been trying to implement query like this: db.posts.find({"_id" : ObjectId("4cb21a15a2749efc83ff831e")}); and I haven't had success. I have tried:

  1. $loaded_posts = $posts->find('{"_id" : ObjectId("4cb21a15a2749efc83ff831e")}');
  2. $loaded_posts = $posts->find('{"_id" : "4cb21a15a2749efc83ff831e"}');
    First one creates exception collection.php "Unable to parse query from JSON string"
    Second one makes Apache eat all of my memory so there might be an endless loop somewhere.

When I create query in database - it works. When I try it in model based on yours it doesn't work. This happens only with native _id and not with other elements of document when I use "find" or "findone"...

Segmentation fault when accessing collections?

I am not a 100% on when this error occurs, but it happens when I am trying to iterate collections.

I have the following code:
<?php

require_once 'modules/cms-mongo/classes/json.php';
require_once 'modules/cms-mongo/classes/mongo/database.php';
require_once 'modules/cms-mongo/classes/mongo/collection.php';
require_once 'modules/cms-mongo/classes/mongo/document.php';

class Model_Doctype extends Mongo_Document 
{
    protected $name = 'doctype';
}

class Model_Form extends Mongo_Document 
{
    protected $name = 'form';
}

/*
 * test code
 */
$db = Mongo_Database::instance('default', Array('database' => 'db'));

$forms = Mongo_Document::factory('Form')->collection();
$doctypes = Mongo_Document::factory('Doctype')->collection();

foreach ($forms as $form)
{
  echo $form->name."\n";
}
foreach ($doctypes as $doctype)
{
  echo $doctype->title."\n";
}

The db looks like this:

> db.form.find()
{"_id" :  ObjectId( "4bd4f06c8ead0eec61000000")  , "name" : "FAQ" , "fields" : [{"name" : "Question" , "type" : "text_area" , "sort" : "1" , "height" : "100" , "width" : "100"},{"name" : "Answer" , "type" : "text_area" , "sort" : "2" , "height" : "100" , "width" : "100"}]}
{"_id" :  ObjectId( "4bd4f1338ead0eeb61000000")  , "name" : "FAQ Rating" , "fields" : [{"name" : "Rating" , "type" : "text_area" , "sort" : "2" , "height" : "100" , "width" : "100"},{"name" : "FAQ" , "type" : "form" , "sort" : "1" , "form_id" : "4bd4f06c8ead0eec61000000"}]}
> db.doctype.find()
{"_id" :  ObjectId( "4bd742fa8ead0e7d13000000")  , "title" : "Document" , "form" : "4bd4f06c8ead0eec61000000" , "time" : 1272398586}
{"_id" :  ObjectId( "4bd7449c8ead0ee014000000")  , "title" : "Document 2" , "form" : "4bd4f1338ead0eeb61000000" , "time" : 1272399004}

The output I get is:

FAQ
FAQ Rating
Document
Document 2
Segmentation fault

atomic operations don't run when document is loaded

Hi, I have document schema like this :

array(
     [_id] => 508ccae5b90fb894090002ea,
     [subscribers] => array(
          {_id}, {_id}, {_id}
     )
)

I am trying to limit subscribers to a maximum of 5 items. When I perform a $pull on the document when it is not loaded, it works fine.

However, in order to check the count of subscribers, I have to load the document. I run count($this->subscribers) and use $addToSet or $pop accordingly.

At this point, all $_operations don't get saved when i call on save()

All $_operations work fine when the document is not loaded.

I had a look at save(). It seems to run all $_operations after possible inserts or updates. There are no DB errors but operations fail silently.

Could this be a bug? Or am I doing something wrong. Any help is kindly appreciated.

Here's some watered down code I am using.

        // fetch the concerned object
        $object = Mongo_Document::factory($object_type, $object_id);


        // update the latest interactions in the object
        if $inc === 1)
        {
            // sadly can't be atomic, object get loaded here.
            $object->load();
            $obj_subscriber_counts = count($object->subscribers);

            // only saving the latest five interactions
            if ($obj_subscriber_counts === 5)
            {
                // if 5, then remove the last entry
                $object->pop('subscribers'); // never got this running. Never reached 5 :)
            }
            // add it to the set
            $object->addToSet('subscribers', ['_id' => $user_id]);
        }
        else
        {
            // just remove the interaction.
            $object->pull('subscribers', ['_id' => $user_id]);
        }

        // increment the count for this interaction
        $object->inc('counts.subscribers', $inc); // Not updated when object is loaded


        $object->save(); // Object not saved here unless a pull was initiated

I also tried this without performing any count(), hence, not loading the document.
All $_operations worked fine. $addToSet and $pull worked perfectly.
But that will mean the embedded document can grow exponentially... :(

Go ahead

  1. It seems it is time to use PHP 5.4 :) Short syntax, traits, etc.
  2. What do you think about the PSR as a whole?

Lazy Loading and Updates?

Hi,

I think I've found an issue with lazy loading. I have the following code:

#!/usr/bin/php
<?

require_once 'mongodb-php-odm/classes/json.php';
require_once 'mongodb-php-odm/classes/mongo/database.php';
require_once 'mongodb-php-odm/classes/mongo/collection.php';
require_once 'mongodb-php-odm/classes/mongo/document.php';

/*
 * Extension classes
 */

class Model_Example_Collection extends Mongo_Collection
{
    protected $name = 'examples';
    protected $db   = 'exampledb';
}

class Model_Example extends Mongo_Document
{
    protected function before_save($state)
    {
        error_log("**** At start of before_save");
        var_dump($this->_changed);

        $this->modifiedTime = time();

        error_log("**** After adding modifiedTime");
        var_dump($this->_changed);

        if(null == $this->createTime)
        {

            $this->createTime = time();
        }

        error_log("**** After checking createTime");
        var_dump($this->_changed);
    }
}

/*
 * test code
 */
$db = Mongo_Database::instance('exampledb', Array('database' => 'exampledb'));

// create a new object
error_log("*** Creating");
$newObj = new Model_Example();
$newObj->exampleName = "FooBar";
$newObj->save();
$objId = $newObj->id;
$newObj = null;

// reload it
error_log("*** Reloading with lazy load");
$reloadedObj = new Model_Example($objId);
$reloadedObj->exampleName = "Modified!";
$reloadedObj->save();
$reloadedObj = null;

// reload it
error_log("*** Reloading with forced load");
$reloadedObj = new Model_Example($objId);
$reloadedObj->load();
$reloadedObj->exampleName = "Modified! Forced Load!";
$reloadedObj->save();

And I get the following output:

*** Creating
**** At start of before_save
array(1) {
  ["exampleName"]=>
  bool(true)
}
**** After adding modifiedTime
array(2) {
  ["exampleName"]=>
  bool(true)
  ["modifiedTime"]=>
  bool(true)
}
**** After checking createTime
array(3) {
  ["exampleName"]=>
  bool(true)
  ["modifiedTime"]=>
  bool(true)
  ["createTime"]=>
  bool(true)
}
*** Reloading with lazy load
**** At start of before_save
array(1) {
  ["exampleName"]=>
  bool(true)
}
**** After adding modifiedTime
array(2) {
  ["exampleName"]=>
  bool(true)
  ["modifiedTime"]=>
  bool(true)
}
**** After checking createTime
array(0) {
}
*** Reloading with forced load
**** At start of before_save
array(1) {
  ["exampleName"]=>
  bool(true)
}
**** After adding modifiedTime
array(1) {
  ["exampleName"]=>
  bool(true)
}
**** After checking createTime
array(1) {
  ["exampleName"]=>
  bool(true)
}

So to recap:

  • if I call ->load() on an object and do an update, everything works
  • if I don't call ->load(), but check for the presence of a value in a before_save() function, all of my changes get wiped out

Thanks!

Namespace the project

Hi!

Namespacing the project is a good idea, so that it is easy to plug in as a module to various PSR-0 compatible frameworks.

I currently use it in FuelPHP (since the built-in one in Fuel sucks), but I would like to get updates via a simple pull instead. The file structure is perfect for cloning it into a fuelphp package folder.

//MO

About Mongo_Document::factory

Hi!

I'm not sure, maybe I do not understand something or something has overlooked ...
Can somebody explain to me what the role self::$models here:

  /**
   * Instantiate an object conforming to Mongo_Document conventions.
   * The document is not loaded until load() is called.
   *
   * @param   string  $name
   * @param   mixed   $load
   * @return  Mongo_Document
   */
  public static function factory($name, $load = NULL)
  {
    if (isset(self::$models[$name]))
    {
      $class = self::$models[$name];
    }
    else if (strpos($name, '\\') !== false)
    {
      $class = $name;
    }
    else
    {
      $class = 'Model_' . implode('_', array_map('ucfirst', explode('_', $name)));
    }
    return new $class($load);
  }

I'm not found any place where this may be reading. It is evident that it is only written.
Excuse me if I say nonsense, I'm just a little confused here...

No way to map embedded document to PHP class.

If you make an association using the $_references field, that object is automatically mapped using a "foreign key", and lazy loaded. This is cool, but there are many times that I want to be able to embed the object, rather than reference it, and still be able to define and manipulate that object using a PHP class.

I think that one of the benefits of using a NoSQL db like MongoDB is that embedding documents is expected, and can make things easier.

If this is possible, can you please post a quick example?

EDIT::
To further clarify, given a blog post, which would most likely have an array of comments embedded in the blog document. I would want to define the fields of a comment in a class called Comment. Each entry in the comments array would then be mapped to a Comment object, even though it's embedded.

Method Naming Conventions

We have methods like:
command_safe
get_auto_increment
set_collection_class

but we also have methods like:
getName
getGridFS
selectCollection

We should stick with one way to name the methods. I think we should use under_score naming, not camelCase naming since that's how it's done in Kohana.

Sort issue

It seems this code

$posts
    ->reset()
    ->sort_desc('published')
    ->skip(0)
    ->limit(30)
    ->as_array();

works the same way

$posts
    ->reset()
    ->sort_asc('published')
    ->skip(0)
    ->limit(30)
    ->as_array();

and works the same way

$posts
    ->reset()
    ->skip(0)
    ->limit(30)
    ->as_array();

Anything does not sorted.
Please could you check it, in order to I could determine what exactly is reason

A bit magic Mongo_Collection::collection

In current master branch PHPDoc of Mongo_Collection::collection says:

 /**
  * Get the corresponding MongoCollection instance
  *
  * @return MongoCollection
  */

But this is wrong. Let's look at it again:

public function collection()
{
    $name = "$this->db.$this->name.$this->gridFS";
    if( ! isset(self::$collections[$name]))
    {
        $selectMethod = ($this->gridFS ? 'getGridFS' : 'selectCollection');
        self::$collections[$name] = $this->db()->db()->$selectMethod($this->name);
    }

    return self::$collections[$name];
}

Note that the $this->db()->db() returns the \MongoDb instance directly. In this case:

  1. $this->db()->db()->gridFS($this->name); Return \MongoGridFS
  2. $this->db()->db()->selectCollection($this->name); Return \MongoCollection
  3. For gridFS need a prefix (by default 'fs'), rather than the collection name

It seems Mongo_Collection::collection method returns not what was expected from him. At least in some cases getGidFS started with the wrong params.

Also note: in some cases:

$name = "$this->db.$this->name.$this->gridFS";

is a

$name = null + null + false;

Best regards : )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.