MongoDB-PHP-工厂方法实现



有很多关于PHP中工厂方法实现的文章。我想在PHP中为我的MongoDB实现实现实现这样一个方法。

我写的代码如下。请看那个代码。

<?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'));
?>

我的问题是:-

  1. 这是工厂方法的正确PHP实现吗
  2. 这种实施是否存在任何问题
  3. 对于这种情况,有没有更好的方法

谢谢大家的回答。

  1. 嗯,不,因为通过您的代码段,您可以使集合类上的所有方法都可用于静态调用。这不是(抽象的)工厂模式的目的
  2. __callStaticcall_user_func_array这样的(魔术)方法非常棘手,因为开发人员可以使用它来调用每个方法
  3. 你真正想做什么?实现工厂模式或者使用静态的一行方法来实现MongoDB

如果图书和作者集合的实现有不同的方法(比如getName()等),我建议这样做:

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!

编辑:静态单行方法的示例

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'));

Collection扩展Document有意义吗?在我看来,Collection可能有Document,但不是Document。。。所以我想说这个代码看起来有点混乱。

此外,对于factory方法,您确实希望使用它来实例化DocumentCollection的不同具体子类。假设您只使用过一种类型的Collection,以便于交谈;那么您的工厂类只需要关注不同的CCD_ 11子类。

因此,您可能有一个Document类,它需要一个表示单个文档的原始array

class Document
{
    private $_aRawDoc;
    public function __construct(array $aRawDoc)
    {
        $this->_aRawDoc = $aRawDoc;
    }
    // Common Document methods here..
}

然后指定文档类型的专用子类

class Book extends Document
{
    // Specialized Book functions ...
}

对于工厂类,您需要一些东西,然后在从光标上读取原始结果时将其包装起来。PDO让我们开箱即用(例如,请参阅PDOStatement::fetchObject$className参数),但我们需要使用装饰器,因为PHP不允许我们对Mongo扩展有那么多兴趣。

class MongoCursorDecorator implements MongoCursorInterface, Iterator
{
    private $_sDocClass;         // Document class to be used
    private $_oCursor;           // Underlying MongoCursor instance
    private $_aDataObjects = []; // Concrete Document instances
    // Decorate the MongoCursor, so we can wrap the results
    public function __construct(MongoCursor $oCursor, $sDocClass)
    {
        $this->_oCursor   = $oCursor;
        $this->_sDocClass = $sDocClass;
    }
    // Delegate to most of the stock MongoCursor methods
    public function __call($sMethod, array $aParams)
    {
        return call_user_func_array([$this->_oCursor, $sMethod], $aParams);
    }
    // Wrap the raw results by our Document classes
    public function current()
    {
        $key = $this->key();
        if(!isset($this->_aDataObjects[$key]))
            $this->_aDataObjects[$key] =
                new $this->sDocClass(parent::current());
        return $this->_aDataObjects[$key];
    }
}

现在是一个如何查询mongo以查找给定作者的书籍的示例

$m          = new MongoClient();
$db         = $m->selectDB('test');
$collection = new MongoCollection($db, 'book');
// search for author
$bookQuery = array('Author' => 'JR Tolken');
$cursor    = $collection->find($bookQuery);
// Wrap the native cursor by our Decorator
$cursor = new MongoCursorDecorator($cursor, 'Book');
foreach ($cursor as $doc) {
    var_dump($doc); // This will now be an instance of Book
}

你可以用MongoCollection子类把它收紧一点,无论如何你也可以拥有它,因为你也希望findOne方法修饰那些原始结果。

class MongoDocCollection extends MongoCollection
{
    public function find(array $query=[], array $fields=[])
    {
        // The Document class name is based on the collection name
        $sDocClass = ucfirst($this->getName());
        $cursor = parent::find($query, $fields);
        $cursor = new MongoCursorDecorator($cursor, $sDocClass);
        return $cursor;
    }
    public function findOne(
        array $query=[], array $fields=[], array $options=[]
    ) {
        $sDocClass = ucfirst($this->getName());
        return new $sDocClass(parent::findOne($query, $fields, $options));
    }
}

然后我们的样品使用变成

$m          = new MongoClient();
$db         = $m->selectDB('test');
$collection = new MongoDocCollection($db, 'book');
// search for author
$bookQuery = array('Author' => 'JR Tolken');
$cursor    = $collection->find($bookQuery);
foreach($cursor as $doc) {
    var_dump($doc); // This will now be an instance of Book
}

最新更新