In this tutorial, we’ll go through the process of implementing recursive partials in Laravel’s Blade templating engine by means of the @each
command. This will allow us to render data structures with an arbitrary number of nested children without needing to know the maximum depth of the array.
The Data
The data I’m talking about is data like folder structures which can go deep into many levels. For our case, let’s imagine we’re dealing with a predefined data set of “Projects” in a todo application like Todoist. Feel free to grab the sample data from this gist or the code embed below:
$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',
),
);
Plain old PHP
When using plain old PHP for outputting such data, one would probably use a method like this one:
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;
}
Ew. It works, but it’s highly inflexible and it mixes presentation with logic. Let’s not do this.
Blade Foreach
With Blade, things become a little simpler. We can use the foreach
construct to help us out.
@if (count($projects) > 0)
<ul>
@foreach ($projects as $project)
@include('partials.project', $project)
@endforeach
</ul>
@else
@include('partials.projects-none')
@endif
As Blade doesn’t really support defining functions, thus not letting us call them recursively like the output
function above, we need to define partials and have them call themselves:
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!
But… so much code for something so rudimentary. Is there no way to shorten this even further?
Blade @each
There is an un(der)documented feature of Laravel Blade that’ll help us decimate the LoC count in our template files, making the lives of both our devs and designers much easier. The feature is @each
and is used thusly:
@each('viewfile-to-render', $data, 'variablename','optional-empty-viewfile')
The first argument is the template to render. This will usually be a partial, like our project.blade.php
. The second one is the iterable dataset, in our case $projects
. Third is the variable name the elements will use when being iterated upon. For example, in foreach ($data as $element)
, this argument would be element
(without the $
). The fourth argument is an optional one – it’s the name of the template file which should be rendered when the second argument ($data
) is empty, i.e. has nothing to iterate over. If we apply all this to our case, we can replace this entire block:
@if (count($projects) > 0)
<ul>
@foreach ($projects as $project)
@include('partials.project', $project)
@endforeach
</ul>
@else
@include('partials.projects-none')
@endif
with
@each('partials.project', $projects, 'project', 'partials.projects-none')
Conclusion
In this short tutorial, we saw how we can leverage an underdocumented feature of Laravel Blade to drastically reduce the number of lines in our template code. By using @each
and relying on partials and their ability to recursively call themselves, we have an amazing arsenal of tools at our disposal for outputting all manners of data – it’s just a matter of putting the building blocks in the right order.
You can use this approach of partials recursion to echo out directory trees, content management categories, employee directories, and much, much more.
Did you know about @each
? Do you know of any other hidden gems? Let us know in the comments!
Frequently Asked Questions (FAQs) about Laravel Blade Recursive Partials
What is the purpose of Laravel Blade Recursive Partials?
Laravel Blade Recursive Partials are a powerful feature of the Laravel Blade templating engine. They allow developers to create reusable chunks of code that can be included in multiple places within a project. This can significantly reduce the amount of code duplication and make the codebase easier to maintain. Recursive Partials are particularly useful when dealing with nested data structures, as they can be used to iterate over the data and generate the corresponding HTML.
How do I create a Recursive Partial in Laravel Blade?
Creating a Recursive Partial in Laravel Blade is straightforward. First, you need to create a new Blade file that will serve as your partial. This file should contain the HTML and Blade syntax that you want to reuse. Then, within your main Blade file, you can include the partial using the @include directive, passing in any necessary data as the second argument.
Can I pass data to a Recursive Partial?
Yes, you can pass data to a Recursive Partial in Laravel Blade. When you include the partial using the @include directive, you can pass in an array of data as the second argument. This data will then be available within the partial. This is particularly useful when dealing with nested data structures, as you can pass each nested item to the partial as you iterate over the data.
How do I handle nested data with Recursive Partials?
Handling nested data with Recursive Partials in Laravel Blade is a two-step process. First, within your main Blade file, you iterate over the top-level data using a foreach loop. Then, within each iteration, you include the Recursive Partial, passing in the current item as data. Within the partial, you can then access the nested data and generate the corresponding HTML.
Can I use conditionals within a Recursive Partial?
Yes, you can use conditionals within a Recursive Partial in Laravel Blade. This allows you to control the output based on the data that is passed to the partial. For example, you might want to output different HTML depending on whether a nested item has children or not. You can achieve this by using the @if, @elseif, and @else directives within your partial.
How can I optimize the performance of Recursive Partials?
One way to optimize the performance of Recursive Partials in Laravel Blade is to avoid unnecessary database queries. Instead of querying the database within your partial, try to fetch all the necessary data upfront in your controller. Then, pass this data to your view and partials. This can significantly reduce the number of database queries and improve the performance of your application.
Can I use other Blade directives within a Recursive Partial?
Yes, you can use other Blade directives within a Recursive Partial. This includes directives for control structures (like @if and @foreach), as well as directives for including other views (like @include and @extends). This makes Recursive Partials a very flexible tool for structuring your views in Laravel.
How can I debug a Recursive Partial?
Debugging a Recursive Partial in Laravel Blade can be done using the same techniques as for any other Blade view. You can use the dd() function to dump the contents of variables and see what data is available within your partial. Additionally, Laravel’s error messages will tell you the file and line number where an error occurred, which can help you pinpoint the problem.
Can I use Recursive Partials with Laravel’s Form Builder?
Yes, you can use Recursive Partials with Laravel’s Form Builder. This can be particularly useful when creating forms with nested fields. You can create a Recursive Partial for each level of nesting, passing in the current item as data. Then, within your partial, you can use the Form Builder to generate the necessary form fields.
Can I use Recursive Partials with other Laravel features?
Yes, you can use Recursive Partials with other Laravel features. For example, you can use them with Laravel’s Eloquent ORM to display data from your database. You can also use them with Laravel’s localization features to create multilingual views. This makes Recursive Partials a versatile tool that can be used in many different contexts within a Laravel application.
Bruno is a blockchain developer and technical educator at the Web3 Foundation, the foundation that's building the next generation of the free people's internet. He runs two newsletters you should subscribe to if you're interested in Web3.0: Dot Leap covers ecosystem and tech development of Web3, and NFT Review covers the evolution of the non-fungible token (digital collectibles) ecosystem inside this emerging new web. His current passion project is RMRK.app, the most advanced NFT system in the world, which allows NFTs to own other NFTs, NFTs to react to emotion, NFTs to be governed democratically, and NFTs to be multiple things at once.