I've found that you can make sense of things by breaking them down, one step at a time.
First, let's grab our desserts.
SELECT categories.cid
FROM categories
WHERE category_name = 'desserts'
Now let's grab all the recipes that are deserts by gluing them on the left side of our results. For every matching row in categories, there can be one or more matching rows in recipe_categories. For every matching row in recipe_categories, there can be one or more matching rows in recipes.
SELECT categories.cid, recipes.recipe_name
FROM categories
LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
WHERE category_name = 'desserts'
Finally, let's attach our ingredients on the left. For every matching row we have so far (we're on recipes now), we can have one or more matches in recipe_ingredients, and then again one or more matches in recipes.
SELECT recipes.recipe_name, ingredients.ingredient_name
FROM categories
LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
LEFT JOIN recipe_ingredients ON (recipe_ingredients.recipe_id = recipes.rid)
LEFT JOIN ingredients ON (ingredients.iid = recipe_ingredients.ingredient_id)
WHERE category_name = 'desserts'
With me so far?
Now, it's late, and I haven't tested this, but it should return the data you're looking for. In fact, I may totally be using the wrong join type to get this done correctly. I'm sure someone in the comments will correct me if I'm wrong.
What this won't do is return the data formatted as you'd like. Let's make one last iteration of the query to add some id fields.
SELECT recipes.rid, recipes.recipe_name, ingredients.iid, ingredients.ingredient_name
FROM categories
LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
LEFT JOIN recipe_ingredients ON (recipe_ingredients.recipe_id = recipes.rid)
LEFT JOIN ingredients ON (ingredients.iid = recipe_ingredients.ingredient_id)
WHERE category_name = 'desserts'
Let's gather the data into a multi-dimensional array. Let's pretend we're using PDO.
$query = '...';
$sh = $db->prepare($query);
$sh->execute();
$recipes = array();
while($row = $sh->fetch(PDO::FETCH_ASSOC)) {
// For each row in the result set, check to see if we've looked at this recipe.
if(!array_key_exists( $row['rid'], $recipes )) {
// If we haven't, let's initialize the row with the recipe name
// and a place to stick each ingredient.
$recipes[ $row['rid'] ] = array(
'recipe_name' => $row['recipe_name'],
'ingredients' => array()
);
}
// Place this ingredient with the proper recipe.
$recipes[ $row['rid'] ]['ingredients'][ $row['iid'] ] = $row['ingredient_name'];
}
The result should be something like:
array(
1 => array(
'recipe_name' => 'black forest cake',
'ingredients' => array(
'1' => 'self-raising flour',
'2' => 'milk',
...
)
)
)
From that structure, you can do what you want.