Please advise on a better formulation of the question.
Basis
I have SQL tables R=recipes, I=ingredients. (minimum example):
R (id)
RI (id, r_id, i_id)
I (id, description)
where RI is intermediate table connecting R and I (otherwise inm,n
-relationship).
You may skip this
In HTML I have an input for filtering recipes by ingredients. Input is sent to PHP with JavaScript's Fetch API (body: JSON.stringify({ input: filter.value.trim() })
).
In PHP I clean it up and explode it into array of words so %&/(Oh/$#/?Danny;:¤ boy!
gets converted to ['Oh', 'Danny', 'boy']
$filterParams = preg_replace('/[_\W]/', ' ', $data['input']);
$filterParams = preg_replace('/\s\s+/', ' ', $filterParams);
$filterParams = trim($filterParams);
$filterParams = explode(' ', $filterParams);
What I want
I need an SQL query for all recipe IDs that require all of the ingredients from the input. Consider these two recipes:
ID RECIPE INGREDIENTS
1 pancake egg, flour, milk
2 egg egg
Filtering for "eg, ilk" should only return 1 but not 2.
What I have #1
This gives me all recipes that require any of the ingredients, therefore it returns 1 and 2.
$recipeFilters = array_map(function ($param) {
return "ri.description LIKE '%{$param}%'";
}, $filterParams);
$recipeFilter = implode(' OR ', $recipeFilters);
$selRecipes = <<<SQL
SELECT DISTINCT rr.id
FROM
recipe_ingredient ri LEFT JOIN
recipe_intermediate_ingredient_recipe riir ON riir.ingredient_id = ri.id LEFT JOIN
recipe_recipe rr ON rr.id = riir.recipe_id
WHERE
{$recipeFilter} AND
rr.id IS NOT NULL
SQL;
$recipes = data_select($selRecipes); // Custom function that prepares statement, binds data (not in this case), and eventually after all error checking returns $statement->get_result()->fetch_all(MYSQLI_ASSOC)
$ids = [];
foreach ($recipes as $recipe)
$ids[] = "{$recipe['id']}";
What I have #2
Replacing OR
with AND
in the fifth line returns neither 1 nor 2, because no ingredient has both eggs and milk (ie. eg, ilk) in it's name.
...
$recipeFilter = implode(' AND ', $recipeFilters);
...
Suboptimal solution
I know I can simply query for each ingredient separately and then with some simple array manipulations get what I desire.
Is it possible to do it in just one query, and how?