CakePHP 多重嵌套连接

发布于 2024-08-26 11:54:23 字数 2144 浏览 3 评论 0原文

我有一个应用程序,其中多个模型通过 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 )
)

When If 'E.myfield' != $some_value, I don根本不想返回记录。

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

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

I have an App in which several of the models are linked by hasMany/belongsTo associations. So for instance, A hasMany B, B hasMany C, C hasMany D, and D hasMany E. Also, E belongs to D, D belongs to C, C belongs to B, and B belongs to A. Using the Containable behavior has been great for controlling the amount of information comes back with each query, but I seem to be having a problem when trying to get data from table A while using a condition that involves table D. For instance, here is an example of my 'A' model:

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
                                )
                            )
                        )
                    )
                )
            )
        ));
    }
}

This still gives me back all the records in 'A', and if it's related 'E' records don't satisfy the condition, then I just get this:

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 )
)

When If 'E.myfield' != $some_value, I don't want the record returned at all.

I hope this expresses my problem clearly enough...

Basically, I want the following query, but in a database-agnostic/CakePHP-y kind of way:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苏佲洛 2024-09-02 11:54:23

您的问题是对 Containable 行为的作用以及 Model::findcontain 选项的作用的误解。第一个代码示例中的 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 的数组函数上。

Your issue is a misconception of what the Containable behaviour does and what the contain option does in Model::find. The Model::find call in your first code-sample would translate roughly to:

Find all A; then find all B associated with each A; then find all C associated with each B; then find all D associated with each C; finally, find all E associated with each D where one field in E matches a specified value.

The condition statement only filters results of D, not up the chain to C, then B, then A. If you scan the SQL log, you'll see an enormous number of queries pulling out each level of your contain chain.

In order to get CakePHP to return the results as you desire, straight from the database, you'd have to configure a hasOne association between A and E. With a long chain such as you describe, this might be fairly unwieldy. It'd look something like (read: untested):

$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 )
));

An alternative is to remove the E.my_value condition entirely from the Model::find call, and instead perform a fairly complex Set::extract at the end:

$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);

Performance would be a real issue with a deep Set::extract though, especially if you were operating on a lot of rows.

EDIT: I just want to emphasise how terrible an idea the Set::extract option is if this operation needs to scale. It shifts the entire filtering burden from the database engine onto PHP's array functions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文