0

我正在使用 Zend 框架并实现域模型。我有模型、映射器和 DbTables。

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

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

在Controller中初始化Mapper可以吗?

4

2 回答 2

1

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

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

<?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];
        }
    }
}

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

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

于 2012-06-12T04:28:57.070 回答
1

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

我在我的模型中使用了瘦包装方法,例如:

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 时使水变得混乱。最近我一直在从我的代码中删除此类事件,而只支持直接调用映射器,如下所示:

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

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

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

于 2012-06-12T05:00:51.397 回答