Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB - PHP - Factory method implementation

There are lots of articles regarding factory method implementation in PHP. I want to implement such a method for my MongoDB implementation in PHP.

I wrote the code something like below. Please Look at that code.

<?php
class Document {

    public $value = array();

    function __construct($doc = array()) {        
        $this->value = $doc;
    }

    /** User defined functions here **/
}

class Collection extends Document {
    //initialize database    
    function __construct() {            
        global $mongo;        
        $this->db = Collection::$DB_NAME;
    }

    //select collection in database
    public function changeCollection($name) {
        $this->collection = $this->db->selectCollection($name);
    }

    //user defined method
    public function findOne($query = array(), $projection = array()) {
        $doc = $this->collection->findOne($query, $projection);
        return isset($doc) ? new Document($doc) : false;
    }

    public function find($query = array(), $projection = array()) {
        $result = array();
        $cur = $this->collection->find($query, $projection);

        foreach($cur as $doc) {
            array_push($result, new Document($doc));
        }

        return $result;
    }

    /* Other user defined methods will go here */
}

/* Factory class for collection */

class CollectionFactory {
    private static $engine;

    private function __construct($name) {}    
    private function __destruct() {}
    private function __clone() {}

    public static function invokeMethod($collection, $name, $params) {
        static $initialized = false;

        if (!$initialized) {
            self::$engine = new Collection($collection);
            $initialized = true;
        }

        self::$engine->changeCollection($collection);

        return call_user_func_array(array(self::$engine, $name), $params);
    }
}

/* books collection */
class Books extends CollectionFactory {    
    public static function __callStatic($name, $params) {
        return parent::invokeMethod('books', $name, $params);
    }
}

/* authors collection */
class Authors extends CollectionFactory {    
    public static function __callStatic($name, $params) {
        return parent::invokeMethod('authors', $name, $params);
    }
}

/* How to use */

$books = Books::findOne(array('name' => 'Google'));
$authors = Authors::findOne(array('name' => 'John'));

Authors::update(array('name' => 'John'), array('name' => 'John White'));
Authors::remove(array('name' => 'John'));
?>

My questions are:-

  1. Is this correct PHP implementation of Factory method?
  2. Does this implementation have any issues?
  3. Are there any better methodologies over this for this scenario?

Thanks all for the answers.

like image 730
user10 Avatar asked Sep 29 '26 18:09

user10


1 Answers

  1. Hmm no, because with your piece of code you make ALL methods on the collection class available for a static call. That's not the purpose of the (abstract) factory pattern.
  2. (Magic) methods like __callStatic or call_user_func_array are very tricky because a developer can use it to call every method.
  3. What would you really like to do? Implement the factory pattern OR use static one-liner methods for your MongoDB implementation?!

If the implementation of the book and author collection has different methods(lets say getName() etc..) I recommend something like this:

class BookCollection extends Collection {
    protected $collection = 'book';

    public function getName() {
        return 'Book!';
    }
}

class AuthorCollection extends Collection {
    protected $collection = 'author';

    public function getName() {
        return 'Author!';
    }
}

class Collection {
    private $adapter = null;

    public function __construct() {
        $this->getAdapter()->selectCollection($this->collection);
    }
    public function findOne($query = array(), $projection = array()) {
        $doc = $this->getAdapter()->findOne($query, $projection);
        return isset($doc) ? new Document($doc) : false;
    }

    public function getAdapter() {
        // some get/set dep.injection for mongo
        if(isset($this->adapter)) {
            return $this->adapter;
        }
        return new Mongo();
    }
}

class CollectionFactory {
    public static function build($collection) 
    {
        switch($collection) {
            case 'book':
                return new BookCollection();
                break;
            case 'author':
                return new AuthorCollection(); 
                break;
        }
        // or use reflection magic
    }
}

$bookCollection = CollectionFactory::build('book');
$bookCollection->findOne(array('name' => 'Google'));
print $bookCollection->getName(); // Book!

Edit: An example with static one-liner methods

class BookCollection extends Collection {
    protected static $name = 'book';
}

class AuthorCollection extends Collection {
    protected static $name = 'author';
}

class Collection {
    private static $adapter;

    public static function setAdapter($adapter) {
        self::$adapter = $adapter;
    }
    public static function getCollectionName() {
        $self = new static();
        return $self::$name;
    }

    public function findOne($query = array(), $projection = array()) {
        self::$adapter->selectCollection(self::getCollectionName());
        $doc = self::$adapter->findOne($query, $projection);
        return $doc;
    }
}

Collection::setAdapter(new Mongo()); //initiate mongo adapter (once)
BookCollection::findOne(array('name' => 'Google'));
AuthorCollection::findOne(array('name' => 'John'));
like image 79
Bas van Dorst Avatar answered Oct 01 '26 06:10

Bas van Dorst