nap*_*lin 5 cakephp associations matching query-builder cakephp-3.1
我想实现食谱及其相关成分的搜索功能。用户应该指定他想要从搜索中排除的成分,同时指定他正在寻找的食谱中包含的成分。
这是我的两个发现者:
public function findByContainingIngredients(Query $query, array $params)
{
$ingredients = preg_replace('/\s+/', '', $params['containing_ingredients']);
if($ingredients) {
$ingredients = explode(',', $ingredients);
$query->distinct(['Recipes.id']);
$query->matching('Ingredients', function ($query) use($ingredients) {
return $query->where(function ($exp, $query) use($ingredients) {
return $exp->in('Ingredients.title', $ingredients);
});
});
}
return $query;
}
public function findByExcludingIngredients(Query $query, array $params)
{
$ingredients = preg_replace('/\s+/', '', $params['excluding_ingredients']);
if($ingredients) {
$ingredients = explode(',', $ingredients);
$query->distinct(['Recipes.id']);
$query->notMatching('Ingredients', function ($query) use ($ingredients) {
return $query->where(function ($exp, $query) use ($ingredients) {
return $exp->in('Ingredients.title', $ingredients);
});
});
}
return $query;
}
Run Code Online (Sandbox Code Playgroud)
在控制器中我调用:
$recipes = $this->Recipes->find()
->find('byExcludingIngredients', $this->request->data)
->find('byContainingIngredients', $this->request->data);
Run Code Online (Sandbox Code Playgroud)
如果用户从搜索中排除某种成分并指定他想要包括的一种或多种成分,则结果为零。当我查看生成的 SQL 时,我发现了问题:
SELECT
Recipes.id AS `Recipes__id`,
Recipes.title AS `Recipes__title`,
.....
FROM
recipes Recipes
INNER JOIN ingredients Ingredients ON (
Ingredients.title IN (: c0)
AND Ingredients.title IN (: c1)
AND Recipes.id = (Ingredients.recipe_id)
)
WHERE
(
Recipes.title like '%%'
AND (Ingredients.id) IS NULL
)
GROUP BY
Recipes.id,
Recipes.id
Run Code Online (Sandbox Code Playgroud)
问题是“AND (Ingredients.id) IS NULL”。这条线使得包含成分的结果消失。我的做法:
还有其他解决方案吗?
对于任何来到此页面并得出结论不能将matching()和组合notMatching()在同一关联类上的人:
是的,有可能(无论如何从 Cake 3.4.9 开始)进行这样的查找。但是您必须为目标表使用不同的别名- 即与通常的类名不同的别名。
所以在OP的情况下,你可以把它放进去RecipesTable.php:
public function initialize(array $config) {
... usual stuff
$this->belongsToMany('Ingredients', [
'foreignKey' => 'recipe_id',
'targetForeignKey' => 'ingredient_id',
'joinTable' => 'ingredients_recipes'
]);
// the next association uses an alias,
// but is otherwise *exactly* the same as the previous assoc.
$this->belongsToMany('ExcludedIngredients', [
'className' => 'Ingredients',
'foreignKey' => 'recipe_id',
'targetForeignKey' => 'ingredient_id',
'joinTable' => 'ingredients_recipes'
]);
}
Run Code Online (Sandbox Code Playgroud)
你应该能够编写这样的 find 语句:
$this->find()
-> ... usual stuff
->matching('Ingredients',function($q) use($okIngredients) {
... check for ingredients ...
})
->notMatching('ExcludedIngredients', function($q) use($excludedIngredients) {
... check for ingredients ...
});
Run Code Online (Sandbox Code Playgroud)
这确实有效。不幸的是,当我在“Recipes”表中有数千行的类似情况下使用它时,查询需要 40 秒才能运行。所以无论如何我都必须回去并用手工制作的连接来替换。notMatching()