You could order by category and then name (make sure you have an index on category, name) Your Query would be:
SELECT * FROM `workouts` ORDER BY category, name
Then when you iterate you check if the category changed and if, close the sublist and open a new one showing the category first.
Another, slightly cleaner Solution would be to iterate twice, using the first loop to sort in an associative array by category:
$sth = $dbh->prepare("SELECT * FROM workouts");
$sth->execute();
$workouts = array();
/* First Loop */
while(($result = $sth->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
$workouts[$result['category']][] = $result;
}
Now you can loop over $workouts, using the key as the Category.
foreach($workouts as $category => $workoutsByCategory) {
//display category and start subset
foreach($workoutsByCategory as $workout) {
//display workout
}
}
It would be a lot cleaner thou to use a second table which holds the category names and use the id of that table in your workouts table to connect. You can then get the results using join. You might want to look into Relational Database Design and Normalization
Example (not tested) Table Layouts:
table "workouts"
id | category_id | title | description | hashtags | video | seen | date_published
table "categories"
id | title
Then you can get the results using:
SELECT workouts.*, categories.title as category_title FROM workouts LEFT JOIN categories ON (category.id = workouts.category_id)
This will make it easier to change the name of a category amongst other advances ... but you'll read up on that i guess.