Skip to content
Nicholas K. Dionysopoulos edited this page Nov 20, 2018 · 14 revisions

Blade Templating

Blade templating is a direct adaptation of Laravel's Blade templating. Blade is a simple and powerful templating engine driven by view template inheritance and sections. All Blade templates use the .blade.php extension. Modern IDEs, such as phpStorm, will recognise them and provide syntax highlighting support. You need to do some overrides in phpStorm; see the last section of this page.

All Blade view templates are parsed at runtime and converted into regular PHP files which are then loaded by your Views like any other regular PHP view template. This means that if Blade doesn't provide a control structure you need you can simply switch to pure PHP in your Blade template. Remember that $this always has the reference to your View object which is a View or DataViewInterface descendant.

Defining a Blade template

A Blade template looks like this:

<!-- Stored in administrator/components/com_foobar/View/something/tmpl/item.blade.php -->

@section('header')
    <p>This is the master header</p>
@show

<div class="foobar-item">
@yield('item')
</div>

Naming and location of the files follows the same conventions as any other FOF view template.

Using a Blade template

<!-- Stored in administrator/components/com_foobar/View/something/tmpl/item_default.blade.php -->

@extends('admin:com_foobar/something/item')

@section('header')
    <p>This is the appended to the master header.</p>
@stop

@section('item')
    <p>This is the content that goes inside the foobar-item div.</p>
@stop

Blade view templates override sections from the template(s) they are extending. The content of any view template can be included in another view template using the @yield directive in a section. This allows you to separate your layout into many distinct areas and have each view template override only some of them.

You can to pass default content to the @yield directive. For example

@yield('item', 'This is my default content')

will display "This is my default content" if the "item" section has not been defined.

Blade control structures

Blade has a lot of directives which allow you to define control structures in a way that's understandable by both back-end and front-end developers.

Echoing data

If you want to escape the data you are echoing use triple curly braces. If you want to display the data unescaped use double curly braces.

<h1>{{{ $this->item->title }}}</h1>
<div>
Posted by {{{ $this->item->user->username }}} on {{{ $this->item->created_on }}} 
</div>
<div>
{{ $this->item->body }}
</div>

Use of double curly braces (unescaped echo) is a bad idea. You should only use it when displaying HTML data and only after making sure you have sanitised it properly. If unsure, use triple curly braces.

If you need to display a string which begins and ends with curly braces prefix your text with an @ sign. For example:

@{{ This line is not parsed by Blade }}

Default text when echoed item doesn't exist

Blade has a shorthand for displaying a default text when the variable you want to display doesn't exist. For example:

<p>{{{ $this->item->name or 'Anonymous Coward' }}}

Conditionals (if-blocks)

You have the complete if-elseif-else-endif structure just like PHP. You also have the shorthand unless-endunless construct which renders if a condition is false.

@if ($this->permissions['editown'] && $this->item->created_by == \JFactory::getUser()->id)
    You can edit this item; you own it.
@elseif ($this->permissions['edit'])
    You can edit this item; you have edit rights
@else
    You can't edit this item
@endif

@unless ($this->permissions['foobar'])
    Go away, you don't have the permission to be here!
@endunless

For Loops

Just like PHP.

@for ($i = 0; $i < 10; $i++)
    The value of i is {{ $i }}
@endfor

ForEach Loops

You can iterate a list just like PHP with the foreach-endforeach construct.

@foreach ($this->items as $item)
    This is item {{ $item->foobar_item_id }}
@endforeach

If you want to display a different message if the list is empty use the forelse-empty-endforeach construct.

@forelse ($this->items as $item)
    This is item {{ $item->foobar_item_id }}
@empty
    <p>No items found</p>
@endforelse

While Loops

Loop while a condition is true.

@while(true)
    <p>Infinite loop</p>
@endwhile

Load subtemplates

@include('admin:com_foobar/something/item_user')

You can also pass additional data to the subtemplate as an array:

@include('admin:com_foobar/something/item_user', array('foo' => 'bar'))

In this case the subtemplate will have the variable $foo defined with a value of "bar".

Load JLayout view templates

Since FOF 3.1.1 you can do

@jlayout('path.to.your.jlayout', ['option1' => $something, 'option2' => 'something else'])

This equivalent to echo FOF30\Layout\LayoutHelper::render($this->container, 'path.to.your.jlayout', ['option1' => $something, 'option2' => 'something else'])

HMVC

There is no Blade shortcut. You will have to do an escape to PHP:

<?php Container::getInstance('com_other', array(
    'tempInstance' => true,
    'input' => array(
        'view' => 'something',
        'task' => 'whatever',
        'other_something_id' => $this->item->other_something_id
    )))->dispatcher->dispatch(); ?>

Each loops for sub-templates

When you have complicated data structures you may want to use a subtemplate (a different view template file) to render each item in your list. You can use the each construct:

@each('admin:com_foobar/something/item_user', $this->item->users, 'user', 'raw|No users found')

The four parameters are:

  • The view template URI
  • The list to iterate through
  • The name of the variable holding each item, passed to the subtemplate.
  • What to display when the list is empty. It can be a template URI, a raw string prefixed with raw| or (from FOF 3.1.1 and later) a language string prefixed with text|.

In the subtemplate use the PHP variables $key and $something (where "something" is the third parameter above) to access the key and value of each iterated item.

Do note that this is not the only way. This construct is roughly equivalent to:

@forelse($this->item->users as $key => $value)
    @include('admin:com_foobar/something/item_user', array('key' => $key, 'user' => $value)
@empty
    No users found
@endforelse

Using @each is simply a bit more elegant.

Overwriting sections

To overwrite (replace the contents) of a section use the @override directive to end your section instead of @stop:

@extends('admin:com_foobar/something/item')

@section('header')
    This replaces the header section
@overwrite

Repeatable sections

IMPORTANT: Repeatable sections only work on PHP 5.4.0 and later.

@repeatable('kot', $lamb)
    <p>
    @if ($lamb == 1)
    Mary had a little lamb.
    @else
    Mary had {{{ $lamb }}} little lambs.
    @endif
    </p>
@endRepeatable

@for ($lamb = 1; $lamb < 11; $lamb++)
    @yieldRepeatable('kot', $lamb)
@endfor

@repeatable takes one or more arguments. The first is the name of the repeatable section, the rest of the arguments are the variable names which will be made known to it.

@endRepeatable closes the repeatable section. You must match the @repeatable and @endRepeatable tags.

@yieldRepeatable parses the repeatable section and yields its contents. The first argument is the repeatable section's name, the rest are the arguments passed to the repeatable section.

Repeatable sections are implemented as anonymous functions. The variable names are passed to the argument list of the anonymous function definition.

Language strings

@lang('COM_FOOBAR_SOME_LANGUAGE_STRING')

@sprintf('COM_FOOBAR_YOU_HAVE_X_ITEMS', count($this->items))

@plural('COM_FOOBAR_N_ITEMS_SAVED', count($this->items))

Note: @plural is available since FOF 3.1.1 and maps to JText::plural().

Routing URLs

Instead of calling JRoute directory you can use the @route shorthand

<a href="@route('index.php?option=com_foobar&view=items')">Items</a>

Adding CSS and Javascript

@css('media:com_example/css/my.css')

@inlineCss('.foo {display: block}')

@js('media:com_example/js/my.js')

@inlineJs("alert('FOF Rocks!')")

@less('media:com_example/less/my.less', 'media:com_example/css/my.css')

These map to the View's addJavascriptFile, addJavascriptInline, addCssFile, addCssInline and addLess methods. See the View class for more information about the methods' parameters.

Using JHtml

@jhtml('calendar', $date, 'myFieldName', 'myFieldId')

This is equivalent to echo JHtml::_('calendar', $date, 'myFieldName', 'myFieldId');

Paths to media files

<img src="@media('media:com_example/images/my.png')" />

Media paths go through Template::parsePath and support media file overrides in the current template.

Including modules

// Show active modules assigned to the 'myModulePosition' position
@modules('myModulePosition');

// Show named module
@module('mod_example');

Comments

{{-- This is like a PHP comment; it will not be present in the HTML output --}}

Extending Blade

Just like Laravel's Blade, you can extend FOF's Blade compiler to define your own custom directives. When the Blade compiler is parsing a template it will call all extensions against its output. This lets you do anything from a simple search and replace to complex output manipulation.

There are two methods you can use to register your Blade extensions, createMatcher and createPlainMatcher. These generate the expressions to handle your custom directives.

The createPlainMatcher directive is used to define directives with no arguments, whereas createMatcher lets you define directives which take arguments.

Example:

$container->blade->extend(function($template, $compiler) {
    $pattern = $compiler->createMatcher('datetime'); // Defines an @datetime directive with one argument
    
    return preg_replace($pattern, '$1<?php echo $2->format(\'d/m/Y H:i:s\'); ?>', $template);
});

Loading sub-templates

Just like the classic PHP view templates, you can use loadAnyTemplate to load subtemplates.

Example

<?php $this->loadAnyTemplate('auto:com_example/Item/default_price'); ?>

You must NOT specify the extension of the view template to load. FOF will automatically look for both classic PHP and Blade templates.

Form tokens

Since FOF 3.0.10 you can insert the CSRF protection form token using a Blade shortcut:

<input type="hidden" name="@token()" value="1"/>

In previous versions of FOF you can do the same with

<input type="hidden" name="{{JFactory::getSession()->getFormToken()}}" value="1"/>

WYSIWYG editor

Since FOF 3.0.10 you can render Joomla!'s WYSIWYG editor with

@editor($fieldName, $htmlData, $width, $height, $columns, $rows, $buttons, $ids, $asset, $author, $params)

Internally this does

JEditor::getInstance($this->container->platform->getConfig()->get('editor', 'tinymce'))
    ->display($fieldName, $htmlData, $width, $height, $columns, $rows, $buttons, $ids, $asset, $author, $params);

Do note that since Joomla! 3.2 you are not supposed to call JFactory::getEditor() any more as it is deprecated!

Setting up phpStorm for FOF's Blade templates

phpStorm comes with a default mapping for Blade templates suitable for Laravel. When working with a FOF project you will need to tell phpStorm how our Blade templates work to let it provide correct type hints. Go to Preferences, Languages & Frameworks, PHP, Blade and click on the Directives tab. You need to set up the following directives:

Name Has Parameter Prefix Suffix
append No
css Yes <?php echo $this->addCssFile( ); ?>
each Yes <?php echo $this->renderEach( ); ?>
else No
elseif Yes <?php elseif( ); ?>
empty Yes <?php if(empty( )): ?>
endfor No
endforeach No
endforelse No
endif No
endpush No
endrepeatable No
endunless No
endwhile No
extends Yes <?php echo $this->loadAnytemplate( ); ?>
fieldtitle Yes <?php echo FOF30\Utils\FEFHelper\BrowseView::fieldLabel( ); ?>
for Yes <?php for( ); ?>>
foreach Yes <?php foreach( ); ?>
forelse Yes <?php foreach( ); ?>
if Yes <?php foreach( ); ?>
include Yes <?php echo $this->loadAnytemplate( ); ?>
inlineCss Yes <?php $this->addCssInline( ); ?>
inlineJs Yes <?php $this->addJavascriptInline( ); ?>
jhtml Yes <?php echo \JHtml::_( ); ?>
jlayout Yes <?php echo \FOF30\Layout\LayoutHelper::render($this->container, ); ?>
js Yes <?php echo $this->addJavascriptFile( ); ?>
lang Yes <?php echo \JText::_( ); ?>
less Yes <?php echo $this->addLessFile( ); ?>
media Yes <?php echo $this->container->template->parsePath( ); ?>
modelfilter Yes <?php echo \FOF30\Utils\FEFHelper\BrowseView::modelFilter( ); ?>
module Yes <?php echo $this->container->template->loadModule( ); ?>
modules Yes <?php echo $this->container->template->loadPosition( ); ?>
overwrite No
plural Yes <?php echo \JText::plural( ); ?>
push Yes <?php $this->startSection( ); ?>
repeatable Yes <?php // ?>
route Yes <?php echo $this->container->template->route( ); ?>
searchfilter Yes <?php echo \FOF30\Utils\FEFHelper\BrowseView::searchFilter( ); ?>
section Yes <?php $this->startSection( ); ?>
selectfilter Yes <?php echo \FOF30\Utils\FEFHelper\BrowseView::selectFilter( ); ?>
show No
sortgrid Yes <?php echo \FOF30\Utils\FEFHelper\BrowseView::sortGrid( ); ?>
sprintf Yes <?php echo \JText::sprintf( ); ?>
stack Yes <?php echo $this->yieldContent( ); ?>
stop No
token Yes <?php \JFactory::getSession()->getFormToken(( ); ?>
unless Yes <?php if ( ! ( )): ?>
while Yes <?php while ( ): ?>
yield Yes <?php $this->yieldContent( ); ?>
yieldRepeatable Yes <?php _fof_blade_repeatable_DUMMY( ); ?>

Please note that you should not keep any of the other default directives. They do not apply to FOF.

Clone this wiki locally