在Magento之后学习Zend框架:模型

时间:2011-08-19 16:30:29

标签: php magento zend-framework zend-db zend-db-table

我和Magento一起工作了一年,并且学到了很多东西。现在我想学习Zend,而且我坚持使用模型。

我习惯在Magento中拥有实体和实体集合,我可能希望使用Zend_Db_TableZend_Db_Table_Row和/或Zend_Db_Table_Rowset。我感到困惑的是每个班级的角色。

我知道我可以扩展每个类,并且我理解在我的Product_Table类(扩展Zend_Db_Table_Abstract)中,可以使用私有方法告诉Zend哪些类用于行和但是我对它感觉不舒服。

在Magento中使用此代码:

示例1

// I understand that maybe I'll use the `new` keyword instead
// Mage::getModel() is only for exemplification
$product = Mage::getModel('catalog/product');
$product->setName('product name');
$product->setPrice(20);
$product->save();

if($id = $product->getId()){
    echo 'Product saved with id' . $id;
}
else{
    echo 'Error saving product';
}

示例2

$collection = Mage::getModel('catalog/product')->getCollection();
// this is the limit, I'm ok with other method's name
$collection->setPageSize(10);
$collection->load()

foreach($collection as $product){
    echo $product->getName() . ' costs ' . $product->getPrice() . PHP_EOL;
}

如何在Zend Framework中实现类似的功能?或者,如果这是一个真的一个坏主意,那么在Zend Framework中实现模型的最佳实践是什么?

由于

4 个答案:

答案 0 :(得分:5)

如其他地方所述,Zend团队对模型层的看法与大多数其他PHP框架创建者不同。他们目前关于使用原始工具提供数据库支持的实体模型的“最佳”方式的想法可以在quick start指南中找到。

也就是说,Zend Framework中大多数人对模型的解决方案都是引导Doctrine

答案 1 :(得分:4)

以下是我个人实施模型的方法。我将使用一个真实的例子:我的User模型。

每当我创建模型时,我都会使用两个文件和两个类:模型本身(例如Application_Model_User)和映射器对象(例如Application_Model_UserMapper)。模型本身显然包含数据,保存,删除,修改等方法。映射器对象包含获取模型对象,查找对象等的方法。

以下是User模型的前几行:

class Application_Model_User {

    protected $_id;
    protected $_name;
    protected $_passHash;
    protected $_role;
    protected $_fullName;
    protected $_email;
    protected $_created;
    protected $_salt;

    // End protected properties

对于每个属性,我都有一个getter和setter方法。 id的示例:

/* 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中正确设置appnamespaceautoloaderNamespaces[]。这是插件:

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;

然后在视图脚本中渲染分页器。

答案 2 :(得分:2)

我做了类似于SimpleCode的方式。我的风格来自Pádraic Brady。他有多篇博客文章,但他最好和最快的资源是他写的一本在线书:Survive the Deep End!。这个链接应该直接带你到他的章节有关模型,数据映射器和其他很酷的东西,如Lazy Loading。这个想法如下:

您有像User这样的实体,属性是在数组中定义的。所有实体都扩展了一个抽象类,其中包含来自或更新此数组的魔术getter / setter。

class User extends Entity
{
    protected $_data = array(
        'user_id' => 0,
        'first_name' => null,
        'last_name' => null
    );
}

class Car extends Entity
{
    protected $_data = array(
        'car_id' => 0,
        'make' => null,
        'model' => null
    );
}

class Entity
{
    public function __construct($data)
    {
        if(is_array($data))
        {
            $this->setOptions($data);
        }
    }

    public function __get($key)
    {
        if(array_key_exists($key, $this->_data)
        {
            return $this->_data[$key];
        }

        throw new Exception("Key {$key} not found.");
    }

    public function __set($key, $value)
    {
        if(array_key_exists($key, $this->_data))
        {
            $this->_data[$key] = $value;
        }

        throw new Exception("Key {$key} not found.");
    }

    public function setOptions($data)
    {
        if(is_array($data))
        {   
            foreach($data as $key => $value)
            {
                $this->__set($key, $value);
            }
        }
    }

    public function toArray()
    {
        return $this->_data;
    }
}

$user = new User();
$user->first_name = 'Joey';
$user->last_name = 'Rivera';

echo $user->first_name; // Joey

$car = new Car(array('make' => 'chevy', 'model' => 'corvette'));
echo $car->model; // corvette

我的数据映射器与实体是分开的,他们的工作是对数据库进行CRUD(创建,读取,更新和删除)。因此,如果我们需要从db加载实体,我会调用特定于该实体的映射器来加载它。例如:

<?php

class UserMapper
{
    $_db_table_name = 'UserTable';
    $_model_name = 'User';

    public function find($id)
    {
        // validate id first

        $table = new $this->_db_table_name();
        $rows = $table->find($id);

        // make sure you get data

        $row = $rows[0]; // pretty sure it returns a collection even if you search for one id
        $user = new $this->_model_name($row); // this works if the naming convention matches the user and db table
        //else
        $user = new $this->_model_name();

        foreach($row as $key => $value)
        {
            $user->$key = $value;
        }

        return $user;
    }
}

$mapper = new UserMapper();
$user = $mapper->find(1); // assuming the user in the previous example was id 1
echo $user->first_name; // Joey

此代码旨在介绍如何以这种方式构建代码。我没有测试这个,所以我可能在编写时创建了一些拼写错误/语法错误。就像其他人提到的那样,Zend可以让你用模特做你想做的事,没有对错,这完全取决于你。我通常为我想要使用的数据库中的每个表创建一个表类。因此,如果我有一个用户表,我通常有一个User实体,User Mapper和一个User Table类。 UserTable将扩展Zend_Db_Table_Abstract,并且根据我正在做的事情,内部没有任何方法,或者有时我会根据我的需要覆盖插入或删除等方法。我最终得到了很多文件,但我相信代码的分离使我更容易快速到达我需要添加更多功能或修复bug的地方,因为我知道代码的所有部分都在哪里。

希望这有帮助。

答案 3 :(得分:0)

文件夹结构

application
--models
----DbTable
------User.php
--controllers
----IndexController.php
--forms
----User.php
--views
----scripts
------index
--------index.phtml

应用/模型/ DBTABLE / user.php的

class Application_Model_DbTable_User extends Zend_Db_Table_Abstract
{
    protected $_name = 'users';
    protected $_primary = 'user_id';
}

应用/表格/ user.php的

class Form_User extends Zend_Form
{
    public function init()
    {       
        $this->setAction('')
            ->setMethod('post');

        $user_name = new Zend_Form_Element_Text('user_name');
        $user_name->setLabel("Name")->setRequired(true);

        $user_password = new Zend_Form_Element_Text('user_password');
        $user_password->setLabel("Password")->setRequired(true);

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Save');

        $this->addElements(array(
            $user_name,
            $user_password,
            $submit
        ));
    }
}

应用/控制器/ IndexController.php

class IndexController extends Zend_Controller_Action
{
    public function init()
    {

    }

    public function indexAction()
    {
        $form = new Form_User();
        if($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost()))
        {
            $post = $this->getRequest()->getPost();
            unlink($post['submit']);

            $ut = new Application_Model_DbTable_User();
            if($id = $ut->insert($post))
            {
                $this->view->message = "User added with id {$id}";
            } else {
                $this->view->message = "Sorry! Failed to add user";
            }
        }
        $this->view->form = $form;
    }
}

应用/视图/脚本/索引/ index.phtml

echo $this->message;
echo $this->form;