Иерархия категорий (PHP / MySQL)

Я пытаюсь получить все категории и подкатегории из базы данных MySQL в иерархии:

Мой результат должен быть таким (только пример):

  1. Cat A
    • Sub-Cat 1
      • Sub_Sub_Cat 1
      • Sub_Sub_Cat 2
    • Sub_Cat 2
  2. Cat B
  3. Cat C

Код MySQL:

CREATE TABLE IF NOT EXISTS `categories` ( `category_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `parent_id` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'for sub-categories' PRIMARY KEY (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ; 

Просто, как можно получить это в hirarchy с PHP-кодами ?

    При использовании модели списка смежности вы можете создать структуру за один проход.

    Взято из структуры одного родительского дочернего массива (сентябрь 2007 г., Нейт Вайнер) :

     $refs = array(); $list = array(); $sql = "SELECT item_id, parent_id, name FROM items ORDER BY name"; /** @var $pdo \PDO */ $result = $pdo->query($sql); foreach ($result as $row) { $ref = & $refs[$row['item_id']]; $ref['parent_id'] = $row['parent_id']; $ref['name'] = $row['name']; if ($row['parent_id'] == 0) { $list[$row['item_id']] = & $ref; } else { $refs[$row['parent_id']]['children'][$row['item_id']] = & $ref; } } 

    Из связанной статьи, вот фрагмент, чтобы создать список для вывода. Он рекурсивный, если есть дети для узла, он снова вызывает себя, чтобы создать поддерево.

     function toUL(array $array) { $html = '<ul>' . PHP_EOL; foreach ($array as $value) { $html .= '<li>' . $value['name']; if (!empty($value['children'])) { $html .= toUL($value['children']); } $html .= '</li>' . PHP_EOL; } $html .= '</ul>' . PHP_EOL; return $html; } 

    Связанный вопрос:

    • Как получить вложенный список HTML из набора записей массива объектов?

    У меня есть новая идея, я думаю, это будет хорошо. Идея такова: в столбце category_parent мы вставим ссылку на всех родителей этого узла.

     + ---- + ---------------------- + ----------------- +
     |  id |  category_name |  иерархия |
     + ---- + ---------------------- + ----------------- +
     |  1 |  cat1 |  1 |
     + ---- + ---------------------- + ----------------- +
     |  2 |  cat2 |  2 |
     + ---- + ---------------------- + ----------------- +
     |  3 |  cat3 |  3 |
     + ---- + ---------------------- + ----------------- +
     |  4 |  subcat1_1 |  1-4 |
     + ---- + ---------------------- + ----------------- +
     |  5 |  subcat1_2 |  1-5 |
     + ---- + ---------------------- + ----------------- +
     |  6 |  subsubcat1_1 |  1-4-6 |
     + ---- + ---------------------- + ----------------- +
     |  7 |  subsubcat1_2 |  1-4-7 |
     + ---- + ---------------------- + ----------------- +
     |  8 |  subsubcat1_3 |  1-4-8 |
     + ---- + ---------------------- + ----------------- +
     |  9 |  subsubcat1_3_1 |  1-4-8-9 |
     + ---- + ---------------------- + ----------------- +
     |  10 |  subsubcat1_3_2 |  1-4-8-10 |
     + ---- + ---------------------- + ----------------- +
     |  11 |  subsubcat1_3_1_1 |  1-4-8-9-11 |
     + ---- + ---------------------- + ----------------- +
     |  12 |  subsubsubcat1_3_1_1 |  1-4-8-9-12 |
     + ---- + ---------------------- + ----------------- +
     |  13 |  subsubsubcat1_3_1_2 |  1-4-8-9-11-13 |
     + ---- + ---------------------- + ----------------- +
     |  14 |  subsubsubcat1_2_1_3 |  1-4-8-9-11-14 |
     + ---- + ---------------------- + ----------------- +
    

    если вы посмотрите на мою обновленную таблицу, вы заметите, что в каждой записи есть ссылка на своих родителей, а не только на прямую, но и на всех родителей. И для этой работы я сделал некоторые изменения для вставки:

     Insert into table_name (category_name, hierarchy) values ('new_name', (concat(parent_hierarch, '-', (SELECT Auto_increment FROM information_schema.tables WHERE table_name='table_name')))) 

    Теперь давайте сделаем желаемые запросы:

    1- все подкатегории автомобилей:

     select * from table_name where hierarchy like '1-%' 

    2- если вам нужен весь родитель BLACK, вы просто вводите:

     select * from table_name where hierarchy = '1-4-8-9' or hierarchy = '1-4-8' or hierarchy = '1-4' or hierarchy = '1' 

    (вы можете построить этот запрос из php, разбивая поле иерархии на символе '-')

    3- Чтобы увидеть все категории, с уровнем и прямым родителем:

     select *, SUBSTR(hierarchy, 1, (LENGTH(hierarchy) - LENGTH(id) - 1)) as parent, LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '-', '')) as level From table_name 
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  id |  название категории |  иерархия |  родитель |  уровень |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  1 |  cat1 |  1 |  |  0 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  2 |  cat2 |  2 |  |  0 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  3 |  cat3 |  3 |  |  0 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  4 |  subcat1_1 |  1-4 |  1 |  1 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  5 |  subcat1_2 |  1-5 |  1 |  1 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  6 |  subsubcat1_1 |  1-4-6 |  1-4 |  2 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  7 |  subsubcat1_2 |  1-4-7 |  1-4 |  2 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  8 |  subsubcat1_3 |  1-4-8 |  1-4 |  2 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  9 |  subsubcat1_3_1 |  1-4-8-9 |  1-4-8 |  3 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  10 |  subsubcat1_3_2 |  1-4-8-10 |  1-4-8 |  3 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  11 |  subsubcat1_3_1_1 |  1-4-8-9-11 |  1-4-8-9 |  4 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  12 |  subsubsubcat1_3_1_1 |  1-4-8-9-12 |  1-4-8-9 |  4 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  13 |  subsubsubcat1_3_1_2 |  1-4-8-9-11-13 | 1-4-8-9-11 |  5 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
     |  14 |  subsubsubcat1_2_1_3 |  1-4-8-9-11-14 | 1-4-8-9-11 |  5 |
     + ---- + ---------------------- + ----------------- + --- -------- + -------- +
    

    Это новая идея и нуждается в улучшении.

    @Amnon Ваш код работает отлично. Просто протестировал его с помощью CodeIgniter, и он работал как шарм. Вот рабочий код, если кому-то это нужно:

     <?php function disTree($all_cats) { $tree = array(); foreach ($all_cats as $cat) { $pid = $cat->parent_id; $id = $cat->cat_id; $name = $cat->cat_name; // Create or add child information to the parent node if (isset($tree[$pid])) // a node for the parent exists // add another child id to this parent $tree[$pid]["children"][] = $id; else // create the first child to this parent $tree[$pid] = array("children"=>array($id)); // Create or add name information for current node if (isset($tree[$id])) // a node for the id exists: // set the name of current node $tree[$id]["name"] = $name; else // create the current node and give it a name $tree[$id] = array( "name"=>$name ); } return $tree; } function toUL($tree, $id, $html){ $html .= '<ul>'.PHP_EOL; if (isset($tree[$id]['name'])) $html .= '<li>' . $tree[$id]['name']; if (isset($tree[$id]['children'])) { $arChildren = &$tree[$id]['children']; $len = count($arChildren); for ($i=0; $i<$len; $i++) { $html .= toUL($tree, $arChildren[$i], ""); } $html .= '</li>'.PHP_EOL; } $html .= '</ul>'.PHP_EOL; return $html; } $tree = disTree($all_cats); // Display the tree echo toUL($tree, 0, ""); ?> 

    Единственное, что я изменил, это добавить мой собственный массив ($ all_cats).

    Есть еще один способ добиться такого же эффекта, который я нахожу немного более легким (без контрольного трюка). Вы строите дерево, добавляя соответствующую информацию к текущему узлу и его родительскому объекту (предположим, что foreach выполняет итерацию по возвращенным строкам из SQL-запроса):

     $tree = array(); foreach ($query->result() as $row) { $pid = $row->parent_id; $id = $row->id; $name = $row->name; // Create or add child information to the parent node if (isset($tree[$pid])) // a node for the parent exists // add another child id to this parent $tree[$pid]["children"][] = $id; else // create the first child to this parent $tree[$pid] = array("children"=>array($id)); // Create or add name information for current node if (isset($tree[$id])) // a node for the id exists: // set the name of current node $tree[$id]["name"] = $name; else // create the current node and give it a name $tree[$id] = array( "name"=>$name ); } return $tree; 

    и для отображения дерева:

     function toUL($tree, $id, $html){ $html .= '<ul>'.PHP_EOL; if (isset($tree[$id]['name'])) $html .= '<li>' . $tree[$id]['name']; if (isset($tree[$id]['children'])) { $arChildren = &$tree[$id]['children']; $len = count($arChildren); for ($i=0; $i<$len; $i++) { $html .= toUL($tree, $arChildren[$i], ""); } $html .= '</li>'.PHP_EOL; } $html .= '</ul>'.PHP_EOL; return $html; } // Display the tree echo toUL($tree, 0, ""); 

    Попробуйте следующий код

    // подключаемся к mysql и выбираем db

    $conn = mysqli_connect('localhost', 'user', 'password','database');

     if( !empty($conn->connect_errno)) die("Error " . mysqli_error($conn)); //call the recursive function to print category listing category_tree(0); //Recursive php function function category_tree($catid){ global $conn; $sql = "select * from category where parent_id ='".$catid."'"; $result = $conn->query($sql); while($row = mysqli_fetch_object($result)): $i = 0; if ($i == 0) echo '<ul>'; echo '<li>' . $row->cat_name; category_tree($row->id); echo '</li>'; $i++; if ($i > 0) echo '</ul>'; endwhile; } //close the connection mysqli_close($conn); ?> 

    введите описание изображения здесь

    Больше…