За последние 24 часа нас посетили 17923 программиста и 1566 роботов. Сейчас ищут 1306 программистов ...

Получение дочерних категорий

Тема в разделе "Laravel", создана пользователем Alex5646, 9 янв 2016.

  1. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    Есть модель Category, я в ней уже реализовал методы:
    Код (PHP):
    1. hasCategory($id) return bool
    2. hasChildren($id) return bool
    3. hasParent($id) return bool
    4. getChildren($id) return $this->where('parent_id', '=', $id)
    5. getParent($id) return $this->where('id', '=', $this->find($id)->parent_id)
    Затруднения возникли при построение деревьев сделал метод private loopParents($id, array &$parents) который сам себя вызывает и строит массив родительных категорий, и ещё обёртку для этого метода getParents($id).
    Начал делать метод loopChildrens($id, array &$children) и тут у меня окончательно фантазия кончилась, хотелось бы иметь вид массива на выходе:
    Код (PHP):
    1.   'id' => array(
    2.     Данные категории
    3.     'children' => array(
    4.       'id' => дальше повторяется
    5.     )
    6.   )
    7. ) 
    Как подобное реализовать?

    Подсказка от модератора:
    Любой код или текст конфигурации пишите между тегом [code=php] и [/code].
    Используйте отступы в коде для форматирования текста.
    Это помогает быстрее понять вас, увеличивает шанс на получение ответа.
    Что выделять? Например: PHP, HTML, CSS, JavaScript, SQL, XML, .htaccess, ini, регулярные выражения, код шаблонизаторов, любая другая разметка, результаты array/object dump и т. д.
     
  2. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Посмотри, может есть готовый код для
    Adjacency List, Matherialized Path & Nested Set
     
  3. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    Гугло поиск не чего не дал.

    Мне хотя бы кто нибуть теорию построения деревьев объяснил, а дальше я сам бы уже написал.
     
  4. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Ты искал отдельно?
    Adjacency List
    Matherialized Path
    Nested Set

    Добавлено спустя 2 минуты 58 секунд:
    https://github.com/etrepat/baum
     
  5. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    Да, с несколько дней назад ещё.
    Я на него посматривал, но я так и понял зачем в таблице иметь колонки left и right, поэтому мимо прошёл.
     
  6. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
  7. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    А как вывести всё дерево категорий, точнее как это правильно сделать. Можно конечно смешать вывод с контроллерами и написать функцию для вывода но это уже как то не правильно, не люблю я делать ТТУК'и. А в blade как это сделать в голову даже не приходит.
     
  8. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Ну с модели получаешь данные в нужном формате, потом виджет во view рисует всё что нужно.
     
  9. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    И в итоге у меня вот такая фигня получилась, не красиво и не правильно наверно.
    Код (PHP):
    1. <?php
    2. function loopCategories($array = null)
    3. {
    4.     echo '<ul class="categories">';
    5.     foreach(($array $array : app('App\Category')->roots()->get()) as $category) {
    6.         echo '<li>' . $category->name;
    7.         loopCategories($category->getDescendants());
    8.         echo '</li>';
    9.     }
    10.     echo '</ul>';
    11. }
    12. ?>
    13. <div class="panel panel-default">
    14.     <div class="panel-heading">
    15.         <h3 class="panel-title">Категории</h3>
    16.     </div>
    17.     <div class="panel-body">
    18.         {{ loopCategories() }}
    19.     </div>
    20. </div>
     
  10. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Если работает, то сойдёт, пройдёт время и сделаешь лучше.
     
  11. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    А хотелось бы сразу по лучше это реализовать.
     
  12. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Это вы так с nested sets маетесь? Зачем? Вы же постоянными вызовами getDescendants() на нет сводите преимущества nested sets. У вас же есть поле depth. Вот из моего проекта, тоже nested sets, только с другим фреймворком. $menu здесь - это все потомки корневого узла, level - то, что у вас должно depth называться. Я, правда, не использовал multi root, у меня один самый коренной элемент всегда зарезервирован
    Код (PHP):
    1. <?php
    2. $levels = [$menu[0]->level];
    3. echo "<ul>";
    4. foreach ($menu as $ind=>$item) {
    5.     if ($item->level > $levels[0]) {
    6.         echo "<ul>";
    7.         array_unshift($levels, $item->level);
    8.     }
    9.     else {
    10.         while ($item->level != $levels[0]) {
    11.             echo "</li></ul>";
    12.             array_shift($levels);
    13.         }
    14.     }
    15.     if ($ind != 0)
    16.         echo "</li>";
    17.     echo "<li><a href='$item->url'>$item->name</a>";
    18. }
    19. while (!empty($levels)) {
    20.    echo "</li></ul>";
    21.    array_shift($levels);
    22. }
    23.  
     
  13. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    я сейчас использую getImmediateDescendants(), и объяснил бы в чём преимущество заключается.
    У меня level называется.

    А если вложенность меню не известна твой код заработает? И дай не много пояснений, я только рекурсий и пользовался.
     
  14. artoodetoo

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

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    в NS можно выбрать любое под-дерево за один запрос. его за этим придумали. поле "глубина" это уже не классика NS и оно необязательно.

    неизвестно, немного - слитно
     
  15. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Заработает. Я только на multi-root его не пробовал, в остальном - должен как часы. Так всё же очевидно. Мы выбираем дерево и всех его потомков за один запрос. level или depth есть во всех реализациях nested sets, которыми я пользовался, поэтому этот алгоритм на него рассчитан. Начинаем идти по дереву, одновременно занося уровень, с которым работаем, в стек $levels. Таким образом мы можем определить, с каким уровнем работаем, сравнив level текущего элемента с уровнем на вершине стека (правда, в итоге получается не совсем чистый стек, поскольку мы проверяем вершину, не выталкивая её из стека, но это уже тонкости). Если уровень оказался больше, чем на вершине стека, заносим новый уровень в стек (теперь он на вершине), выводим новый ul. Если уровень меньше - немного сложнее. Скачок может быть сразу на несколько уровней вниз, поэтому идёт цикл - пока уровень на вершине стека не достиг уровня текущего элемента, закрываем ul и li и выталкиваем один элемент из стека. Ну и после того, как все элементы выведены, повторяем цикл закрытия, до тех пор, пока стек не станет пустым.

    Добавлено спустя 8 минут 22 секунды:
    Кстати, artoodetoo, было бы интересно, если бы вы предложили решение этой задачи без стека и рекурсии, или хотя бы без глубины. Я не представляю себе такого решения.
     
  16. artoodetoo

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

    С нами с:
    11 июн 2010
    Сообщения:
    11.108
    Симпатии:
    1.243
    Адрес:
    там-сям
    в исходном алгоритме Joe Celko есть только left и right, насколько я помню.
    depth упрощает нахождение immediate children. зачем оно для вывода всего поддерева, я не понимаю, если честно. меню ведь строится по всему списку?!

    конкретно построение меню по NS: http://habrahabr.ru/post/188118/

    Добавлено спустя 1 минуту 35 секунд:
    я бы предложил итерацию со стеком.
     
  17. denis01

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

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Ещё бы не забыть все эти построения дерева кэшировать, часть в массив, другую часть как сформированный html.
     
  18. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Т.е. ровно то, что написал я. В статье у них кстати всё сложнее
     
  19. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    mkramer, как не странно но твой способ "не работает" всё зависит от порядка элементов массива, вот с этим массивом всё норм (left и right отпустил):
    Код (PHP):
    1. [
    2.   [
    3.     'id' => 1,
    4.     'level' => 0,
    5.     'parent' => 0
    6.   ],
    7.         [
    8.             'id' => 2,
    9.             'level' => 1,
    10.             'parent' => 1,
    11.         ],
    12.   [
    13.     'id' => 3,
    14.     'level' => 0,
    15.     'parent' => 0
    16.   ],
    17. ]
    Вот с таким заработает но вложеность будет не правильная:
    Код (PHP):
    1. [
    2.   [
    3.     'id' => 1,
    4.     'level' => 0,
    5.     'parent' => 0
    6.   ],
    7.   [
    8.     'id' => 2,
    9.     'level' => 0,
    10.     'parent' => 0
    11.   ],
    12.         [
    13.             'id' => 3,
    14.             'level' => 1,
    15.             'parent' => 1,
    16.         ],
    17. ]
    А если всё вразброс и неограниченный уровень вложенности, так вообще ошибок хренова туча. Это массив надо как то сортировать что бы дочерние элементы стояли за родительными.
     
  20. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Alex5646, со вторым да, не сработает. Но я же рассчитывал алгоритм на правильную выборку Nested Sets, с сортировкой по left. Иначе Nested Sets вообще смысла не имеют, если не делать сортировку по left при выборках. Эта сортировка как раз и обеспечит, что дочерние элементы будут идти за родительскими.
     
  21. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    А моя выборка то есть не правильная? Я выбираю комментарии по post_id. Как мне узнать нужный left, если в комментариях полнейший хаос?
     
  22. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.583
    Симпатии:
    1.761
    Ну так, к каждому post_id своё дерево комментариев строится, ведь комменты разные. Я обычно ставлю корневой комментарий к каждому посту, а потом выбираю его потомков. То есть при добавлении коммента к посту (если нужны древовидные), я создаю корневой сначала узел,с 0 уровнем, и содержанием типа "Comments for post 123", и потом уже добавляю к нему потомков. Таким образом полный порядок. Честно говоря, пришло решение это само собой. Надо решить, или использовать nested sets и его преимущества, или parent_id с вечной рекурсией. Есть люди, которым не нравятся Nested Sets, они предлагают строить рекурсивные массивы и потом использовать SPL для обхода, мне больше нравится мой способ. Если бы мы сразу обсуждали комменты, я бы это сразу написал, но мы обсуждали категории, которые обычно сами по себе
     
  23. Alex5646

    Alex5646 Новичок

    С нами с:
    29 дек 2015
    Сообщения:
    277
    Симпатии:
    4
    Адрес:
    От верлюда
    Вот так теперь работает.