Yes, you are stuck doing loops. It's not a bad thing.
If you think about it, you can do this 2 ways:
You can do one query that retrieves all categories and subcategories and products:
SELECT category.name, subcategory.name, product.name
FROM product
INNER JOIN subcategory ON product.subcategoryID=subcategory.ID
INNER JOIN category ON
category.ID=subcategory.categoryID
ORDER BY category.name, subcategory.name, product.name
This would return like
Category 1 Subcategory 1 Product 1
Category 1 Subcategory 1 Product 2
Category 1 Subcategory 2 Product 1
Category 1 Subcategory 2 Product 2
Category 2 Subcategory 1 Product 1
Category 2 Subcategory 1 Product 2
Category 2 Subcategory 2 Product 1
etc.
Then loop through the returned rows, parsing them with PHP to create your navigation structure
OR you can do query on category, and for each returned row nest another query and return its subcategory, then query all products for each subcategory, etc.
There are other variations.
It would be nice if MySQL had a 'CONNECT BY' contstruct like Oracle does, that does these sorts of part-explosions more automatically -- but then it would be nice if Oracle were free, too.