cakephp 构建查询以过滤模型或关联模型

cakephp build query to filter on model OR associated model

我想通过过滤 table 的列来查找行, 在关联的 table的专栏。我可以使用 AND 版本,但我不确定如何将 ->where()->matching() 之间的条件更改为 OR.

在此示例中,我想通过用户名或常规名称查找用户。在这种情况下,用户 hasMany 名称。

$users = $this->Users->find()
    ->where(['username LIKE' => '%' . $search_term . '%'])
    ->matching('Names', function($q) use ($search_term){
        return $q->where(function($exp, $query) use ($search_term) {
            $full_name = $query->func()->concat([
                'first' => 'identifier', ' ',
                'last' => 'identifier'
            ]);
            return $exp->like($full_name, '%' . $search_term . '%');
        });
    })
    ->contain(['Names'])
    ->limit(10)
    ->all();

matching() creates INNER joins,在联接的 ON 子句中应用条件,如果不存在匹配的名称,则会导致用户被过滤掉,因此您不能使用它来创建 OR您正在寻找的条件。

例如,您可以将您的姓名检查移动到相关子查询(引用外部查询列的查询),以及 use it with EXISTS,类似于:

$namesMatcher = $this->Users->Names
    ->find()
    ->select(['Names.id'])
    ->where(function($exp, $query) use ($search_term) {
        $full_name = $query->func()->concat([
            'Names.first' => 'identifier', ' ',
            'Names.last' => 'identifier'
        ]);

        return $exp
            ->equalFields('Names.user_id', 'Users.id')
            ->like($full_name, '%' . $search_term . '%');
    });

$users = $this->Users->find()
    ->where(function($exp, $query) use ($search_term, $namesMatcher) {
        return $exp
            ->or_(['Users.username LIKE' => '%' . $search_term . '%'])
            ->exists($namesMatcher);
    })
    ->contain(['Names'])
    ->limit(10)
    ->all();

生成的查询看起来像这样:

SELECT
    Users.id ...
FROM
    users Users
WHERE
    Users.username LIKE '%term%' OR
    EXISTS(
        SELECT
            Names.id
        FROM
            names Names
        WHERE
            Names.user_id = Users.id AND
            CONCAT(Names.first, ' ', Names.last) LIKE '%term%'
    )

这也可以通过使用 LEFT 联接来解决,这样用户记录就不会受到联接的影响(请参阅 Query::leftJoinWith()),从而允许您创建一个 OR 相应条件,但由于您似乎不需要进一步访问查询中的名称,因此选择它们并没有什么意义。