За последние 24 часа нас посетили 18824 программиста и 1635 роботов. Сейчас ищут 1922 программиста ...

Вывод категорий и подкатегорий

Тема в разделе "PHP для новичков", создана пользователем Dimon2x, 19 дек 2017.

  1. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    Правильно ли я сделал вывод?

    PHP:
    1. <?php
    2.  
    3. $host = 'localhost';
    4.     $db = 'test';
    5.     $user = 'root';
    6.     $pass = '';
    7.     $charset = 'utf8';
    8.     $dsn = "mysql:host=$host; dbname=$db;charset=$charset";
    9.     $opt = [
    10.         PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    11.         PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    12.         PDO::ATTR_EMULATE_PREPARES   => false,
    13.         ];
    14.        
    15.     $pdo = new PDO($dsn, $user, $pass, $opt);
    16.    
    17.     $sql = "SELECT * FROM `category`";
    18.    
    19.     $res = $pdo->query($sql)->fetchAll();
    20.    
    21.     echo '<ul>';
    22.         foreach($res as $key => $val) {
    23.             $subCat = "SELECT * FROM sub_category WHERE category_id = " . $val['id'];
    24.             $result = $pdo->query($subCat)->fetchAll();
    25.            
    26.             echo '<li>';
    27.                 echo '<a href="cat-'.$val['id'].'">'.$val['name']. '</a>';
    28.                
    29.                 echo '<ul>';
    30.                 foreach($result as $k => $v) {
    31.                     echo '<li><a href="sub-'.$v['id'].'">' . $v['name'] . '</a></li>';
    32.                 }
    33.                 echo '</ul>';
    34.             echo '</li>';
    35.         }
    36.     echo '</ul>';
    Для каждой категории, сделал отельный запрос, на подкатегорию, это правильный подход?

    Если сделать запрос на категории и подкатегории

    Код (Text):
    1. SELECT category.id, category.name, sub_category.name
    2.     FROM category LEFT JOIN sub_category
    3.     ON sub_category.category_id = category.id
    То получится вот такое

    1 Программы Антивирусы
    1 Программы Запись
    1 Программы Интернет
    1 Программы Аудио
    2 Фильмы Боевики
    2 Фильмы Фантастика
    2 Фильмы Ужастики

    И такой массив с массивами, будет сложнее вывести.
     
  2. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    PDO::FETCH_GROUP группирует, но не выводит саму категорию
     
  3. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    Я сделал, получилась такая штука

    PHP:
    1. $host = 'localhost';
    2.     $db = 'test';
    3.     $user = 'root';
    4.     $pass = '';
    5.     $charset = 'utf8';
    6.     $dsn = "mysql:host=$host; dbname=$db;charset=$charset";
    7.     $opt = [
    8.         PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    9.         PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    10.         PDO::ATTR_EMULATE_PREPARES   => false,
    11.         ];
    12.        
    13.     $pdo = new PDO($dsn, $user, $pass, $opt);
    14.    
    15.     $sql = "SELECT category.name, category.id as catId, sub_category.name, sub_category.id
    16.    FROM category LEFT JOIN sub_category
    17.    ON sub_category.category_id = category.id";
    18.    
    19.     $res = $pdo->query($sql)->fetchAll(PDO::FETCH_GROUP);
    20.    
    21.     echo '<ul>';
    22.     foreach($res as $key => $val) {
    23.         echo '<li>';
    24.         echo '<a href="/cat='.$val[0]['catId'].'">'.$key. '</a>';
    25.         echo '<ul>';
    26.             foreach($val as $k => $v) {
    27.                 echo '<li><a href="sub-'.$v['id'].'">' . $v['name'] . '</a></li>';
    28.             }
    29.         echo '</ul>';
    30.         echo '</li>';
    31.     }
    32.     echo '</ul>';
     
  4. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    Я усложнил задачу, теперь надо у подкатегории вывести подкатегорию.
    Убил 2 дня, перепробовал всякие способы, в итоге сделал свой велосипед на костылях, рекурсией мне такое задание сделать очень сложно.

    Такой код допускается на проекте? Какой способ ещё есть?

    Делаю запрос

    Код (Text):
    1. SELECT
    2.     category.id AS catId, category.name AS catName,
    3.     sub_category.id AS subCatId, sub_category.name AS subCatName,
    4.     page.id AS pageId, page.name AS pageName
    5.    
    6. FROM category LEFT JOIN sub_category
    7. ON sub_category.category_id = category.id LEFT JOIN page
    8. ON page.sub_category_id = sub_category.id ORDER BY category.id
    Мне присылается вот такой массив

    PHP:
    1. $res = [
    2.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 1, 'subCatName' => "Антивирусы", 'pageId' => "1", 'pageName' => "Касперский"],
    3.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 4, 'subCatName' => "Аудио", 'pageId' => "4", 'pageName' => "VirtualDJ"],
    4.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 4, 'subCatName' => "Аудио", 'pageId' => "5", 'pageName' => "FL Studio"],
    5.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 1, 'subCatName' => "Антивирусы", 'pageId' => "6", 'pageName' => "NOD32"],
    6.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 2, 'subCatName' => "Запись", 'pageId' => "NULL", 'pageName' => "NULL"],
    7.         ['catId' => 1, 'catName' => 'Программы', 'subCatId' => 3, 'subCatName' => "Интернет", 'pageId' => "NULL", 'pageName' => "NULL"],
    8.         ['catId' => 2, 'catName' => 'Фильмы',    'subCatId' => 5, 'subCatName' => "Боевики", 'pageId' => "2", 'pageName' => "Джпеки Чан"],
    9.         ['catId' => 2, 'catName' => 'Фильмы',    'subCatId' => 7, 'subCatName' => "Ужастики", 'pageId' => "3", 'pageName' => "Псы войны"],
    10.         ['catId' => 2, 'catName' => 'Фильмы',    'subCatId' => 5, 'subCatName' => "Боевики", 'pageId' => "8", 'pageName' => "Американский ниндзя"],
    11.         ['catId' => 2, 'catName' => 'Фильмы',    'subCatId' => 6, 'subCatName' => "Фантастика", 'pageId' => "NULL", 'pageName' => "NULL"],
    12.     ];
    Потом я из него делаю другой массив, который мне нужен.
    Вот так я его делаю

    PHP:
    1. $mass = [];
    2. foreach($res as $cat) {
    3.     if (!array_key_exists($cat['catName'] . '|' . $cat['catId'], $mass)) {
    4.         $mass[$cat['catName'] . '|' . $cat['catId']] = [$cat['subCatName'] . '|' . $cat['subCatId']=> [$cat['pageId']=>$cat['pageName']]];
    5.     }
    6.    
    7.     else {
    8.         $mass[$cat['catName']. '|' .$cat['catId']][$cat['subCatName'] . '|' .$cat['subCatId']][$cat['pageId']]=$cat['pageName'];
    9.     }
    10. }
    После этого получился вот такой массив

    Код (Text):
    1. Array
    2. (
    3.     [Программы|1] => Array
    4.         (
    5.             [Антивирусы|1] => Array
    6.                 (
    7.                     [1] => Касперский
    8.                     [6] => NOD32
    9.                 )
    10.  
    11.             [Аудио|4] => Array
    12.                 (
    13.                     [4] => VirtualDJ
    14.                     [5] => FL Studio
    15.                 )
    16.  
    17.             [Запись|2] => Array
    18.                 (
    19.                     [] =>
    20.                 )
    21.  
    22.             [Интернет|3] => Array
    23.                 (
    24.                     [] =>
    25.                 )
    26.  
    27.         )
    28.  
    29.     [Фильмы|2] => Array
    30.         (
    31.             [Боевики|5] => Array
    32.                 (
    33.                     [2] => Джеки Чан
    34.                     [8] => Американский ниндзя
    35.                 )
    36.  
    37.             [Ужастики|7] => Array
    38.                 (
    39.                     [3] => Псы войны
    40.                 )
    41.  
    42.             [Фантастика|6] => Array
    43.                 (
    44.                     [] =>
    45.                 )
    46.  
    47.         )
    48.  
    49. )
    И потом уже, создаю списки

    PHP:
    1. echo '<ul>';
    2. foreach($mass as $key => $val) {
    3.     $category = explode('|', $key);
    4.     echo '<li><a href="/category/'.$category[1].'">'.$category[0].'</a>';
    5.          foreach($val as $k => $v) {
    6.             $subCategory = explode('|', $k);
    7.                 echo '<ul>';
    8.                     echo '<li><a href="/sub-category/'.$subCategory[1].'">'.$subCategory[0].'</a>';
    9.                          foreach($v as $postKey => $postVal) {
    10.                             echo '<ul>';
    11.                                 echo '<li><a href="/post/'.$postKey.'">'.$postVal.'</a></li>';
    12.                             echo '</ul>';
    13.                          }
    14.                     echo '</li>';
    15.                 echo '</ul>';
    16.          }
    17.     echo '</li>';
    18. }
    19. echo '</ul>';
    Получается вот такое
    555.jpg
     
  5. Sergey_Tsarev

    Sergey_Tsarev Активный пользователь

    С нами с:
    17 мар 2016
    Сообщения:
    502
    Симпатии:
    105
    Как-то странно у тебя данные хранятся. Зачем писать в каждой строке имя категории и имя подкатегории, если у них уже есть id? А если ты в последствии захочешь поменять название категории, ты будешь все строки в таблице менять?
    На мой взгляд правильнее список категорий и подкатегорий хранить в отдельной таблице или массиве. Например так:
    PHP:
    1. $category =
    2.     [
    3.         1 =>
    4.             [
    5.                 'name' => 'Программы',
    6.                 'subcategory' =>
    7.                     [
    8.                         1 => 'Антивирусы',
    9.                         2 => 'Запись',
    10.                         3 => 'Аудио',
    11.                     ]
    12.             ],
    13.         2 =>
    14.             [
    15.                 'name' => 'Фильмы',
    16.                 'subcategory' =>
    17.                     [
    18.                         1 => 'Боевики',
    19.                         2 => 'Ужастики',
    20.                         3 => 'Фантастика',
    21.                     ]
    22.             ],
    23.     ];
     
  6. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    @Sergey_Tsarev Не получается сделать такой массив

    А почему в твоём массиве, у подкатегорий одинаковые ключи? Как потом запрос составлять?
     
  7. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    @Dimon2x, используй нормальные инструменты хранения деревьев в базе: Nested Sets, Materialized Paths и пр.
     
  8. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
  9. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    @Dimon2x, вполне. + существует куча готовых библиотек для работы с Nested Sets
     
  10. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
  11. Sergey_Tsarev

    Sergey_Tsarev Активный пользователь

    С нами с:
    17 мар 2016
    Сообщения:
    502
    Симпатии:
    105
    Ну, а в чем проблема? Примерно такой запрос:
    Код (Text):
    1. "SELECT `category` WHERE `catid` = '1' AND `subcatid` = '2'"
    Или можно получить все записи и на php их уже отсортировать так как нужно.
    --- Добавлено ---
    А одинаковые ключи в подкатегориях потому, что они никак между собой не пересекаются. Ведь они находятся в массиве конкретной категории. Например, если мне нужно вызвать название категории с id равным 1 я напишу так:
    PHP:
    1. echo $category[1]['name'];
    А если хочу вызвать название подкатегории с id равным 2 из категории с id равным 1, то вот так:
    PHP:
    1. echo $category[1]['subcategory'][2];
     
  12. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    Ну ты по статье разберись, почему оно работает, а код из проекта проект таскать. Ну можно свою библиотеку замутить, в принципе
    Наверное, не знаю. Я не пользовался. По описанию ничего
     
  13. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    @Sergey_Tsarev в БД у подкатегории Аудио id 4, а у тебя в массиве 3, ну и как потом сделать запрос, что вывелись все под категории а Аудио?
    --- Добавлено ---
    что-то я ни одной найти не могу
     
  14. Sergey_Tsarev

    Sergey_Tsarev Активный пользователь

    С нами с:
    17 мар 2016
    Сообщения:
    502
    Симпатии:
    105
    Ну я же тебе для примера написал :) Напиши массив, чтобы как у тебя все было. А ещё лучше создай в базе данных отдельную таблицу для категорий и подкатегорий.
     
  15. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    @Sergey_Tsarev так у меня и есть 3 таблицы для этого
     
  16. Sergey_Tsarev

    Sergey_Tsarev Активный пользователь

    С нами с:
    17 мар 2016
    Сообщения:
    502
    Симпатии:
    105
    @Dimon2x, ну так и сделай тогда три простых запроса к базе:

    1. Получить список всех категорий:
    Код (Text):
    1. "SELECT * FROM 'category'"
    2. Получить список всех подкатегорий:
    Код (Text):
    1. "SELECT * FROM 'subcategory'"
    3.Список программ, фильмов и т.п.
    Код (Text):
    1. "SELECT * FROM 'items'"
    А дальше выводи на экран.
     
  17. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    @Sergey_Tsarev создавать несколько запросов, когда можно только 1 это плохо
     
  18. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    Зачем, если есть решения, как неограниченную глубину подкатегорий хранить в одной таблице?
     
  19. Sergey_Tsarev

    Sergey_Tsarev Активный пользователь

    С нами с:
    17 мар 2016
    Сообщения:
    502
    Симпатии:
    105
    Да я и не спорю :) Я это писал к тому, что вот так, на мой взгляд, лучше не делать:
    PHP:
    1. $mass[$cat['catName'] . '|' . $cat['catId']] = [$cat['subCatName'] . '|' . $cat['subCatId']=> [$cat['pageId']=>$cat['pageName']]];
    2. $category = explode('|', $key);
    --- Добавлено ---
    Лучше 3 простых запроса, чем один супернавороченный.
    --- Добавлено ---
    Да и данные полученные из первых двух запросов можно хранить в кэше или в сессии наример.
     
  20. glorsh66

    glorsh66 Активный пользователь

    С нами с:
    9 июл 2017
    Сообщения:
    247
    Симпатии:
    4
    Кстати - а не быстрее для данной задачи - вначале выгрузить весь список категорий (целиком) и потом уже сдлеать обход полученного массива без запросов к базе?
     
  21. glorsh66

    glorsh66 Активный пользователь

    С нами с:
    9 июл 2017
    Сообщения:
    247
    Симпатии:
    4
    Хорошая статья! Только я вот не понял по какому принципу добавляются данные в данную структуру. Там очень замороченно написанно в разделе где обновление данных.
     
  22. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    Ну так, Nested Sets хороши при выборке, а добавление данных да - весьма заморочное.
    Вот, я попытался стрелками показать путь формирования left и right у Nested Sets
    upload_2017-12-27_17-2-45.png
     
    glorsh66 нравится это.
  23. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185
    Случайно нашёл способ вывода дерева Adjacency, это хороший код?

    PHP:
    1.  function get_tree($tree, $pid)
    2.     {
    3.         $html = '';
    4.  
    5.         foreach ($tree as $row)
    6.         {
    7.             if ($row['pid'] == $pid)
    8.             {
    9.                 $html .= '<li>' . "\n";
    10.                 $html .= '    ' . $row['name'] . "\n";
    11.                 $html .= '    ' . get_tree($tree, $row['id']);
    12.                 $html .= '</li>' . "\n";
    13.             }
    14.         }
    15.  
    16.         return $html ? '<ul>' . $html . '</ul>' . "\n" : '';
    17.     }
    18.        
    19.     $tree = array(
    20.         array('name' => 'Уровень 1',     'id' => 1,  'pid' => 0),
    21.         array('name' => 'Уровень 1.1',   'id' => 2,  'pid' => 1),
    22.         array('name' => 'Уровень 1.2',   'id' => 3,  'pid' => 1),
    23.         array('name' => 'Уровень 1.3',   'id' => 4,  'pid' => 1),
    24.         array('name' => 'Уровень 2',     'id' => 5,  'pid' => 0),
    25.         array('name' => 'Уровень 2.1',   'id' => 6,  'pid' => 5),
    26.         array('name' => 'Уровень 2.2',   'id' => 7,  'pid' => 5),
    27.         array('name' => 'Уровень 3',     'id' => 8,  'pid' => 0),
    28.         array('name' => 'Уровень 3.1',   'id' => 9,  'pid' => 8),
    29.         array('name' => 'Уровень 3.1.1', 'id' => 10, 'pid' => 9),
    30.         array('name' => 'Уровень 3.1.2', 'id' => 11, 'pid' => 9),
    31.     );
    32.     echo get_tree($tree, 0);
     
  24. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.588
    Симпатии:
    1.763
    Да не особо. Каждый рекурсивный вызов перебирает заново весь список, что хорошего?
     
  25. Dimon2x

    Dimon2x Старожил

    С нами с:
    26 фев 2012
    Сообщения:
    2.210
    Симпатии:
    185