matching()
создает INNER
объединений , с условиями, применяемыми в предложении объединения ON
, в результате чего пользователи отфильтровываются, если не найдено соответствующих имен, следовательно, вынельзя использовать это для создания искомого условия OR
.
Например, вы можете переместить проверку имен в коррелированный подзапрос (запрос, который ссылается на столбцы внешнего запроса), и используйте его с 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
, но, поскольку вам больше не нужен доступ к именам в запросе, на самом деле нет смысла выбирать их.