Je veux mettre en place une fonction de recherche pour les recettes et les ingrédients associés. L'utilisateur doit spécifier les ingrédients qu'il veut exclure de la recherche et en même temps les ingrédients qui sont contenus dans les recettes qu'il recherche.
Ce sont mes deux viseurs :
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;
}
Dans le contrôleur, j'appelle :
$recipes = $this->Recipes->find()
->find('byExcludingIngredients', $this->request->data)
->find('byContainingIngredients', $this->request->data);
Si l'utilisateur exclut un ingrédient de la recherche et spécifie un ou plusieurs ingrédients qu'il souhaite inclure, il n'obtient aucun résultat. Lorsque je regarde le SQL généré, je vois le problème :
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
Le problème est "AND (Ingredients.id) IS NULL". Cette ligne fait disparaître les résultats des ingrédients inclus. Mes approches :
- Création d'un alias lors de l'appel de notMatching() sur l'association deux fois. Je pense que ce n'est pas possible dans Cake3.1.
- Utiliser une jointure gauche sur le PK/FK et le titre exclu et créer un alias. En fait, j'ai écrit ma propre fonction de non-concordance. Cela fonctionne, mais cela ne semble pas correct.
Existe-t-il d'autres solutions ?