CakePHP多个嵌套连接

时间:2010-03-25 14:14:14

标签: cakephp database-agnostic

我有一个应用程序,其中几个模型由hasMany / belongsTo关联链接。因此,例如,A hasMany B,B hasMany C,C hasMany D,D hasMany 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 )
)

当如果'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

1 个答案:

答案 0 :(得分:2)

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

  

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

条件语句只过滤D的结果,而不过滤链到C,然后是B,然后是A.如果你扫描SQL日志,你会看到大量的查询从你的{{1链。

为了让CakePHP直接从数据库中返回结果,你必须在A和E之间配置一个contain关联。如你所描述的那样,这可能是相当笨拙。它看起来像(读:未经测试):

hasOne

另一种方法是完全从$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选项如果这个操作需要扩展有多糟糕。它将整个过滤负担从数据库引擎转移到PHP的数组函数。