Zerillian Engineering

Irradiated with cheeky programmer fervor!

Blade Extensions in Laravel 4

| Comments

Laravel 4 is here, and it’s a thing of beauty. Among all the remarkable new features remains the ability to extend the Blade template system with your own statements and functionality. I previously wrote about how to do it in Laravel 3, and thankfully it is equally straightforward in Laravel 4. There’s even a bit more explanation this time around. So here it is, Blade Extensions for every artisan’s favorite framework.

Blade has a nice ‘n easy syntax for template creation that works by substituting PHP for the Blade statements when a view is rendered. Making a Blade extension means defining a closure function that does this substitution and telling Laravel to run it on the stack of Blade compilers already in place.

This is done like so:

Blade::extend(function($view) {
    // Statement code...
});

Just like Laravel 3. After calling Blade::extend(), the closure you define will automatically be run for every view. The $view variable contains the plain text contents of the view to be rendered. The rendering takes place on this raw data, passing the view out to the PHP parser after all the Blade statements have been evaluated.

You can plop Blade extensions in your routes.php or filters.php file or place them in a separate file to be included via the auto-loader or another mechanism you choose. They just have to be somewhere, since Laravel uses its autoloader and IoC to resolve the Blade class anywhere.

Take a peek at the class and study it a little. You’ll notice some patterns, which you’ll be replicating in your Blade extensions. If you take time to observe how they work and read up on regular expressions, I won’t have to explain them to you.

Almost all of my view layouts have a @yield('title') statement in the document head. The title of the page is set by the view that extends the layout. Normally, that would look like this:

@section('title')
Page Title
@stop

It would be nicer this way, I think:

@title('Page Title')

The extension looks a little intimidating, but it really isn’t. It’s just a wrapper for a standard find-and-replace function (preg_replace()) that swaps the custom Blade tag with some raw PHP.

Update: It’s been pointed out to me that there’s an existing function to create matchers for PHP swaps like this. Look at the createMatcher docs.

1
2
3
4
5
6
<?php
Blade::extend(function($view) {
  $pattern = '/@title\(\s?[\'\"](.*)[\'\"]\s?\)/';
  $replacement = "<?php $__env->startSection('title');?>$1<?php $__env->stopSection();?>";
  return preg_replace($pattern, $replacement, $view);
});

Laravel has a variable to refer to its environment/execution context. That’s $__env. The methods startSection() and stopSection() do pretty much what you’d expect. So when making a Blade template that extends a layout, such as this:

title.blade.php
1
2
3
4
5
@extends('master')

@section('title')
Good Site, Home of the Good Site.
@stop

You end up with a cached view that looks like this:

title.blade.php (Rendered)
1
2
3
4
5
6
7
<?php $__env->startSection('title'); ?>

Good Site, Home of the Good Site.

<?php $__env->stopSection(); ?>

<?php echo $__env->make('main', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>

Using the extension defined previously, this same output is produced. Don’t fret over the last line. You can probably guess what it does. Just remember that $__env->startSection() and $__env->stopSection() are what stake out a certain chunk of content. If you sniff around in the BladeCompiler API, you can find the PHP that backs up the stock Blade statements.

This is just the beginning. If you want to see a markup extension, take a look at my last article. Add in functional PHP to your custom Blade tags if you want. As long as you can regex-match and substitute it, the sky’s the limit. Be mindful of the number of extensions you add, since every view rendered has to be passed through them all at least once. The overhead isn’t intrinsically huge, but be careful anyway. And be thankful that Laravel does you a favor caching things.

Full disclosure: I’ve yet to fully plunge the depths of Laravel 4, so this article may contain small inaccuracies. I’ve tested the code semi-scientifically and it works as expected. If it turns out I’m full of shit, leave a note or send a message to armand(at)zerilliworks(dot)net.

Comments