В этом уроке мы рассмотрим процесс реализации рекурсивных элементов в шаблонном движке Laravel Blade при помощи команды @each. Это позволит нам создавать структуры данных со случайным числом вложенных элментов, без необходимости знания максимальной глубины массива.
Данные
Данные, о которых я говорю – данные как структуры папок, которые могут углубиться во многие уровни. В нашем случае, давайте представим, что мы имеем дело с заранее определенным набором “проектов” в приложении todo как Todoist. Не стесняйтесь прихватить выборочные данные из этой ссылки или кода ниже:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
$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 для вывода этих данных, можно было бы, вероятно, использовать такой метод:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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;
}
|
Уф. Работает, но он крайне негибкий и смешивает вывод с логикой. Давайте не будем делать этого.
Blade Foreach
С Blade, все становится немного проще. Для помощи мы можем использовать конструкцию foreach.
1
2
3
4
5
6
7
8
9
|
@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
1
2
3
4
5
6
7
8
|
<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
1
|
У вас нет проектов!
|
Но…Зачем столько много кода для столь элементарного. Неужели нет способа сократить это?
Blade @each
Существует не(до)документированная особенность Laravel Blade, которая поможет нам уничтожить подсчет LOC в наших файлах шаблонов, что облегчит жизнь нашим разработчикам и дизайнерам. Этой особенностью является @each и используется таким образом:
1
|
@each('viewfile-to-render', $data, 'variablename','optional-empty-viewfile')
|
Первым аргументом является шаблон для визуализации. Это, как правило, элемент, как наш project.blade.php. Вторым является итерируемый набор данных, в нашем случае $projects. Третьим является переменное имя, которое элементы будут использовать будучи итерируемыми. Например, в foreach ($data as $element), этот аргумент будет element (без $). Четвертым аргументом является необязательный – это имя файла шаблона, который должен быть передан, когда второй аргумент ($data) пуст, т.е. там нечего итерировать. Если мы применим все это к нашему случаю, мы можем заменить весь этот блок:
1
2
3
4
5
6
7
8
9
|
@if (count($projects) > 0)
<ul>
@foreach ($projects as $project)
@include('partials.project', $project)
@endforeach
</ul>
@else
@include('partials.projects-none')
@endif
|
с
1
|
@each('partials.project', $projects, 'project', 'partials.projects-none')
|
Вывод
В этом коротком уроке мы увидели, как можно использовать мало документированный Laravel Blade для резкого сокращения числа строк в коде шаблона. При использовании @each и опираясь на частицы и их способность рекурсивно называть себя, мы получаем удивительный арсенал инструментов в распоряжении для вывода всех видов данных – это похоже на кладку строительных блоков в правильном порядке.
Вы можете использовать этот подход рекурсии частиц для отражения дерева каталогов, категорий управления контентом, каталогов сотрудников, и многого, многого другое.
Знаете ли вы о @each? Знаете ли вы о каких-либо другие скрытых сокровищах? Расскажите нам в комментариях!