我正在使用 Zend 框架并实现域模型。我有模型、映射器和 DbTables。
假设我们应该从数据库中获取多行或获取表单数据,我们应该在其中创建多个模型并从数据库行或表单中填充这些模型。
我应该在 Mapper 中实现模型的获取和创建,然后从 Controller 调用该方法吗?或者我应该在模型中实现这个?
在Controller中初始化Mapper可以吗?
我正在使用 Zend 框架并实现域模型。我有模型、映射器和 DbTables。
假设我们应该从数据库中获取多行或获取表单数据,我们应该在其中创建多个模型并从数据库行或表单中填充这些模型。
我应该在 Mapper 中实现模型的获取和创建,然后从 Controller 调用该方法吗?或者我应该在模型中实现这个?
在Controller中初始化Mapper可以吗?
简短的回答是肯定的!
如果您需要从数据库中获取任何东西,您几乎可以在某处使用映射器,因为理想情况下,域模型根本不应该知道数据库,或者映射器确实存在。
我确信有许多使用和访问域模型和映射器的策略和模式。我也相信每个人都会对如何最好地利用这些资源有不同的看法。底线是你必须使用你知道如何使用的东西,当你知道更多时你总是可以重构。
仅作为示例,我将包含一个基本映射器和一个基本实体(域)模型。
<?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];
}
}
}
我参考了许多教程和书籍,最终了解了这些概念和技术。
这些对象的使用是需要的,如果你需要从数据库中拉出一个对象你调用映射器,如果你需要用表单数据(或其他一些数据)构建一个对象你可以直接调用该对象。
我希望这会有所帮助。祝你好运!
我在所有 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);
后一个示例意味着我的控制器方法中多了一行代码,但为了简化测试,我认为这是值得的。