以下是我个人实现模型的方式。我将使用一个真实的例子:我的“User”模型。
每当我创建一个模型时,我使用两个文件和两个类:该模型本身(例如“Application_Model_User”)和一个映射器对象(例如“Application_Model_UserMapper”)。模型本身显然包含数据、保存、删除、修改等方法。映射器对象包含获取模型对象、查找对象等方法。
这里是用户模型的前几行代码:
class Application_Model_User
对于每个属性,我都有一个getter和setter方法。例如对于id
:
public function getId() {
return $this->_id;
}
public function setId($value) {
$this->_id = (int) $value;
return $this;
}
我还使用了一些标准的"魔法方法"来公开暴露getter和setter(在每个模型的底部):
public function __set($name, $value) {
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
$this->$method($value);
}
public function __get($name) {
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
return $this->$method();
}
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;
}
示例的save
方法:
我在save()
方法内进行验证,在信息未通过验证时使用异常。
public function save() {
// Validate username
if (preg_match("/^[a-zA-Z](\w{6,15})$/", $this->_name) === 0) {
throw new Application_Exception_UserInfoInvalid();
}
// etc.
$db = Zend_Registry::get("db");
// Below, I would check if $this->_id is null. If it is, then we need to "insert" the data into the database. If it isn't, we need to "update" the data. Use $db->insert() or $db->update(). If $this->_id is null, I might also initialize some fields like 'created' or 'salt'.
}
对于mapper对象,我至少有两种方法:一种方法返回一个查询对象以选择对象,另一种方法执行查询,初始化并返回对象。我这样做是为了在控制器中操作查询以进行排序和过滤。
编辑
正如我在评论中所说,这篇文章http://weierophinney.net/matthew/archives/202-Model-Infrastructure.html启发了我当前的模型实现。
更多选项
您也可以使用Zend_Form
进行验证,而不是自己编写代码: http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html。我个人不喜欢这个选项,因为我认为Zend_Form
很难使用和精确控制。
当大多数人第一次学习Zend Framework时,他们学习如何子类化与
Zend_Db
相关的类。这里有一篇文章演示了这一点:
http://akrabat.com/zend-framework/on-models-in-a-zend-framework-application/
我提到我不喜欢这样做的原因如下:
- 很难创建包含派生/计算字段(即从其他表填充数据)的模型
- 我发现无法合并来自我的数据库的访问控制
- 我喜欢对我的模型有完全控制权
编辑2
对于您的第二个示例:您可以使用
Zend_Paginator。我提到,在您的包装器中,您可以创建一个方法,返回用于选择对象的数据库查询对象。这是我的简化但有效的用户映射程序:
class Application_Model_UserMapper {
public function generateSelect() {
$db = Zend_Registry::get("db");
$selectWhat = array(
"users_id",
"name",
"role",
"full_name",
"email",
"DATE_FORMAT(created, '%M %e, %Y at %l:%i:%s %p') as created",
"salt",
"passhash"
);
return $db->select()->from(array("u" => "users"), $selectWhat);
}
public function fetchFromSelect($select) {
$rows = $select->query()->fetchAll();
$results = array();
foreach ($rows as $row) {
$user = new Application_Model_User();
$user->setOptions(array(
"id" => $row["users_id"],
"name" => $row["name"],
"role" => $row["role"],
"fullName" => $row["full_name"],
"email" => $row["email"],
"created" => $row["created"],
"salt" => $row["salt"],
"passHash" => $row["passhash"]
));
$results[] = $user;
}
return $results;
}
}
为了处理分页器,我编写了一个自定义的Paginator插件,并将其保存到
library/Application/Paginator/Adapter/Users.php
。确保在application.ini中正确设置了
appnamespace
和
autoloaderNamespaces[]
。这是插件的代码:
class Application_Paginator_Adapter_Users extends Zend_Paginator_Adapter_DbSelect {
public function getItems($offset, $itemCountPerPage) {
// Simply inject the limit clause and return the result set
$this->_select->limit($itemCountPerPage, $offset);
$userMapper = new Application_Model_UserMapper();
return $userMapper->fetchFromSelect($this->_select);
}
}
在我的控制器中:
// Get the base select statement
$userMapper = new Application_Model_UserMapper();
$select = $userMapper->generateSelect();
// Create our custom paginator instance
$paginator = new Zend_Paginator(new Application_Paginator_Adapter_Users($select));
// Set the current page of results and per page count
$paginator->setCurrentPageNumber($this->_request->getParam("page"));
$paginator->setItemCountPerPage(25);
$this->view->usersPaginator = $paginator;
然后在您的视图脚本中呈现分页器。