【问题标题】:CakePHP Multiple Nested JoinsCakePHP 多重嵌套连接
【发布时间】:2011-01-31 18:03:53
【问题描述】:

我有一个应用程序,其中几个模型由 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

【问题讨论】:

    标签: cakephp database-agnostic


    【解决方案1】:

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

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

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

    为了让 CakePHP 直接从数据库返回你想要的结果,你必须在 A 和 E 之间配置一个 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 )
    ));
    

    另一种方法是从Model::find 调用中完全删除E.my_value 条件,而是在最后执行一个相当复杂的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 hasMany B,等等等等,我仍然需要保持这种关系正常工作。感谢您的帮助,我想我只能硬着头皮“手动”过滤记录...
    • 对于它的价值,如果绑定额外的 hasOne 关联,hasMany 关联应该可以继续正常工作。事实上,Model::bindModel 的默认行为是创建一个“一次性”绑定;关联将在下一个 Model::find() 操作后重置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多