【问题标题】:Zend Framework - ORM relationships and optimizationZend Framework - ORM 关系和优化
【发布时间】:2012-04-03 18:31:18
【问题描述】:

我已经使用 ZF 几个月了,我对它非常满意,但是我不完全确定如何处理模型关系,同时避免对数据库进行多次查询。很多人都有这个问题,似乎没有人找到一个好的解决方案。 (并避免使用第三方 ORM)例如我有一个用户列表,每个用户都属于一个组。我想要一个显示用户信息和组名的用户列表(对于表:用户和组。用户具有表组的外键)。 我有: 2 个映射器类来处理这些表,UserMapper 和 GroupMapper。 2 模型类用户和组 2 扩展 Zend_DB_Table_Abstract 的数据源类

在用户映射器中,我可以执行 findParentRow 以获取每个用户的组信息,但问题是我对每一行都有一个额外的查询,这不好我认为在加入时我只能在一。当然,现在我们必须将该结果映射到一个对象。所以在我的抽象 Mapper 类中,我尝试使用列别名(类似于 Yii 所做的......我认为)急切地加载每个父行的连接表,所以我在一个查询中得到一个像这样的值对象 //用户模型对象

$userMapper= new UserMapper();
$users= $userMapper->fetchAll(); //Array of user objects
echo $user->id;
echo $user->getGroup()->name // $user->getParentModel('group')->name // this info is already in the object so no extra query is required.

我想你明白我的意思...是否有一个本地解决方案,可能比我的更学术,以便在不避免多次查询的情况下做到这一点? // Zend db 表执行额外的查询以获取可以缓存的元数据。我的问题是为了获取父行信息......就像在 yii.... 那样的东西 $userModel->with('group')->fetchAll(); 谢谢你。

【问题讨论】:

  • 我在同样的问题上苦苦挣扎。我认为核心问题是Zend_Db_Table 是一个TableDataGateway (TDG) 实现。它足够灵活,可以处理链接表,但是如果没有使用连接的自定义查询,您最终会得到多个查询,正如您所确定的那样。正是这种 TDG 与我的实际需求之间的不匹配,经常将我推向像 Doctrine 这样的第三方 ORM。
  • 没错,但是 Doctrine 对于我通常使用的项目来说太大了。 ,所以我尽可能只依赖 Zend 类,并保持简单。我不知道ZF2中Zend_db和相关类的计划是什么,但我希望我们能有一些改进……我认为ZF2应该在今年夏天/秋天发布
  • ZF2 Zend\Db\Table API 在功能上可能与 ZF1 Zend_Db_Table 相同;毕竟,两者都是 TDG 实现。可悲的是,期望 ZF2 解决这种基本的 TDG/映射器/连接/用例紧张关系可能是不合理的。

标签: zend-framework model relationship zend-db-table mapper


【解决方案1】:

我通过子类化Zend_Db_Table_Rowset_AbstractZend_Db_Table_Row_Abstract 编写了一个解决方案。我将尝试简要总结一下,如果有人感兴趣,我可以对其进行扩展。

我创建了一个抽象模型类 - My_Db_Table_Row - 它包含一个子类行集的数组(以子类名为键)。

我创建了一个抽象 Rowset 类 - My_Db_Table_Rowset - 它根据列名从查询中提取数据并创建存储在 My_Db_Table_Row_children 中的行集。

My_Db_Table_Rowset 类使用来自Zend_Db_Table_Abstract_dependantTables_referenceMap 创建子实例(来自连接的列)并将它们添加到其父实例的_children 内的适当数组中(从'primary表的列)。

访问孩子的方式如下:$car->getDrivers();

public function getDrivers() {
    // allow for lazy loading
    if (!isset($this->_children['My_Model_Driver'])) {
        $this->_children['My_Model_Driver'] = My_Model_Driver::fetch........;
    }
    return $this->_children('My_Model_Driver');
}

最初,我将其编码为 2 个级别,父级和子级,但我正在扩展它以处理更多级别,例如祖父母-子女。

【讨论】:

    【解决方案2】:

    开发您的映射器以使用 Zend_Db_Select。这应该允许您需要的灵活性。组表是否加入取决于提供给映射器方法的参数,在本例中组对象是关键参数。

    class Model_User {
        //other fields id, username etc.
        //...
    
        /**
        * @var Model_Group
        */
        protected $_group;
    
        public function getGroup() {
            return $this->_group;
        }
    
        public function setGroup(Model_Group $group) {
            $this->_group = $group;
        }
    
    }
    
    class Model_Mapper_User {
    
        /**
        * User db select object, joins with group table if group model provided
        * @param Model_Group $group
        * @return Zend_Db_Select
        */
        public function getQuery(Model_Group $group = NULL) {
            $userTable = $this->getDbTable('user'); //mapper is provided with the user table
            $userTableName = $userTable->info(Zend_Db_Table::NAME); //needed for aliasing
            $adapter = $userTable->getAdapter();
    
            $select = $adapter->select()->from(array('u' => $userTableName));
    
            if (NULL !== $group) {
                //group model provided, include group in query
                $groupTable = $this->getDbTable('group');
                $groupTableName = $groupTable->info(Zend_Db_Table::NAME);
                $select->joinLeft(array('g' => $groupTableName), 
                                    'g.group_id = u.user_group_id');
            }
    
            return $select;
        }
    
        /**
        * Returns an array of users (user group optional)
        * @param Model_User $user
        * @param Model_Group $group
        * @return array
        */
        public function fetchAll(Model_User $user, Model_Group $group = NULL) {
            $select = $this->getQuery();
            $adapter = $select->getAdapter();
            $rows = $adapter->fetchAll($select);
    
            $users = array();
    
            if (NULL === $group) {
                foreach ($rows as $row) {
                    $users[] = $this->_populateUser($row, clone $user);
                }
            } else {
                foreach ($rows as $row) {
                    $newUser = $this->_populateUser($row, clone $user);
                    $newGroup = $this->_populateGroup($row, clone $group);
    
                    //marrying user and group
                    $newUser->setGroup($newGroup);
    
                    $users[] = $newUser;
                }
            }
    
            return $users;
        }
    
        /**
        * Populating user object with data
        */
        protected function _populateUser($row, Model_User $user) {
            //setting fields like id, username etc
            $user->setId($row['user_id']);
            return $user;
        }
    
        /**
        * Populating group object with data
        */
        protected function _populateGroup($row, Model_Group $group) {
            //setting fields like id, name etc
            $group->setId($row['group_id']);
            $group->setName($row['group_name']);
            return $group;
        }
    
        /**
        * This method also fits nicely
        * @param int $id
        * @param Model_User $user
        * @param Model_Group $group 
        */
        public function fetchById($id, Model_User $user, Model_Group $group = NULL) {
            $select = $this->getQuery($group)->where('user_id = ?', $id);
            $adapter = $select->getAdapter();
            $row = $adapter->fetchRow($select);
    
            $this->_populateUser($row, $user);
            if (NULL !== $group) {
                $this->_populateGroup($row, $group);
                $user->setGroup($group);
            }
    
            return $user;
        }
    
    }
    

    使用场景

    /**
     * This method needs users with their group names 
     */
    public function indexAction() {
        $userFactory = new Model_Factory_User();
        $groupFactory = new Model_Factory_Group();
        $userMapper = $userFactory->createMapper();
        $users = $userMapper->fetchAll($userFactory->createUser(), 
                                            $groupFactory->createGroup());
    }
    
    /**
     * This method needs no user group
     */
    public function otherAction() {
        $userFactory = new Model_Factory_User();
        $userMapper = $userFactory->createMapper();
        $users = $userMapper->fetchAll($userFactory->createUser());
    }
    

    干杯

    【讨论】:

    • 您好,感谢您的回答。我想我正在做你描述的事情。我的映射器从一个抽象类扩展,该类具有通用方法,例如 fetch all,在抽象类中,我传递了扩展 zend db table abstract 的数据表源的名称,因此我可以使用_referenceMap 信息。有了这个和我打算映射的模型类的名称,我可以使用别名在一个连接中进行映射。它适用于我的目的(自定义和简单的 CMS)...
    • ...但是当我写这篇文章时,我有点问自己是否可以将这个功能添加到框架中。 (将对象映射到相关表)但我认为这也是 ZF 哲学的一部分“模型/映射器”是你的工作;)
    • 有人编写了一个由每个模型的 $_referenceMap 提供的抽象类,这样我们就不必为 Zend 写那么多了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-13
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    相关资源
    最近更新 更多