Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 29.05.2017 09:20:48

Рекурсивное отображение категорий

Здравствуйте.
У меня не как не получается отобразить категории с 3-4 уровнем вложенности, только с 2 уровнем вложенности выходит.

@foreach($categories->where('parent_id', 0) as $category)
    <div>{{ $category->name }}</div>
    @foreach($categories->where('parent_id', $category->id) as $child)
        <div>{{ $child->name }}</div>
    @endforeach
@endforeach

Вот этим кодом отрисовывается только 2 уровня, как можно рекурсивно категории отрисовать что б 3-4 уровня были? Как вы это делаете?

Не в сети

#2 29.05.2017 20:40:49

Re: Рекурсивное отображение категорий

Рекурсивно (вызов самого себя) это не значит итеративно (через цикл). Для этого делается отдельный шаблон (функция, …) и он вызывает сам себя.

Шаблон:

@include('categories', [ 'cat' => $categories->where('parent_id', 0) ])

Шаблон categories:

@foreach ($cat as $category)
  <div>...</div>
  @include('categories', ['cat' => ...where...])
@endforeach

Но вообще делать запросы к БД в шаблонах это плохая практика.

Не в сети

#3 30.05.2017 05:43:10

Re: Рекурсивное отображение категорий

Но вообще делать запросы к БД в шаблонах это плохая практика

я когда увидел where в примерах, подумал про коллекции. на коллекциях же тоже есть where.

Не в сети

#4 30.05.2017 08:04:24

Re: Рекурсивное отображение категорий

Спасибо за подробный ответ!
Это не запросы, это на уровне коллекций все идет) даже если это были бы запросы, то возвращался бы билдер так как нет "get" или "pluck".

constb пишет:

я когда увидел where в примерах, подумал про коллекции. на коллекциях же тоже есть where.

Да это коллекция, вы правильно подумали.

Просто во всех туториалах и ответах, делают 2 запроса к бд (создают связь на саму себя) на поле parent_id. А с коллекциями получается всего один запрос.

Не в сети

#5 01.06.2017 17:40:18

Re: Рекурсивное отображение категорий

Сегодня заметил что при подключении шаблона, создаются запросы к бд.

// controller
$categories = Category::parent()-with('children')->orderBy('parent_id, 'asc')->get();
// Mutator
public function scopeParent($query)
{
    return $query-where('parent_id',0);
}
// Relation
public function children()
{
    return $this->hasMany(Category::class, 'parent_id');
}

В шаблоне

<div class="category-tree">
   @include('partials.tree', ['categories' => $categories])
</div>

// Partials.tree
<ul>
    @foreach($categories as $category)
        <li>{{ $category->name }}</li>
        @if($category->children)
            @include('partials.tree', ['categories' => $category->children])
        @endif
    @endforeach
</ul>

Запросы создает код @include('partials.tree', ['categories' => $category->children]). Но почему? Коллекция ведь уже загружена. Может дебагбар ошибается?)

Не в сети

#6 02.06.2017 05:28:41

Re: Рекурсивное отображение категорий

не ошибается. связь children загружена только для верхнего уровня категорий, для вложенных обращение к ней всё равно происходит – модель не может знать что их там нет. самый простой вариант – загружать с ->with(['children', 'children.children']) – там всё равно будет «холостой» запрос на несуществующий уровень, но зато в момент исполнения шаблона модели уже будут знать что вложенных в них элементов не существует.

Не в сети

#7 03.06.2017 06:48:36

Re: Рекурсивное отображение категорий

constb, спасибо помогло!
Но это всеравно не то… Аж 4 запроса к базе идет при 3х уровневой иерархии. Блин, столько разработчиков на ларе, и неужели никто ничего подобного не делал?

Нашел работающий код, но он отдает один сгенерированный массив да еще и не удобно, по ссылке.

PHP
// Then write a function and call it recursively as many times as you need. filter method would be really helpful to work with your categories collection

// Something like this should work:

function getCategories($categories, &$result$parent_id 0$depth 0)
{
    
//filter only categories under current "parent"
    
$cats $categories->filter(function ($item) use ($parent_id) {
        return 
$item->parent_id == $parent_id;
    });

    
//loop through them
    
foreach ($cats as $cat)
    {
        
//add category. Don't forget the dashes in front. Use ID as index
        
$result[$cat->id] = str_repeat('-'$depth) . $cat->title;
        
//go deeper - let's look for "children" of current category
        
getCategories($categories$result$cat->id$depth 1);
    }
}

//get categories data. In this case it's eloquent.
$categories Category::get();
//if you don't have the eloquent model you can use DB query builder:
//$categories = DB::table('categories')->select('id', 'parent_id', 'title')->get();
//prepare an empty array for $id => $formattedVal storing
$result = [];
//start by root categories
getCategories($categories$result);

Вообще решение с collection->filter() самое подходящее. На первом проходе он отдает коллекцию родителей, потом потомков и тд. Осталось только как нибудь массив сформировать примерно такой

PHP
array(
    
=> array(
        
'parent' => Category() // model
        
'children' => collection() // collection of children models
    
)
    
=> array(
        
'parent' => Category() // model
        
'children' => collection() // collection of children models
    
)
    
=> array(
        
'parent' => Category() // model
        
'children' => collection() // collection of children models
    
)
)

В общем думаю дальше)

Не в сети

#8 03.06.2017 08:46:00

Re: Рекурсивное отображение категорий

4 запроса это не так много, но мне кажется тут оптимизировать проще и быстрее через кэш, чем пытаться ещё лучше обрабатывать эти категории. дело в том что список категорий вряд ли меняется часто, даже если время жизни кэша поставить на 5 минут – это всё равно экономия будет в несколько запросов на 5 минут – овчинка выделки мягко говоря не стоит. я бы оставил 4 запроса и завернул их в cache()->remember(). из кэша, даже если он на файлах или в базе, выбирать данные всё равно намного быстрее, не говоря уже о мемкэше или редисе…

сам кэш удобно сбрасывать на событиях модели кстати – буквально в несколько строчек кода выходит и потом работает само

Не в сети

#9 04.06.2017 00:51:34

Re: Рекурсивное отображение категорий

Доброго всем здравия. В ларавель я пока только вникаю, но всё-же вставлю свои 5 коп. по теме.
Есть одно, удобное на мой взгляд, решение для древовидных структур, в т.ч. категорий, комментариев и т.п.
По моему опыту, категорий обычно не запредельно много. Поэтому выбираю всегда все категории одним запросом, а вот уже полученный массив рекурсивно строю в дерево с учётом значения parent_id (зависимостей).
Способ этот в своё время нашёл вроде-бы на "тостере" https://toster.ru/q/7550#answer_32413
Как это грамотно реализовать в рамках laravel - самому интересно.

Изменено Real (04.06.2017 00:52:51)


Всё, что существует на свете, когда-то было мечтой.

Не в сети

Подвал раздела