Chapter 4. Blade Templating
Compared to most other backend languages, PHP actually functions relatively well as a templating language. But it has its shortcomings, and it’s also just ugly to be using <?php
inline all over the place, so you can expect most modern frameworks to offer a templating language.
Laravel offers a custom templating engine called Blade, which is inspired by .NET’s Razor engine. It boasts a concise syntax, a shallow learning curve, a powerful and intuitive inheritance model, and easy extensibility.
For a quick look at what writing Blade looks like, check out Example 4-1.
Example 4-1. Blade samples
<
h1
>
{{
$group
->
title
}}
</
h1
>
{
!!
$group
->
heroImageHtml
()
!!
}
@
forelse
(
$users
as
$user
)
•
{{
$user
->
first_name
}}
{{
$user
->
last_name
}}
<
br
>
@
empty
No
users
in
this
group
.
@
endforelse
As you can see, Blade uses curly braces for its “echo” and introduces a convention in which its custom tags, called “directives,” are prefixed with an @
. You’ll use directives for all of your control structures and also for inheritance and any custom functionality you want to add.
Blade’s syntax is clean and concise, so at its core it’s just more pleasant and tidy to work with than the alternatives. But the moment you need anything of any complexity in your templates—nested inheritance, complex conditionals, or recursion—Blade starts to really shine. Just like the best Laravel components, it takes complex application requirements and makes them easy and accessible.
Additionally, since all Blade syntax is compiled into normal PHP code and then cached, it’s fast and it allows you to use native PHP in your Blade files if you want. However, I’d recommend avoiding using PHP if at all possible—usually if you need to do anything that you can’t do with Blade or a custom Blade directive, it doesn’t belong in the template.
Creating views
Views are just the files ending in .blade.php
that live in the resources/views
directory. You can create your own view files manually, but there’s also an Artisan command for it:
phpartisan
make:view
posts.show
Notice the use of the dot syntax; this command will create a file called show.blade.php
in the resources/views/posts
directory.
Echoing Data
As you can see in Example 4-1, {{
and }}
are used to wrap sections of PHP that you’d like to echo. {{
is similar to $variable
}}<?=
$variable
?>
in plain PHP.
It’s different in one way, however: Blade escapes all echoes by default using PHP’s htmlentities()
to protect your users from malicious script insertion. That means {{
is functionally equivalent to $variable
}}<?=
htmlentities(
$variable
)?>
. If you want to echo without the escaping, use {!!
and !!}
instead.
Control Structures
Most of the control structures in Blade will be very familiar. Many directly echo the name and structure of the same tag in PHP.
There are a few convenience helpers, but in general, the control structures just look cleaner than they would in PHP.
Conditionals
First, let’s take a look at the control structures that allow for logic.
@if
Blade’s @if ($condition)
compiles to <?php if ($condition): ?>
. @else
, @elseif
, and @endif
also compile to the exact same style of syntax in PHP. Take a look at Example 4-2 for some examples.
Example 4-2. @if
, @else
, @elseif
, and @endif
@
if
(
count
(
$talks
)
===
1
)
There
is
one
talk
at
this
time
period
.
@
elseif
(
count
(
$talks
)
===
0
)
There
are
no
talks
at
this
time
period
.
@
else
There
are
{{
count
(
$talks
)
}}
talks
at
this
time
period
.
@
endif
Just like with the native PHP conditionals, you can mix and match these how you want. They don’t have any special logic; there’s literally a parser looking for something with the shape of @if (
and replacing it with the appropriate PHP code.$condition
)
@unless and @endunless
@unless
, on the other hand, is a new syntax that doesn’t have a direct equivalent in PHP. It’s the direct inverse of @if
. @unless ($condition)
is the same as <?php if (! $condition)
. You can see it in use in Example 4-3.
Example 4-3. @unless
and @endunless
@
unless
(
$user
->
hasPaid
())
You
can
complete
your
payment
by
switching
to
the
payment
tab
.
@
endunless
Loops
Next, let’s take a look at the loops.
@for, @foreach, and @while
@for
, @foreach
, and @while
work the same in Blade as they do in PHP; see Examples 4-4, 4-5, and 4-6.
Example 4-4. @for
and @endfor
@
for
(
$i
=
0
;
$i
<
$talk
->
slotsCount
();
$i
++
)
The
number
is
{{
$i
}}
<
br
>
@
endfor
Example 4-5. @foreach
and @endforeach
@
foreach
(
$talks
as
$talk
)
•
{{
$talk
->
title
}}
({{
$talk
->
length
}}
minutes
)
<
br
>
@
endforeach
Example 4-6. @while
and @endwhile
@
while
(
$item
=
array_pop
(
$items
))
{{
$item
->
orSomething
()
}}
<
br
>
@
endwhile
@forelse and @endforelse
@forelse
is a @foreach
that also allows you to program in a fallback if the object you’re iterating over is empty. We saw it in action at the start of this chapter; Example 4-7 shows another example.
Example 4-7. @forelse
@
forelse
(
$talks
as
$talk
)
•
{{
$talk
->
title
}}
({{
$talk
->
length
}}
minutes
)
<
br
>
@
empty
No
talks
this
day
.
@
endforelse
Template Inheritance
Blade provides a structure for template inheritance that allows views to extend, modify, and include other views.
Let’s take a look at how inheritance is structured with Blade.
Defining Sections with @section/@show and @yield
Let’s start with a top-level Blade layout, like in Example 4-8. This is the definition of a generic page wrapper that we’ll later place page-specific content into.
Example 4-8. Blade layout
<!-- resources/views/layouts/master.blade.php -->
<
html
>
<
head
>
<
title
>
My Site | @yield('title', 'Home Page')</
title
>
</
head
>
<
body
>
<
div
class
=
"container"
>
@yield('content')</
div
>
@section('footerScripts')<
script
src
=
"app.js"
></
script
>
@show</
body
>
</
html
>
This looks a bit like a normal HTML page, but you can see we’ve yielded in two places (title
and content
) and we’ve defined a section in a third (footerScripts
). We have three Blade directives here: @yield('content')
alone, @yield('title', 'Home Page')
with a defined default, and @section/@show
with actual content in it.
Although they each look a little different, all three function essentially the same. All three are defining that there’s a section with a given name (the first parameter) that can be extended later, and all three are defining what to do if the section isn’t extended. They do this either by providing a string fallback ('Home Page'
), no fallback (which will just not show anything if it’s not extended), or an entire block fallback (in this case, <script src="app.js"></script>
).
What’s different? Well, clearly, @yield('content')
has no default content. But additionally, the default content in @yield('title')
will only be shown if it’s never extended. If it is extended, its child sections will not have programmatic access to the default value. @section/@show
, on the other hand, is both defining a default and doing so in such a way that its default contents will be available to its children, through @parent
.
Once you have a parent layout like this, you can extend it in a new template file like in Example 4-9.
Example 4-9. Extending a Blade layout
<!-- resources/views/dashboard.blade.php -->
@extends('layouts.master') @section('title', 'Dashboard') @section('content') Welcome to your application dashboard! @endsection @section('footerScripts') @parent<
script
src
=
"dashboard.js"
></
script
>
@endsection
@show Versus @endsection
You may have noticed that Example 4-8 uses @section/@show
, but Example 4-9 uses @section/@endsection
. What’s the difference?
Use @show
when you’re defining the place for a section, in the parent template. Use @endsection
when you’re defining the content for a template in a child template.
This child view allows us to cover a few new concepts in Blade inheritance.
@extends
In Example 4-9, with @extends('layouts.master')
, we define that this view should not be rendered on its own but that it instead extends another view. That means its role is to define the content of various sections, but not to stand alone. It’s almost more like a series of buckets of content, rather than an HTML page. This line also defines that the view it’s extending lives at resources/views/layouts/master.blade.php.
Each file should only extend one other file, and the @extends
call should be the first line of the file.
@section and @endsection
With @section('title', 'Dashboard')
, we provide our content for the first section, title
. Since the content is so short, instead of using @section
and @endsection
, we’re just using a shortcut. This allows us to pass the content in as the
second parameter of @section
and then move on. If it’s a bit disconcerting to see @section
without @endsection
, you could just use the normal syntax.
With @section('content')
and following, we use the normal syntax to define the contents of the content
section. We’ll just throw a little greeting in for now. Note, however, that when you’re using @section
in a child view, you end it with @endsection
(or its alias @stop
), instead of @show
, which is reserved for defining sections in parent views.
@parent
Finally, with @section('footerScripts')
and on, we use the normal syntax to define the contents of the footerScripts
section.
But remember, we actually defined that content (or, at least, its “default”) already in the master layout. So this time, we have two options: we can either overwrite the content from the parent view, or we can add to it.
You can see that we have the option to include the content from the parent by using the @parent
directive within the section. If we didn’t, the content of this section would entirely overwrite anything defined in the parent for this section.
Including View Partials
Now that we’ve established the basics of inheritance, there are a few more tricks we can perform.
@include
What if we’re in a view and want to pull in another view? Maybe we have a call-to-action “Sign up” button that we want to reuse around the site. And maybe we want to customize the button text every time we use it. Take a look at Example 4-10.
Example 4-10. Including view partials with @include
<!-- resources/views/home.blade.php -->
<
div
class
=
"content"
data-page-name
=
"{{ $pageName }}"
>
<
p
>
Here's why you should sign up for our app:<
strong
>
It's Great.</
strong
></
p
>
@include('sign-up-button', ['text' => 'See just how great it is'])</
div
>
<!-- resources/views/sign-up-button.blade.php -->
<
a
class
=
"button button--callout"
data-page-name
=
"{{ $pageName }}"
>
<
i
class
=
"exclamation-icon"
></
i
>
{{ $text }}</
a
>
@include
pulls in the partial and, optionally, passes data into it. Note that not only can you explicitly pass data to an include via the second parameter of @include
, but you can also reference any variables within the included file that are available to the including view ($pageName
, in this example). Once again, you can do whatever you want, but I would recommend you consider always explicitly passing every variable that you intend to use, just for clarity.
You also use the @includeIf
, @includeWhen
, and @includeFirst
directives, as shown in Example 4-11.
Example 4-11. Conditionally including views
{{-- Include a view if it exists --}} @includeIf('sidebars.admin', ['some' => 'data']) {{-- Include a view if a passed variable is truth-y --}} @includeWhen($user->isAdmin(), 'sidebars.admin', ['some' => 'data']) {{-- Include the first view that exists from a given array of views --}} @includeFirst(['customs.header', 'header'], ['some' => 'data'])
@each
You can probably imagine some circumstances in which you’d need to loop over an array or collection and @include
a partial for each item. There’s a directive for that: @each
.
Let’s say we have a sidebar composed of modules, and we want to include multiple modules, each with a different title. Take a look at Example 4-12.
Example 4-12. Using view partials in a loop with @each
<!-- resources/views/sidebar.blade.php -->
<
div
class
=
"sidebar"
>
@each('partials.module', $modules, 'module', 'partials.empty-module')</
div
>
<!-- resources/views/partials/module.blade.php -->
<
div
class
=
"sidebar-module"
>
<
h1
>
{{ $module->title }}</
h1
>
</
div
>
<!-- resources/views/partials/empty-module.blade.php -->
<
div
class
=
"sidebar-module"
>
No modules :(</
div
>
Consider that @each
syntax. The first parameter is the name of the view partial. The second is the array or collection to iterate over. The third is the variable name that each item (in this case, each element in the $modules
array) will be passed as to the view. And the optional fourth parameter is the view to show if the array or collection is empty (or, optionally, you can pass a string in here that will be used as your
template).
Using Components
Laravel offers another pattern for including content between views: components. Components make the most sense in contexts where you find yourself using view partials and passing large chunks of content into them as variables. Take a look at Example 4-13 for an example of a modal, or popover, that might alert the user in response to an error or other action.
Example 4-13. A modal as an awkward view partial
<!-- resources/views/partials/modal.blade.php -->
<
div
class
=
"modal"
>
<
h2
>
{{ $title }}</
h2
>
<
div
>
{!! $content !!}</
div
>
<
div
class
=
"close button etc"
>
...</
div
>
</
div
>
<!-- in another template -->
@include('partials.modal', [ 'title' => 'Insecure password', 'content' => '<
p
>
The password you have provided is not valid. Here are the rules for valid passwords: [...]</
p
><
p
><
a
href
=
"#"
>
...</
a
></
p
>
' ])
This is too much for these poor variables, and it’s the perfect fit for a component.
Laravel’s components are another way of structuring view partials that looks much closer to how components work in frontend frameworks like Vue. They may be more familiar to frontend developers, but they also have a few significant benefits compared to view partials, including that it’s much easier to pass large sections of template code into them.
Take a look at Example 4-14 to see how to refactor Example 4-13 with components.
Example 4-14. A modal as a more appropriate component
<!-- resources/views/components/modal.blade.php -->
<
div
class
=
"modal"
>
<
h2
>
{{ $title }}</
h2
>
<
div
>
{{ $slot }}</
div
>
<
div
class
=
"close button etc"
>
...</
div
>
</
div
>
<!-- in another template -->
<
x-modal
title
=
"Insecure password"
>
<
p
>
The password you have provided is not valid. Here are the rules for valid passwords: [...]</
p
>
<
p
><
a
href
=
"#"
>
...</
a
></
p
>
</
x-modal
>
As you can see in Example 4-14, components allow us to pull our HTML out of a cramped variable string and back into the template space.
Let’s dig into more of the features of components, how they’re structured, and how we write them.
Creating components
Components can exist either as purely Blade templates (anonymous components), or as Blade templates backed by a PHP class that injects data and functionality (class-based components).
If you only need a template, you can generate your component with the --view
flag:
phpartisan
make:component
Modal
--view
If you also want to generate the PHP class, exclude that flag:
phpartisan
make:component
Modal
If you’d like to group your components under folders, you can separate them with a slash:
# To create it:
php
artisan
make
:
component
Modals
/
Cancellation
This component is created in the app/View/Components/Modals directory and the corresponding view in the resources/views/components/modals directory
// To use it:
<
x
-
modals
.
cancellation
/>
You can also customize the path for your components:
phpartisan
make:component
NewUserPopup
--path
ui/popups
This will create a component in the app/View/Components directory, and its view in the resources/views/ui/popups directory.
Passing data into components
There are four ways to pass data into components: string attributes, PHP attributes, the default slot, and named slots.
Passing data into components via attributes
Let’s start with attributes. You can pass strings directly into components by passing attributes with no prefix, or you can pass PHP variables and expressions with a colon prefix, as you can see in Example 4-15.
Example 4-15. Passing data to components via attributes
<!-- Passing the data in -->
<
x-modal
title
=
"Title here yay"
:width
=
"$width"
/>
<!-- Accessing the data in the template -->
<
div
style
=
"width: {{ $width }}"
>
<
h1
>
{{ $title }}</
h1
>
</
div
>
For class-based components, you’ll need to define every attribute in the PHP class and set it as a public property on the class, as in Example 4-16.
Example 4-16. Defining attributes as public on component classes
class
Modal
extends
Component
{
public
function
__construct
(
public
string
$title
,
public
string
$width
,
)
{}
}
For anonymous components, you’ll need to define the attributes in a props
array at the top of your template:
@
props
([
'width'
,
'title'
,
])
<
div
style
=
"width: {{
$width
}}"
>
<
h1
>
{{
$title
}}
</
h1
>
</
div
>
Passing data into components via slots
In Example 4-14 you may have noticed that the contents of the modal were refered to as a variable, $slot
. But where did this come from?
By default, every component that has an opening and a closing tag when it’s referenced has a $slot
variable, and it’s filled with all the HTML between those two tags. In Example 4-14, the $slot
variable contains the two <p>
tags and everything inside (and between) them.
But what if you need two or more slots? You can add more than just the default slot, giving each slot its own name and variable. Let’s rework Example 4-14 assuming we want to define the title in a slot; take a look at Example 4-17.
Example 4-17. Defining multiple slots
<
x-modal
>
<
x-slot:title
>
<
h2
class
=
"uppercase"
>
Password requirements not met</
h2
>
</
x-slot
>
<
p
>
The password you have provided is not valid. Here are the rules for valid passwords: [...]</
p
>
<
p
><
a
href
=
"#"
>
...</
a
></
p
>
</
x-modal
>
The contents of this new $slot
variable will be accessible to the component template as a $title
variable, just like the attribute was before.
Component methods
At times it can be helpful to have a helper method in a component that performs some logic. One common pattern is to use these methods for complex logic checks you’d rather keep out of your templates.
Components allow you to call any public method on their associated PHP class in the template by prefixing the method name with $
, as you can see in Example 4-18.
Example 4-18. Defining and calling component methods
// in the component definition
public
function
isPromoted
(
$item
)
{
return
$item
->
promoted_at
!==
null
&&
!
$item
->
promoted_at
->
isPast
();
}
<!-- in the template -->
<
div
>
@if ($isPromoted($item))<!-- show promoted badge -->
@endif<!-- ... -->
</
div
>
Attributes grab bag
Most of the attributes we’ll pass into our components will be named, specific, and similar to passing parameters to a PHP function.
But sometimes there are just loose HTML attributes we want to pass in, almost always so they can be assigned to the root element of our template.
With components, you can grab all of those attributes at once, using the $attributes
variable. This variable captures all the attributes not defined as properties and allows you to echo them out (by treating it like a string) or interact with some of its methods for grabbing or inspecting data.
Take a look at the docs to learn about all the ways you can interact with the $attributes
object, but here is one very useful trick:
<!-- Merge default classes with passed-in classes -->
<!-- Definition -->
<
div
{{
$
attributes-
>
merge(['class' => 'p-4 m-4']) }}> {{ $message }}</
div
>
<!-- Usage -->
<
x-notice
class
=
"text-blue-200"
>
Message here</
x-notice
>
<!-- Outputs: -->
<
div
class
=
"p-4 m-4 text-blue-200"
>
Message here</
div
>
Using Stacks
One common pattern that can be difficult to manage using basic Blade includes is when each view in a Blade includes hierarchy needs to add something to a certain section—almost like adding an entry to an array.
The most common situation for this is when certain pages (and sometimes, more broadly, certain sections of a website) have specific, unique CSS and JavaScript files they need to load. Imagine you have a site-wide “global” CSS file, a “jobs section” CSS file, and an “apply for a job” page CSS file.
Blade’s stacks are built for exactly this situation. In your parent template, define a stack, which is just a placeholder. Then, in each child template you can “push” entries onto that stack with @push/@endpush
, which adds them to the bottom of the stack in the final render. You can also use @prepend/@endprepend
to add them to the top of the stack. Example 4-19 illustrates.
Example 4-19. Using Blade stacks
<!-- resources/views/layouts/app.blade.php -->
<
html
>
<
head
>
<
link
href
=
"/css/global.css"
>
<!-- the placeholder where stack content will be placed -->
@stack('styles')</
head
>
<
body
>
<!-- // -->
</
body
>
</
html
>
<!-- resources/views/jobs.blade.php -->
@extends('layouts.app') @push('styles')<!-- push something to the bottom of the stack -->
<
link
href
=
"/css/jobs.css"
>
@endpush<!-- resources/views/jobs/apply.blade.php -->
@extends('jobs') @prepend('styles')<!-- push something to the top of the stack -->
<
link
href
=
"/css/jobs--apply.css"
>
@endprepend
These generate the following result:
<
html
>
<
head
>
<
link
href
=
"/css/global.css"
>
<!-- the placeholder where stack content will be placed -->
<!-- push something to the top of the stack -->
<
link
href
=
"/css/jobs--apply.css"
>
<!-- push something to the bottom of the stack -->
<
link
href
=
"/css/jobs.css"
>
</
head
>
<
body
>
<!-- // -->
</
body
>
</
html
>
View Composers and Service Injection
As we covered in Chapter 3, it’s simple to pass data to our views from the route definition (see Example 4-20).
Example 4-20. Reminder of how to pass data to views
Route
::
get
(
'passing-data-to-views'
,
function
()
{
return
view
(
'dashboard'
)
->
with
(
'key'
,
'value'
);
});
There may be times, however, when you find yourself passing the same data over and over to multiple views. Or you might find yourself using a header partial or something similar that requires some data; will you have to pass that data in from every route definition that might ever load that header partial?
Binding Data to Views Using View Composers
Thankfully, there’s a simpler way. The solution is called a view composer, and it allows you to define that any time a particular view loads, it should have certain data passed to it—without the route definition having to pass that data in explicitly.
Let’s say you have a sidebar on every page, which is defined in a partial named partials.sidebar
(resources/views/partials/sidebar.blade.php) and then included on every page. This sidebar shows a list of the last seven posts that were published on your site. If it’s on every page, every route definition would normally have to grab that list and pass it in, like in Example 4-21.
Example 4-21. Passing sidebar data in from every route
Route
::
get
(
'home'
,
function
()
{
return
view
(
'home'
)
->
with
(
'posts'
,
Post
::
recent
());
});
Route
::
get
(
'about'
,
function
()
{
return
view
(
'about'
)
->
with
(
'posts'
,
Post
::
recent
());
});
That could get annoying quickly. Instead, we’re going to use view composers to “share” that variable with a prescribed set of views. We can do this a few ways, so let’s start simple and move up.
Sharing a variable globally
First, the simplest option: just globally “share” a variable with every view in your application, like in Example 4-22.
Example 4-22. Sharing a variable globally
// Some service provider
public
function
boot
()
{
...
view
()
->
share
(
'recentPosts'
,
Post
::
recent
());
}
If you want to use view()->share()
, the best place would be the boot()
method of a service provider so that the binding runs on every page load. You can create a custom ViewComposerServiceProvider
(see “Service Providers” for more detail), but for now just put it in App\Providers\AppServiceProvider
in the boot()
method.
Using view()->share()
makes the variable accessible to every view in the entire application, however, so it might be overkill.
View-scoped view composers with closures
The next option is to use a closure-based view composer to share variables with a single view, like in Example 4-23.
Example 4-23. Creating a closure-based view composer
view
()
->
composer
(
'partials.sidebar'
,
function
(
$view
)
{
$view
->
with
(
'recentPosts'
,
Post
::
recent
());
});
As you can see, we’ve defined the name of the view we want it shared with in the first parameter (partials.sidebar
) and then passed a closure to the second parameter; in the closure we’ve used $view->with()
to share a variable, but only with a specific view.
View-scoped view composers with classes
Finally, the most flexible, but also the most complex, option is to create a dedicated class for your view composer.
First, let’s create the view composer class. There’s no formally defined place for view composers to live, but the docs recommend App\Http\ViewComposers
. So, let’s create App\Http\ViewComposers\RecentPostsComposer
like in Example 4-24.
Example 4-24. A view composer
<?
php
namespace
App\Http\ViewComposers
;
use
App\Post
;
use
Illuminate\Contracts\View\View
;
class
RecentPostsComposer
{
public
function
compose
(
View
$view
)
{
$view
->
with
(
'recentPosts'
,
Post
::
recent
());
}
}
As you can see, when this composer is called, it runs the compose()
method, in which we bind the recentPosts
variable to the result of running the Post
model’s recent()
method.
Like the other methods of sharing variables, this view composer needs to have a binding somewhere. Again, you’d likely create a custom ViewComposerServiceProvider
, but for now, as seen in Example 4-25, we’ll just put it in the boot()
method of App\Providers\AppServiceProvider
.
Example 4-25. Registering a view composer in AppServiceProvider
public
function
boot
()
:
void
{
view
()
->
composer
(
'partials.sidebar'
,
\App\Http\ViewComposers\RecentPostsComposer
::
class
);
}
Note that this binding is the same as a closure-based view composer, but instead of passing a closure, we’re passing the class name of our view composer. Now, every time Blade renders the partials.sidebar
view, it’ll automatically run our provider and pass the view a recentPosts
variable set to the results of the recent()
method on our Post
model.
Blade Service Injection
There are three primary types of data we’re most likely to inject into a view: collections of data to iterate over, single objects that we’re displaying on the page, and services that generate data or views.
With a service, the pattern will most likely look like Example 4-26, where we inject an instance of our analytics service into the route definition by typehinting it in the route’s method signature, and then pass it into the view.
Example 4-26. Injecting services into a view via the route definition constructor
Route
::
get
(
'backend/sales'
,
function
(
AnalyticsService
$analytics
)
{
return
view
(
'backend.sales-graphs'
)
->
with
(
'analytics'
,
$analytics
);
});
Just as with view composers, Blade’s service injection offers a convenient shortcut to reduce duplication in your route definitions. Normally, the content of a view using our analytics service might look like Example 4-27.
Example 4-27. Using an injected navigation service in a view
<
div
class
=
"finances-display"
>
{{
$analytics
->
getBalance
()
}}
/
{{
$analytics
->
getBudget
()
}}
</
div
>
Blade service injection makes it easy to inject an instance of a class from the container directly into the view, like in Example 4-28.
Example 4-28. Injecting a service directly into a view
@
inject
(
'analytics'
,
'App\Services\Analytics'
)
<
div
class
=
"finances-display"
>
{{
$analytics
->
getBalance
()
}}
/
{{
$analytics
->
getBudget
()
}}
</
div
>
As you can see, this @inject
directive has actually made an $analytics
variable available, which we’re using later in our view.
The first parameter of @inject
is the name of the variable you’re injecting, and the second parameter is the class or interface that you want to inject an instance of. This is resolved just like when you typehint a dependency in a constructor elsewhere in Laravel; if you’re unfamiliar with how that works, check out Chapter 11 to learn more.
Just like view composers, Blade service injection makes it easy to make certain data or functionality available to every instance of a view, without having to inject it via the route definition every time.
Custom Blade Directives
All of the built-in syntax of Blade that we’ve covered so far—@if
, @unless
, and so on—are called directives. Each Blade directive is a mapping between a pattern (e.g., @if ($condition)
) and a PHP output (e.g., <?php if ($condition): ?>
).
Directives aren’t just for the core; you can actually create your own. You might think directives are good for making little shortcuts to bigger pieces of code—for example, using @button('buttonName')
and having it expand to a larger set of button HTML. This isn’t a terrible idea, but for simple code expansion like this you might be better off including a view partial.
Custom directives tend to be most useful when they simplify some form of repeated logic. Say we’re tired of having to wrap our code with @if (auth()
->
guest())
(to check if a user is logged in or not) and we want a custom @ifGuest
directive. As with view composers, it might be worth having a custom service provider to register these, but for now let’s just put it in the boot()
method of App\Providers\AppServiceProvider
. Take a look at Example 4-29 to see what this binding will look like.
Example 4-29. Binding a custom Blade directive in a service provider
public
function
boot
()
:
void
{
Blade
::
directive
(
'ifGuest'
,
function
()
{
return
"<?php if (auth()->guest()): ?>"
;
});
}
We’ve now registered a custom directive, @ifGuest
, which will be replaced with the PHP code <?php if (auth()->guest()): ?>
.
This might feel strange. You’re writing a string that will be returned and then executed as PHP. But what this means is that you can now take the complex, ugly, unclear, or repetitive aspects of your PHP templating code and hide them behind clear, simple, and expressive syntax.
Custom Directive Result Caching
You might be tempted to do some logic to make your custom directive faster by performing an operation in the binding and then embedding the result within the returned string:
Blade
::
directive
(
'ifGuest'
,
function
()
{
// Antipattern! Do not copy.
$ifGuest
=
auth
()
->
guest
();
return
"<?php if (
{
$ifGuest
}
): ?>"
;
});
The problem with this idea is that it assumes this directive will be re-created on every page load. However, Blade caches aggressively, so you’re going to find yourself in a bad spot if you try this.
Parameters in Custom Blade Directives
What if you want to accept parameters in your custom logic? Check out Example 4-30.
Example 4-30. Creating a Blade directive with parameters
// Binding
Blade
::
directive
(
'newlinesToBr'
,
function
(
$expression
)
{
return
"<?php echo nl2br(
{
$expression
}
); ?>"
;
});
// In use
<
p
>@
newlinesToBr
(
$message
->
body
)
</
p
>
The $expression
parameter received by the closure represents whatever’s within the parentheses. As you can see, we then generate a valid PHP code snippet and return it.
If you find yourself constantly writing the same conditional logic over and over, you should consider a Blade directive.
Example: Using Custom Blade Directives for a Multitenant App
Let’s imagine we’re building an application that supports multitenancy, which means users might be visiting the site from www.myapp.com, client1.myapp.com, client2.myapp.com, or elsewhere.
Suppose we have written a class to encapsulate some of our multitenancy logic and named it Context
. This class will capture information and logic about the context of the current visit, such as who the authenticated user is and whether the user is visiting the public website or a client subdomain.
We’ll probably frequently resolve that Context
class in our views and perform conditionals on it, like in Example 4-31. app('context')
is a shortcut to get an instance of a class from the container, which we’ll learn more about in Chapter 11.
Example 4-31. Conditionals on context without a custom Blade directive
@
if
(
app
(
'context'
)
->
isPublic
())
&
copy
;
Copyright
MyApp
LLC
@
else
&
copy
;
Copyright
{{
app
(
'context'
)
->
client
->
name
}}
@
endif
What if we could simplify @if (app('context')->isPublic())
to just @ifPublic
? Let’s do it. Check out Example 4-32.
Example 4-32. Conditionals on context with a custom Blade directive
// Binding
Blade
::
directive
(
'ifPublic'
,
function
()
{
return
"<?php if (app('context')->isPublic()): ?>"
;
});
// In use
@
ifPublic
&
copy
;
Copyright
MyApp
LLC
@
else
&
copy
;
Copyright
{{
app
(
'context'
)
->
client
->
name
}}
@
endif
Since this resolves to a simple if
statement, we can still rely on the native @else
and @endif
conditionals. But if we wanted, we could also create a custom @elseIfClient
directive, or a separate @ifClient
directive, or really whatever else we want.
Easier Custom Directives for “if” Statements
Although custom Blade directives are powerful, the most common use for them is if
statements. So there’s a simpler way to create custom “if” directives: Blade::if()
. Example 4-33 shows how we could refactor Example 4-32 using the Blade::if()
method:
Example 4-33. Defining a custom “if” Blade directive
// Binding
Blade
::
if
(
'ifPublic'
,
function
()
{
return
(
app
(
'context'
))
->
isPublic
();
});
You’ll use the directives exactly the same way, but as you can see, defining them is a bit simpler. Instead of having to manually type out PHP braces, you can just write a closure that returns a Boolean.
Testing
The most common method of testing views is through application testing, meaning that you’re actually calling the route that displays the views and ensuring the views have certain content (see Example 4-34). You can also click buttons or submit forms and ensure that you are redirected to a certain page or that you see a certain error. (You’ll learn more about testing in Chapter 12.)
Example 4-34. Testing that a view displays certain content
// EventsTest.php
public
function
test_list_page_shows_all_events
()
{
$event1
=
Event
::
factory
()
->
create
();
$event2
=
Event
::
factory
()
->
create
();
$this
->
get
(
'events'
)
->
assertSee
(
$event1
->
title
)
->
assertSee
(
$event2
->
title
);
}
You can also test that a certain view has been passed a particular set of data, which, if it accomplishes your testing goals, is less fragile than checking for certain text on the page. Example 4-35 demonstrates this approach.
Example 4-35. Testing that a view was passed certain content
// EventsTest.php
public
function
test_list_page_shows_all_events
()
{
$event1
=
Event
::
factory
()
->
create
();
$event2
=
Event
::
factory
()
->
create
();
$response
=
$this
->
get
(
'events'
);
$response
->
assertViewHas
(
'events'
,
Event
::
all
());
$response
->
assertViewHasAll
([
'events'
=>
Event
::
all
(),
'title'
=>
'Events Page'
,
]);
$response
->
assertViewMissing
(
'dogs'
);
}
With assertViewHas()
we can pass in a closure, meaning we can customize how we want to check more complex data structures. Example 4-36 illustrates how we might use this.
Example 4-36. Passing a closure to assertViewHas()
// EventsTest.php
public
function
test_list_page_shows_all_events
()
{
$event1
=
Event
::
factory
()
->
create
();
$response
=
$this
->
get
(
"events/{
$event1->id
}"
);
$response
->
assertViewHas
(
'event'
,
function
(
$event
)
use
(
$event1
)
{
return
$event
->
id
===
$event1
->
id
;
});
}
TL;DR
Blade is Laravel’s templating engine. Its primary focus is a clear, concise, and expressive syntax with powerful inheritance and extensibility. Its “safe echo” brackets are {{
and }}
, its unprotected echo brackets are {!!
and !!}
, and it has a series of custom tags called “directives” that all begin with @
(@if
and @unless
, for example).
You can define a parent template and leave “holes” in it for content using @yield
and @section
/@show
. You can then teach its child views to extend the parent using @extends('parent.view')
and define their sections using @section
/@endsection
. You use @parent
to reference the content of the block’s parent.
View composers make it easy to define that every time a particular view or subview loads, it should have certain information available to it. And service injection allows the view itself to request data straight from the application container.
Get Laravel: Up & Running, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.