我正在研究在Zend Framework中创建一个与数据访问层分开的域层。数据访问层由两个主要对象组成,即表数据网关和行数据网关。根据Bill Karwin对this earlier question的回复,我现在为我的域Person对象提供以下代码:
class Model_Row_Person
{
protected $_gateway;
public function __construct(Zend_Db_Table_Row $gateway)
{
$this->_gateway = $gateway;
}
public function login($userName, $password)
{
}
public function setPassword($password)
{
}
}
但是,这仅适用于单个行。我还需要创建一个可以表示整个表的域对象,并且(可能)可以用来遍历表中的所有Person并返回适当类型的人(管理员,买家等)对象以供使用。基本上,我想象如下:
class Model_Table_Person implements SeekableIterator, Countable, ArrayAccess
{
protected $_gateway;
public function __construct(Model_DbTable_Person $gateway)
{
$this->_gateway = $gateway;
}
public function current()
{
$current = $this->_gateway->fetchRow($this->_pointer);
return $this->_getUser($current);
}
private function _getUser(Zend_Db_Table_Row $current)
{
switch($current->userType)
{
case 'admin':
return new Model_Row_Administrator($current);
break;
case 'associate':
return new Model_Row_Associate($current);
break;
}
}
}
这是处理这个特定问题的好/坏方法吗?我应该对整体设计做出哪些改进或调整?
提前感谢您的评论和批评。
答案 0 :(得分:9)
我想到你会使用Domain Model类来完全隐藏你正在使用数据库表进行持久化的事实。因此,传递Table对象或Row对象应该完全在幕后:
<?php
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
$db = Zend_Db::factory('mysqli', array('dbname'=>'test',
'username'=>'root', 'password'=>'xxxx'));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
class Table_Person extends Zend_Db_Table_Abstract
{
protected $_name = 'person';
}
class Model_Person
{
/** @var Zend_Db_Table */
protected static $table = null;
/** @var Zend_Db_Table_Row */
protected $person;
public static function init() {
if (self::$table == null) {
self::$table = new Table_Person();
}
}
protected static function factory(Zend_Db_Table_Row $personRow) {
$personClass = 'Model_Person_' . ucfirst($personRow->person_type);
return new $personClass($personRow);
}
public static function get($id) {
self::init();
$personRow = self::$table->find($id)->current();
return self::factory($personRow);
}
public static function getCollection() {
self::init();
$personRowset = self::$table->fetchAll();
$personArray = array();
foreach ($personRowset as $person) {
$personArray[] = self::factory($person);
}
return $personArray;
}
// protected constructor can only be called from this class, e.g. factory()
protected function __construct(Zend_Db_Table_Row $personRow) {
$this->person = $personRow;
}
public function login($password) {
if ($this->person->password_hash ==
hash('sha256', $this->person->password_salt . $password)) {
return true;
} else {
return false;
}
}
public function setPassword($newPassword) {
$this->person->password_hash = hash('sha256',
$this->person->password_salt . $newPassword);
$this->person->save();
}
}
class Model_Person_Admin extends Model_Person { }
class Model_Person_Associate extends Model_Person { }
$person = Model_Person::get(1);
print "Got object of type ".get_class($person)."\n";
$person->setPassword('potrzebie');
$people = Model_Person::getCollection();
print "Got ".count($people)." people objects:\n";
foreach ($people as $i => $person) {
print "\t$i: ".get_class($person)."\n";
}
“我认为静态方法很糟糕 这就是我试图创造的原因 表级方法作为实例 方法“。
我不会接受任何一揽子声明static
总是坏的,或者单身总是坏的,或goto
总是坏的,或者你有什么。做出这种明确陈述的人正在寻求过度简化问题。适当使用语言工具,它们对你有好处。
也就是说,当你选择一种语言结构时,通常需要权衡,这使得做某些事情变得更容易,而做其他事情则更难。人们常常指出static
使得编写单元测试代码变得困难,而且PHP也存在一些与静态和子类相关的烦人缺陷。但正如我们在此代码中看到的那样,还有一些优点。你必须根据具体情况自行判断优势是否超过劣势。
“Zend Framework会支持Finder吗? 类?“
我认为没必要。
“你有什么特别的原因吗? 重命名了要进入的find方法 模型类?“
我将方法get()
命名为与find()
不同。 “getter”范例与OO接口相关联,而“finders”传统上与数据库内容相关联。我们正在尝试设计域模型以假装没有涉及数据库。
“你会继续使用 实现特定getBy的逻辑相同 和getCollectionBy方法?“
我反对创建一个通用的getBy()
方法,因为它很容易让它接受一个通用的SQL表达式,然后逐字传递给数据访问对象。这将我们的域模型的使用耦合到底层数据库表示。