Статьи

Laravel Blade Рекурсивные Частицы с @each

В этом уроке мы пройдем процесс реализации рекурсивных партиалов в шаблонном движке Laravel’s Blade с помощью команды @each . Это позволит нам визуализировать структуры данных с произвольным числом вложенных дочерних элементов без необходимости знать максимальную глубину массива.

Данные

Данные, о которых я говорю, — это данные, подобные структурам папок, которые могут углубляться во многие уровни. Для нашего случая давайте представим, что мы имеем дело с предопределенным набором данных «Проекты» в приложении todo, таком как Todoist . Не стесняйтесь взять пример данных из этой сущности или кода, вставленного ниже:

 $a = array( 0 => array( 'indent' => 1, 'name' => 'Inbox', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'inbox_project' => true, 'archived_date' => null, 'item_order' => 0, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837507, 'children' => array(), 'parent' => 'root', ), 1 => array( 'indent' => 1, 'name' => 'Personal', 'color' => '#fc603c', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 1, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837508, 'children' => array(), 'parent' => 'root', ), 2 => array( 'indent' => 1, 'name' => 'Work', 'color' => '#a8c9e5', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 2, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837509, 'children' => array( 0 => array( 'indent' => 2, 'name' => 'Work indent 1-1', 'color' => '#a8c9e5', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 3, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576614, 'children' => array( 0 => array( 'indent' => 3, 'name' => 'Work indent 1-2', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 4, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576626, 'children' => array(), 'parent' => 139576614, ), 1 => array( 'indent' => 3, 'name' => 'Work indent 1-2 2nd', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 5, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576629, 'children' => array(), 'parent' => 139576614, ), ), 'parent' => 138837509, ), 1 => array( 'indent' => 2, 'name' => 'Work indent 2-1', 'color' => '#a8c9e5', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 6, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576622, 'children' => array( 0 => array( 'indent' => 3, 'name' => 'Work indent 2-2', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 7, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576636, 'children' => array(), 'parent' => 139576622, ), ), 'parent' => 138837509, ), 2 => array( 'indent' => 2, 'name' => 'Work indent 3-1', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 8, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 139576646, 'children' => array(), 'parent' => 138837509, ), ), 'parent' => 'root', ), 3 => array( 'indent' => 1, 'name' => 'Errands', 'color' => '#74e8d4', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 9, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837510, 'children' => array(), 'parent' => 'root', ), 4 => array( 'indent' => 1, 'name' => 'Shopping', 'color' => '#dddddd', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 10, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837511, 'children' => array(), 'parent' => 'root', ), 5 => array( 'indent' => 1, 'name' => 'Movies to watch', 'color' => '#e3a8e5', 'is_deleted' => 0, 'collapsed' => 0, 'archived_date' => null, 'item_order' => 11, 'is_archived' => 0, 'archived_timestamp' => 0, 'user_id' => 3840103, 'id' => 138837512, 'children' => array(), 'parent' => 'root', ), ); 

Простой старый PHP

При использовании старого доброго PHP для вывода таких данных, вероятно, следует использовать такой метод:

 public function output($projects) { $string = "<ul>"; foreach ($projects as $i => $project) { $string .= "<li>"; $string .= $project['name']; if (count($project['children'])) { $string .= $this->output($project['children']); } $string .= "</li>"; } $string .= "</ul>"; return $string; } 

Еа. Это работает, но очень негибко и смешивает представление с логикой. Давай не будем этого делать.

Клинок Foreach

С Blade все становится немного проще. Мы можем использовать конструкцию foreach чтобы выручить нас.

 @if (count($projects) > 0) <ul> @foreach ($projects as $project) @include('partials.project', $project) @endforeach </ul> @else @include('partials.projects-none') @endif 

Поскольку Blade на самом деле не поддерживает определение функций, таким образом, не позволяя нам вызывать их рекурсивно, как описано выше в output функции, нам нужно определить партиалы и заставить их вызывать себя:

  • partials/project.blade.php

     <li>{{ $project['name'] }}</li> @if (count($project['children']) > 0) <ul> @foreach($project['children'] as $project) @include('partials.project', $project) @endforeach </ul> @endif 
  • partials/projects-none.blade.php

     You have no projects! 

Но … так много кода для чего-то такого элементарного. Нет ли способа сократить это еще дальше?

Blade @each

Существует документированная функция Laravel Blade, которая поможет нам уменьшить количество LoC в наших файлах шаблонов, делая жизнь наших разработчиков и дизайнеров намного проще. Функция @each используется следующим образом:

 @each('viewfile-to-render', $data, 'variablename','optional-empty-viewfile') 

Первый аргумент — это шаблон для отображения. Обычно это будет частичным, как наш project.blade.php . Второй — это итеративный набор данных, в нашем случае $projects . Третье — это имя переменной, которую элементы будут использовать при повторении. Например, в foreach ($data as $element) этот аргумент будет element (без $ ). Четвертый аргумент является необязательным — это имя файла шаблона, которое должно отображаться, когда второй аргумент ( $data ) пуст, т.е. не имеет итерации. Если мы применим все это к нашему случаю, мы можем заменить весь этот блок:

 @if (count($projects) > 0) <ul> @foreach ($projects as $project) @include('partials.project', $project) @endforeach </ul> @else @include('partials.projects-none') @endif 

с

 @each('partials.project', $projects, 'project', 'partials.projects-none') 

Вывод

В этом коротком руководстве мы увидели, как мы можем использовать недокументированную функцию Laravel Blade, чтобы значительно сократить количество строк в коде нашего шаблона. Используя @each и полагаясь на партиалы и их способность рекурсивно вызывать себя, мы имеем в своем распоряжении удивительный арсенал инструментов для вывода всех типов данных — это всего лишь вопрос размещения строительных блоков в правильном порядке.

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

Вы знали о @each ? Знаете ли вы какие-либо другие скрытые драгоценные камни? Дайте нам знать об этом в комментариях!