CakePHP多重嵌套联接

CakePHP多重嵌套联接,cakephp,database-agnostic,Cakephp,Database Agnostic,我有一个应用程序,其中几个模型通过hasMany/belongsTo关联进行链接。例如,A有许多B,B有许多C,C有许多D,D有许多E。同样,E属于D,D属于C,C属于B,B属于A。使用可包含行为对于控制每次查询返回的信息量非常有用,但在使用涉及表D的条件时,试图从表a获取数据时,我似乎遇到了问题。例如,下面是我的“a”模型的一个示例: class A extends AppModel { var $name = 'A'; var $hasMany = array(

我有一个应用程序,其中几个模型通过hasMany/belongsTo关联进行链接。例如,A有许多B,B有许多C,C有许多D,D有许多E。同样,E属于D,D属于C,C属于B,B属于A。使用可包含行为对于控制每次查询返回的信息量非常有用,但在使用涉及表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”!=$有些值,我根本不希望返回记录

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

基本上,我想要以下查询,但以一种与数据库无关的/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

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

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

condition语句只过滤D的结果,而不是从上到C,再到B,再到A的链。如果您扫描SQL日志,您将看到大量的查询从
包含链的每一级拉出

为了让CakePHP直接从数据库返回您想要的结果,您必须在a和E之间配置一个
hasOne
关联。使用您描述的长链,这可能相当麻烦。它看起来像(读:未测试):

另一种方法是从
Model::find
调用中完全删除
E.my_值
条件,并在末尾执行相当复杂的
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的数组函数上。

是的,我可能不会为Set::extract选项操心……而且我对这样设置hasOne关系有点谨慎,因为a有许多B,等等,我仍然不需要保持这种关系正常工作。谢谢你的帮助,我想我必须咬紧牙关,用“手”过滤记录……不管它值多少钱,如果你绑定额外的hasOne协会,hasMany协会应该可以继续工作,没有问题。实际上,Model::bindModel的默认行为是创建一个“一次性”绑定;关联将在下一个Model::find()操作后重置。
$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);