3

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

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

$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(); 谢谢你。

4

2 回答 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());
}

干杯

于 2012-03-20T15:26:25.930 回答
2

Zend_Db_Table_Rowset_Abstract我已经通过子类化和编写了一个解决方案Zend_Db_Table_Row_Abstract。我将尝试对其进行简要总结,如果任何人感兴趣,我可以对其进行扩展。

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

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

该类My_Db_Table_Rowset使用_dependantTablesand _referenceMapfromZend_Db_Table_Abstract创建子实例(从连接列)并将它们添加到_children其父实例(从“主表”列创建)内的适当数组中。

访问孩子的过程如下:$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 个级别,父级和子级,但我正在扩展它以处理更多级别,例如祖父母 - 父母 - 孩子。

于 2012-05-14T15:17:19.263 回答