3

我有一个应用程序,其中几个模型由 hasMany/belongsTo 关联链接。例如,A 有很多 B,B 有很多 C,C 有很多 D,D 有很多 E。另外,E 属于 D,D 属于 C,C 属于 B,B 属于 A。使用 Containable 行为已经非常适合控制每个查询返回的信息量,但是在使用涉及表 D 的条件时尝试从表 A 获取数据时我似乎遇到了问题。例如,这是我的“A”的示例模型:

class A extends AppModel {
    var $name = 'A';

    var $hasMany = array(
        'B' => array('dependent' => true)
    );

    function findDependentOnE($condition) {
        return $this->find('all', array(
            'contain' => array(
                'B' => array(
                    'C' => array(
                        'D' => array(
                            'E' => array(
                                'conditions' => array(
                                    'E.myfield' => $some_value
                                )
                            )
                        )
                    )
                )
            )
        ));
    }
}

这仍然给了我'A'中的所有记录,如果它的相关'E'记录不满足条件,那么我就得到这个:

Array(
    [0] => array(
        [A] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [B] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [C] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [D] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [E] => array( 
            // empty if 'E.myfield' != $some_value'
        )
    ),
    [1] => array( // ...etc )
)

当 If 'E.myfield' != $some_value 时,我根本不希望返回记录。

我希望这足以清楚地表达我的问题......

基本上,我想要以下查询,但是以一种与数据库无关/CakePHP-y 的方式:

SELECT * 
FROM A INNER JOIN
        (B INNER JOIN 
            (C INNER JOIN 
                (D INNER JOIN 
                    E ON D.id=E.d_id) 
                ON C.id=D.c_id) 
            ON B.id=C.b_id) 
        ON A.id=B.a_id 
    WHERE E.myfield = $some_value
4

1 回答 1

2

您的问题是对 Containable 行为的作用以及contain选项在Model::find. 您的第一个代码示例中的Model::find调用将大致转换为:

找到所有 A;然后找到与每个 A 关联的所有 B;然后找到与每个 B 关联的所有 C;然后找到与每个 C 相关联的所有 D;最后,找到与每个 D 相关联的所有 E,其中 E 中的一个字段与指定值匹配。

条件语句只过滤 D 的结果,而不是向上到 C,然后是 B,然后是 A。如果您扫描 SQL 日志,您会看到大量的查询从contain链的每个级别中提取出来。

为了让 CakePHP 直接从数据库返回您想要的结果,您必须配置hasOneA 和 E 之间的关联。对于您描述的长链,这可能相当笨拙。它看起来像(阅读:未经测试):

$this->bindModel(array('hasOne'=>array(
    'B'=>array(
        'foreignKey' => false,
        'conditions' => array('A.id = B.a_id')
    ),
    'C'=>array(
        'foreignKey' => false,
        'conditions' => array('B.id = C.b_id')
    ),
    'D'=>array(
        'foreignKey' => false,
        'conditions' => array('C.id = D.c_id')
    ),
    'E'=>array(
        'foreignKey' => false,
        'conditions' => array('D.id = E.d_id')
    )
)));

$this->find('all', array(
    'conditions' => array( 'E.my_field' => $some_value )
));

另一种方法是从调用中完全删除E.my_value条件Model::find,并在最后执行相当复杂Set::extract的操作:

$results = $this->find('all', array(
    'contain' => array(
        'B' => array(
            'C' => array(
                'D' => array(
                    'E' => array()
                )
            )
        )
    )
));
return Set::extract("/A/B/C/D/E[my_field={$some_value}]/../../../../", $results);

但是,性能将是一个真正的问题Set::extract,尤其是当您在很多行上进行操作时。

编辑:我只想强调Set::extract如果此操作需要扩展,该选项的想法是多么糟糕。它将整个过滤负担从数据库引擎转移到 PHP 的数组函数上。

于 2010-03-25T16:11:32.520 回答