在CakePHP中使用查询生成器根据相同的端点关联选择行,但与它的关系不同

在CakePHP中使用查询生成器根据相同的端点关联选择行,但与它的关系不同,php,sql,cakephp,Php,Sql,Cakephp,我想做的是以不同的方式获取与user_id相关的所有行。 第一个条件是通过Resources表获取与用户相关的所有书籍,用户id存储在Resources表中,换句话说,用户拥有的书籍。第二个条件是通过存储在资源表中的城市模型再次获取与用户相关的所有书籍以及属于用户拥有的城市的书籍 我尝试了很多东西,但我无法使这两个条件起作用,因为我在同一最终模型用户上使用了JOIN,并尝试了innerJoinWith和leftJoinWith的不同组合 到目前为止我所做的: $userBooks = $this

我想做的是以不同的方式获取与user_id相关的所有行。 第一个条件是通过Resources表获取与用户相关的所有书籍,用户id存储在Resources表中,换句话说,用户拥有的书籍。第二个条件是通过存储在资源表中的城市模型再次获取与用户相关的所有书籍以及属于用户拥有的城市的书籍

我尝试了很多东西,但我无法使这两个条件起作用,因为我在同一最终模型用户上使用了JOIN,并尝试了innerJoinWith和leftJoinWith的不同组合

到目前为止我所做的:

$userBooks = $this->Books->find()
                ->leftJoinWith("Resources.Users")
                ->leftJoinWith("Cities.Resources.Users")
                ->where(["Resources.Users" => 1])
                ->orWhere(["Cities.Resources.Users" => 1])
                ->all();
这当然行不通,但我希望你明白我想要达到的目的。通过尝试不同的方法,我所能得到的最好结果是,只有一条符合逻辑的JOIN语句。 基本上,这可以分为两部分,给出预期的结果,但我不喜欢它,因为我想用一个查询完成它,当然:

$userBooks = $this->Books->find()
                ->innerJoinWith("Resources.Users", function($q) {
                    return $q->where(["Users.id" => 1]);
                })
                ->all();
$userBooks2 = $this->Books->find()
                ->innerJoinWith("Cities.Resources.Users", function($q) {
                    return $q->where(["Users.id" => 1]);
                })
                ->all();
此外,在此之前,我创建了一个SQL脚本,该脚本运行良好,结果如预期:

SELECT books.id FROM books, cities, users_resources WHERE 
        (users_resources.resource_id = books.resource_id AND users_resources.user_id = 1) 
        OR 
        (users_resources.resource_id = cities.resource_id AND books.city_id = cities.id AND users_resources.user_id = 1)
这个查询是有效的,我想将它转换为CakePHP中的ORM样式的查询,以获取用户拥有的书籍和通过Cities与用户连接的书籍。我希望以某种方式分离这些连接,以便像在SQL查询中那样单独过滤数据

编辑

我尝试了@ndm解决方案,但结果与只有一个关联用户的情况相同-我仍然能够基于一条join语句获取数据,第二条语句被忽略。由于我不得不继续前进,我最终与

    $userBooks = $this->Books->find()
        ->innerJoinWith("Cities.Resources.Users"‌​)
        ->where(["Users.id" => $userId])
        ->union($this->Books->find()
            ->innerJoinWith("Resour‌​ces.Users")
            ->where([‌​"Users.id" => $userId])
        )
        ->all(); 

它通过两个查询的并集输出正确的结果,但不是非常有效。我真的很想知道解决这个问题的最佳方法,因为这是一种非常常见的情况,由与其他模型关联的相关模型用户进行筛选。

ORM(特别是渴望加载程序)不允许多次加入同一别名

这可以通过多种方式解决,最简单的方式可能是使用唯一别名创建单独的关联。例如,在ResourcesTable中,创建另一个与具有不同别名的用户(如Users2)的关联,如:

然后可以在第二个leftJoinWith中使用该关联,并相应地应用条件:

$this->Books
    ->find()
    ->leftJoinWith('Resources.Users')
    ->leftJoinWith('Cities.Resources.Users2')
    ->where(['Users.id' => 1])
    ->orWhere(['Users2.id' => 1])
    ->group('Books.id')
    ->all();
别忘了把你的书分组,以避免重复的结果

您还可以使用leftJoin或join手动创建连接,您可以自己定义别名,或者根本不使用别名,这样就不会有冲突,但对于更复杂的查询,这可能是一项繁琐的任务

您还可以使用两个单独的查询作为书籍条件的子查询,甚至可以从它们创建联合查询,但这可能会执行得更糟

另见


谢谢你的回答,我不知道我是如何通过创建另一个关联来解决这个问题的,但我会尝试一下。然而,我首先想到的是:如果我有更深层的嵌套关系,我会需要每次创建新的assoc.Users3、Users4吗?对于更复杂的情况,它看起来不灵活,连接方法过于复杂化了。我来自Laravel框架,这里有一些方法,比如whereHas,它接受关系,闭包,允许过滤。CakePHP显然需要这样的解决方案。我尝试过您的解决方案,但结果与只有一个关联用户的情况相同-我仍然能够基于一个join语句获取数据,第二个语句被忽略。由于我不得不继续前进,我最终得到了$userBooks=$this->Books->find->innerJoinWithCities.Resources.Users->where[Users.id=>$userId]->union$this->Books->find->innerJoinWithResources.Users->where[Users.id=>$userId]->all;它通过两个查询的并集输出正确的结果,但不是非常有效。我真的很想知道实现这一点的最佳方法。我不知道你到底在做什么,我只能告诉你,使用如所示示例中的附加关联通常是有效的。CakePHP ORM为其匹配功能生成非常不同的SQL,即作为条件的联接而不是子查询,因此它需要唯一的别名。您也可以使用类似于Laravel的子查询,但必须手动构建它们。至于别名问题,您可能需要添加一个功能请求。@techquestions另请参见,该问题解决了您的问题。
$this->Books
    ->find()
    ->leftJoinWith('Resources.Users')
    ->leftJoinWith('Cities.Resources.Users2')
    ->where(['Users.id' => 1])
    ->orWhere(['Users2.id' => 1])
    ->group('Books.id')
    ->all();