Il existe plusieurs solutions. Tout d'abord, je vais utiliser les données suivantes (categories
tableau) à titre d'exemple.
+----+--------------------------+-----------+
| id | name | parent_id |
+----+--------------------------+-----------+
| 1 | Electronics | NULL |
| 2 | Apparel & Clothing | NULL |
| 3 | Phones & Accessories | 1 |
| 4 | Computer & Office | 1 |
| 5 | Men's Clothing | 2 |
| 6 | Women's Clothing | 2 |
| 7 | Cell Phones | 3 |
| 8 | Cell Phone Accessories | 3 |
| 9 | Phone Parts | 3 |
| 10 | Computers & Accessories | 4 |
| 11 | Tablets & Accessories | 4 |
| 12 | Computer Peripherals | 4 |
| 13 | Computer Components | 4 |
| 14 | Office Electronics | 4 |
+----+--------------------------+-----------+
Solution 1 (Liste de contiguïté ):
Vous pouvez facilement récupérer toutes les catégories ou sous-catégories d'une catégorie en une seule requête en utilisant AVEC (expressions de table communes) clause (nécessite MySQL 8.0):
// Database connection
$options = [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$pdo = new PDO('mysql:host=localhost;dbname=<DATABASE_NAME>', '<USERNAME>', '<PASSWORD>', $options);
function getCategories(PDO $db, $parentId = null)
{
$sql = $parentId ? 'WITH RECURSIVE cte (id, name, parent_id) AS (SELECT id, name, parent_id FROM categories WHERE parent_id = ? UNION ALL SELECT c.id, c.name, c.parent_id FROM categories c INNER JOIN cte ON c.parent_id = cte.id) SELECT * FROM cte' : 'SELECT * FROM categories';
$stmt = $db->prepare($sql);
$stmt->execute($parentId ? [$parentId] : null);
return $stmt->fetchAll();
}
Si vous utilisez MySQL 5.7, modifiez cette fonction comme suit :
function getCategories(PDO $db, $parentId = null)
{
$sql = $parentId ? 'SELECT id, name, parent_id FROM (SELECT * FROM categories ORDER BY parent_id, id) c, (select @pv := ?) initialisation WHERE find_in_set(parent_id, @pv) AND LENGTH(@pv := concat(@pv, ",", id))' : 'SELECT * FROM categories';
$stmt = $db->prepare($sql);
$stmt->execute($parentId ? [$parentId] : null);
return $stmt->fetchAll();
}
Pour obtenir toutes les catégories dans votre base de données :
$allCategories = getCategories($pdo);
Sortie :
+----+--------------------------+-----------+
| id | name | parent_id |
+----+--------------------------+-----------+
| 1 | Electronics | NULL |
| 2 | Apparel & Clothing | NULL |
| 3 | Phones & Accessories | 1 |
| 4 | Computer & Office | 1 |
| 5 | Men's Clothing | 2 |
| 6 | Women's Clothing | 2 |
| 7 | Cell Phones | 3 |
| 8 | Cell Phone Accessories | 3 |
| 9 | Phone Parts | 3 |
| 10 | Computers & Accessories | 4 |
| 11 | Tablets & Accessories | 4 |
| 12 | Computer Peripherals | 4 |
| 13 | Computer Components | 4 |
| 14 | Office Electronics | 4 |
+----+--------------------------+-----------+
Pour obtenir les sous-catégories d'une catégorie :
$subCategories = getCategories($pdo, 1); // 1 is parent_id
Sortie :
+----+--------------------------+-----------+
| id | name | parent_id |
+----+--------------------------+-----------+
| 3 | Phones & Accessories | 1 |
| 4 | Computer & Office | 1 |
| 7 | Cell Phones | 3 |
| 8 | Cell Phone Accessories | 3 |
| 9 | Phone Parts | 3 |
| 10 | Computers & Accessories | 4 |
| 11 | Tablets & Accessories | 4 |
| 12 | Computer Peripherals | 4 |
| 13 | Computer Components | 4 |
| 14 | Office Electronics | 4 |
+----+--------------------------+-----------+
Si vous voulez une sortie HTML, vous pouvez parcourir $allCategories
/ $subCategories
(basé sur votre exemple):
function prepareCategories(array $categories)
{
$result = [
'all_categories' => [],
'parent_categories' => []
];
foreach ($categories as $category) {
$result['all_categories'][$category['id']] = $category;
$result['parent_categories'][$category['parent_id']][] = $category['id'];
}
return $result;
}
function buildCategories($categories, $parentId = null)
{
if (!isset($categories['parent_categories'][$parentId])) {
return '';
}
$html = '<ul>';
foreach ($categories['parent_categories'][$parentId] as $cat_id) {
if (isset($categories['parent_categories'][$cat_id])) {
$html .= "<li><a href='#'>{$categories['all_categories'][$cat_id]['name']}</a>";
$html .= buildCategories($categories, $cat_id);
$html .= '</li>';
} else {
$html .= "<li><a href='#'>{$categories['all_categories'][$cat_id]['name']}</a></li>";
}
}
$html .= '</ul>';
return $html;
}
echo buildCategories(prepareCategories($allCategories));
Sortie :
echo buildCategories(prepareCategories($subCategories), 1);
Sortie :
Solution 2 (Ensembles imbriqués ):
Nous ajouterons des colonnes supplémentaires left
et right
à notre tableau et y mettre des numéros qui identifieront les groupes appartenant au parent. (Notez que nous n'utiliserons pas parent_id
colonne.)
+----+--------------------------+--------------------------+
| id | name | parent_id | left | right |
+----+--------------------------+--------------------------+
| 1 | Electronics | NULL | 1 | 22 |
| 2 | Apparel & Clothing | NULL | 23 | 28 |
| 3 | Phones & Accessories | 1 | 2 | 9 |
| 4 | Computer & Office | 1 | 10 | 21 |
| 5 | Men's Clothing | 2 | 24 | 25 |
| 6 | Women's Clothing | 2 | 26 | 27 |
| 7 | Cell Phones | 3 | 3 | 4 |
| 8 | Cell Phone Accessories | 3 | 5 | 6 |
| 9 | Phone Parts | 3 | 7 | 8 |
| 10 | Computers & Accessories | 4 | 11 | 12 |
| 11 | Tablets & Accessories | 4 | 13 | 14 |
| 12 | Computer Peripherals | 4 | 15 | 16 |
| 13 | Computer Components | 4 | 17 | 18 |
| 14 | Office Electronics | 4 | 19 | 20 |
+----+--------------------------+--------------------------+
Maintenant, nous devons changer notre fonction :
function getCategories(PDO $db, $parentId = null)
{
$sql = $parentId ? 'SELECT children.* FROM categories parent INNER JOIN categories children ON parent.left < children.left AND parent.right > children.left WHERE parent.id = ?' : 'SELECT * FROM categories';
$stmt = $db->prepare($sql);
$stmt->execute($parentId ? [$parentId] : null);
return $stmt->fetchAll();
}
Pour obtenir toutes les catégories dans votre base de données :
$allCategories = getCategories($pdo);
Sortie :
+----+--------------------------+--------------------------+
| id | name | parent_id | left | right |
+----+--------------------------+--------------------------+
| 1 | Electronics | NULL | 1 | 22 |
| 2 | Apparel & Clothing | NULL | 23 | 28 |
| 3 | Phones & Accessories | 1 | 2 | 9 |
| 4 | Computer & Office | 1 | 10 | 21 |
| 5 | Men's Clothing | 2 | 24 | 25 |
| 6 | Women's Clothing | 2 | 26 | 27 |
| 7 | Cell Phones | 3 | 3 | 4 |
| 8 | Cell Phone Accessories | 3 | 5 | 6 |
| 9 | Phone Parts | 3 | 7 | 8 |
| 10 | Computers & Accessories | 4 | 11 | 12 |
| 11 | Tablets & Accessories | 4 | 13 | 14 |
| 12 | Computer Peripherals | 4 | 15 | 16 |
| 13 | Computer Components | 4 | 17 | 18 |
| 14 | Office Electronics | 4 | 19 | 20 |
+----+--------------------------+--------------------------+
Pour obtenir les sous-catégories d'une catégorie :
$subCategories = getCategories($pdo, 1); // 1 is parent_id
Sortie :
+----+--------------------------+--------------------------+
| id | name | parent_id | left | right |
+----+--------------------------+--------------------------+
| 3 | Phones & Accessories | 1 | 2 | 9 |
| 4 | Computer & Office | 1 | 10 | 21 |
| 7 | Cell Phones | 3 | 3 | 4 |
| 8 | Cell Phone Accessories | 3 | 5 | 6 |
| 9 | Phone Parts | 3 | 7 | 8 |
| 10 | Computers & Accessories | 4 | 11 | 12 |
| 11 | Tablets & Accessories | 4 | 13 | 14 |
| 12 | Computer Peripherals | 4 | 15 | 16 |
| 13 | Computer Components | 4 | 17 | 18 |
| 14 | Office Electronics | 4 | 19 | 20 |
+----+--------------------------+--------------------------+
Vous pouvez afficher le HTML comme indiqué dans la Solution 1 . En savoir plus sur la mise à jour et l'insertion de nouvelles données dans le modèle d'ensemble imbriqué.
Sources et lectures :
- Gestion des données hiérarchiques dans MySQL - ensemble imbriqué
- Gestion des données hiérarchiques dans MySQL
- Modèles pour les données hiérarchiques
- Gestion des données hiérarchiques dans MySQL à l'aide du modèle de liste d'adjacence
- Liste de contiguïté et ensembles imbriqués :MySQL
- Un plus Comparaison entre les intervalles imbriqués et la liste de contiguïté