创建多个模型

时间:2012-06-11 11:29:38

标签: zend-framework model datamapper domain-model

我正在使用Zend Framework并实现域模型。我有模型,Mappers和DbTables。

假设我们应该从数据库中获取多行或获取表单数据,我们应该创建多个模型并从数据库行或表单中填充这些模型。

我应该在Mapper中实现获取和创建模型,然后从Controller调用该方法吗?或者我应该在模型中实现它?

可以在Controller中初始化Mapper吗?

2 个答案:

答案 0 :(得分:1)

简短回答是YES!
如果您需要从数据库中获取任何内容,您几乎可以在某处使用映射器,因为理想情况下,域模型应该根本不了解数据库,或者确实存在映射器事件。
我确信有许多使用和访问域模型和映射器的策略和模式。我也相信每个人都会对如何最好地利用这些资源有不同的看法。最重要的是你必须使用你知道如何使用的东西,你可以在以后知道更多时重构。

仅作为示例,我将包括基本映射器和基本实体(域)模型。

<?php
/**
 * Base mapper model to build concrete data mappers around.
 * Includes identity map functionallity.
 */
abstract class My_Application_Model_Mapper
{ 
    protected $_tableGateway = NULL;
    protected $_map = array();
    /**
     * Will accept a DbTable model passed or will instantiate
     * a Zend_Db_Table_Abstract object from table name.
     *
     * @param Zend_Db_Table_Abstract $tableGateway
     */
    public function __construct(Zend_Db_Table_Abstract $tableGateway = NULL) {
        if (is_null($tableGateway)) {

            $this->_tableGateway = new Zend_Db_Table($this->_tableName);
        } else {

            $this->_tableGateway = $tableGateway;
        }
    }
    /**
     * @return Zend_Db_Table_Abstract
     */
    protected function _getGateway() {

        return $this->_tableGateway;
    }
    /**
     * @param string $id
     * @param object $entity
     */
    protected function _setMap($id, $entity) {
        $this->_map[$id] = $entity;
    }
    /**
     * @param string $id
     * @return string
     */
    protected function _getMap($id) {
        if (array_key_exists($id, $this->_map)) {
            return $this->_map[$id];
        }
    }
    /**
     * findByColumn() returns an array of rows selected
     * by column name and column value.
     * Optional orderBy value.
     *
     * @param string $column
     * @param string $value
     * @param string $order
     * @return array
     */
    public function findByColumn($column, $value, $order = NULL) {
        $select = $this->_getGateway()->select();
        $select->where("$column = ?", $value);
        if (!is_null($order)) {
            $select->order($order);
        }
        $result = $this->_getGateway()->fetchAll($select);
        $entities = array();
        foreach ($result as $row) {
            $entity = $this->createEntity($row);
            $this->_setMap($row->id, $entity);
            $entities[] = $entity;
        }

        return $entities;
    }
    /**
     * findById() is proxy for find() method and returns
     * an entity object. Utilizes fetchRow() because it returns row object
     * instead of primary key as find() does.
     * @param string $id
     * @return object
     */
    public function findById($id) {
        //return identity map entry if present
        if ($this->_getMap($id)) {
            return $this->_getMap($id);
        }
        $select = $this->_getGateway()->select();
        $select->where('id = ?', $id);
        //result set, fetchRow returns a single row object
        $row = $this->_getGateway()->fetchRow($select);
        //create object
        $entity = $this->createEntity($row);
        //assign object to odentity map
        $this->_setMap($row->id, $entity);

        return $entity;
    }

    /**
     * findAll() is a proxy for the fetchAll() method and returns
     * an array of entity objects.
     * Optional Order parameter. Pass order as string ie. 'id ASC'
     * @param string $order
     * @return array
     */
    public function findAll($order = NULL) {

        $select = $this->_getGateway()->select();

        if (!is_null($order)) {
            $select->order($order);
        }
        $rowset = $this->_getGateway()->fetchAll($select);
        $entities = array();
        foreach ($rowset as $row) {
            $entity = $this->createEntity($row);
            $this->_setMap($row->id, $entity);
            $entities[] = $entity;
        }
        return $entities;
    }
    /**
     * Abstract method to be implemented by concrete mappers.
     */
    abstract protected function createEntity($row);
}

这是基本域对象:

<?php
/**
 * Base domain object
 * includes lazy loading of foreign key objects.
 */
abstract class My_Application_Model_Entity_Abstract
{
    protected $_references = array();

    /**
     * Accepts an array to instantiate the object, else use
     * __set() when creating objects
     * @param array $options
     */
    public function __construct(array $options = NULL) {

        if (is_array($options)) {
            $this->setOptions($options);
        }
    }
    /**
     * @param array $options
     * @return \My_Application_Model_Entity_Abstract
     */
    public function setOptions(array $options) {
        $methods = get_class_methods($this);
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }
    /**
     * Map the setting of non-existing fields to a mutator when
     * possible, otherwise use the matching field
     */
    public function __set($name, $value) {
        $property = '_' . strtolower($name);
        if (!property_exists($this, $property)) {
            throw new \InvalidArgumentException("Setting the property '$property'
                    is not valid for this entity");
        }
        $mutator = 'set' . ucfirst(strtolower($name));
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);
        } else {
            $this->$property = $value;
        }
        return $this;
    }
    /**
     * Map the getting of non-existing properties to an accessor when
     * possible, otherwise use the matching field
     */
    public function __get($name) {
        $property = '_' . strtolower($name);
        if (!property_exists($this, $property)) {
            throw new \InvalidArgumentException(
                    "Getting the property '$property' is not valid for this entity");
        }
        $accessor = 'get' . ucfirst(strtolower($name));
        return (method_exists($this, $accessor) && is_callable(array(
                    $this, $accessor))) ? $this->$accessor() : $this->$property;
    }
    /**
     * Get the entity fields.
     */
    public function toArray() {

        //TODO
    }
    /**
     * set and get for _references array, allows the potential to lazy load
     * foreign objects.
     */
    public function setReferenceId($name, $id) {

        $this->_references[$name] = $id;
    }
    public function getReferenceId($name) {
        if (isset($this->_references[$name])) {
            return $this->_references[$name];
        }
    }
}

我参考了许多教程和书籍,最终了解了这些概念和技巧 如果您需要从调用映射器的DB中提取对象,则需要使用这些对象,如果需要使用表单数据(或其他一些数据)构建对象,则可以直接调用该对象。

我希望这会有所帮助。祝你好运!

答案 1 :(得分:1)

我在所有ZF工作中使用相同的架构和设计模式。您的映射器应该是整个系统中访问数据库的唯一类。这确保了良好的Separation of Concerns

我在模型中使用薄包装方法玩弄了一些,例如:

class Application_Model_Foo {

    public function save() {
        $mapper = $this->_getMapper();
        $mapper->save($this);
    }

}

这使我可以调用类似的东西:

$foo = new Application_Model_Foo();
$foo->setBar('baz-bum');

$foo->save();

但是这使得测试变得更加复杂,并且当涉及到SoC时,水会变得混乱。最近我一直在从代码中删除这些事件,转而直接调用mapper,如:

$foo = new Application_Model_Foo();
$foo->setBar('baz-bum');

$mapper = new Application_Model_FooMapper();
$mapper->save($foo);

后一个例子意味着我的控制器方法中还有一行代码,但为了简化测试,我认为这是值得的。

相关问题