Actions

J3.x

JLayout Improvements for Joomla!

From Joomla! Documentation

Revision as of 09:47, 15 November 2013 by Tom Hutchison (Talk | contribs)

This article introduces you to the new JLayouts features in Joomla! 3.2.

Most of the changes are focused in improve the flexibility of the system for third party developers.

Contents

JLayout-image.jpg

A bit of History

Let's use an example: you have articles that have tags, but also contacts have tags, but also categories have tags....

In the old template system previous to JLayouts (basically what we still use for views) render that would require to copy paste the same HTML rendering in as many places as you wanted to display your tags. Well, that's not so hard no? Ok but then we have a bug, someone fixes it for content but forgets to do it for categories, and then someone adds a cool feature but only for contacts..... Problems start and the system becomes harder to maintain.

Yannick Gaultier contributed JLayouts to solve this issue. An awesome simple system behind the concept of reusable pieces of code that allows you to render HTML from objects/arrays of data.

Benefits of using layouts

Thanks to the JLayout system you can render the tags of an item with:

echo JLayoutHelper::render('joomla.content.tags', $itemTags);

That will basically echo the output of the file in /layouts/joomla/content/tags.php passing $itemTags as paremeter for internal use. What we were doing in modules but in a standard way. The only requirement is that $itemTags object shares the same structure for all the items.

So what are the benefits of that?

Reusability

We only have to maintain one layout. Designers only have to customise a layout.

Untie data & design

Another of the cool benefits of JLayouts is to become another tool for devs to untie HTML code from PHP code. In an ideal world 100% of the markup in a page should be overridable by a designer/developer without touching Joomla! core. That means that if you load a JS file you should do it in a place that a designer can override it if he decides to not use your JS but replace it with another library. This is actually one of the most important problems we have in Joomla! and the reason why most designers run scared after trying to collaborate for some time.

Easy integration with 2.5.x

All the layout system is just 4 files. You can include them in your library for 2.5 support and/or extend them in your own classes.

Previous JLayout system

With the previous JLayout system a call like this:

$layout = new JLayoutFile('joomla.content.tags');
$layout->render($itemTags);

Would search for the layout in:

[0] => templates/mytemplate/html/layouts
[1] => layouts

Cool! That means that I can override it. But also you could force the folder where you wanted to load layouts with:

$layout = new JLayoutFile('joomla.content.tags', JPATH_SITE . '/components/com_mycomponent/layouts');
$layout->render($itemTags);

That would search layouts in:

[0] => templates/mytemplate/html/layouts
[1] => components/com_mycomponent/layouts

Nice because it's still overridable!

New requirements

With the previous layout system we still have some problems:

  • Developers that want to use layouts inside their components have to specify the path of the layouts in each call or create their own extended classes.
  • What happens if a designer wants to show tags differently in the blog view than in the category view?
  • How can a designer customise the way that he wants to render a field for one specific component?

I faced those problems in my job and that's where I started to improve the system. Some of them had a "hard" solution using the old system but I wanted an easy automatic system that also works for complex solutions.

New features

After my initial proposal Joomla! magic started and people came with more suggestions. The final result is a cool system much better that my initial proposal. The magic of open source.

As a clue of the new improvements this can be sample call to layouts now:

$layout = new JLayoutFile('joomla.content.tags', null, array('debug' => true, 'client' => 1, 'component' => 'com_tags'));

Component layout override

One of the modifications is that now the system automatically searches for layouts in the component loaded.

Now the same call that we used before:

$layout = new JLayoutFile('joomla.content.tags');
$layout->render($itemTags);

Will search automatically for layouts in these folders (ordered by priority):

[0] => templates/mytemplate/html/layouts/com_mycomponent
[1] => components/com_mycomponent/layouts
[2] => templates/mytemplate/html/layouts
[3] => layouts

That means that you can use the standard layouts, override them at component level and override the component override at template level.

In our example a developer can customise the way that tags are shown in his component and a designer can override the way that tags are shown in that component.

Force component

The previous example automatically detects the component when the layout has been called. But what happens if I want to render my tags the same way they are rendered in com_tags? This is also covered with this sample call:

$layout = new JLayoutFile('joomla.content.tags', null, array('component' => 'com_tags'));

Force client

Other of the things that the system now automatically detects is the client from where it's called. That means that if you are in frontend it will search for layouts in frontend.

But I want to render my tags in backend in the same way that com_tags renders them in the frontend! This is covered with this sample call:

$layout = new JLayoutFile('joomla.content.tags', null, array('client' => 0, 'component' => 'com_tags'));

Client parrameter supports these values:

  • 0, 'site' > frontend
  • 1, 'admin' > backend

Adding include paths

Let's say that you end having a custom folder for layouts but you don't want to store all of them. You just want to, for example, add a folder where joomla has to search for layouts and if it doesn't find them load the standard ones. For example in my company we have a library that contains our shared custom layouts for all our components.

This is done with the new call:

$layout = new JLayoutFile('joomla.content.tags');
$layout->addIncludePaths(JPATH_LIBRARIES . '/hacknsa');

That will add /libraries/hacknsa in the top level to search for layouts (maximum priority). This method also supports an array of paths. In an array remember that the latest will have the highest priority.

Suffixes

Another of the proposals (in this case from Robert Deutz) was to be able to specify suffixes of the layout. The original idea was to allow extensions to load search a layout specific active Joomla! version or use the default one. Example:

$layout = new JLayoutFile('joomla.content.tags', null, array('suffixes' => array('j3x', 'j25')));
echo $layout->render($this->item->tags->itemTags);

But that's only one of the uses. Imagine that you need a different layout for RTL (Right To Left) languages. You can add it to all the searches to always look for it in case of active RTL language enabled. Or imagine a customer address whose zip code, is shown in different places/formats depending on the country. You can add a check for a specific layout for the customer country.

Sublayouts

One of the things I found nasty in JLayouts is that you cannot inherit the settings of a layout and use them to render another small part of code without having to specify again all the options. Let's see another example: customisable invoices. I fastly thought in layouts. This way you can have a global call like:

echo JLayoutHelper::render('invoice', $invoiceData);

And then inside that layout something like:

<div class="invoice">
        <div class="customer">
                <?php echo $this->sublayout('shopper', $displayData['shopper']); ?>
        </div>
        <div class="header">
                <?php echo $this->sublayout('header', $displayData); ?>
        </div>
        <div class="products">
                <?php echo $this->sublayout('products', $displayData['products']); ?>
        </div>
        <div class="footer">
                <?php echo $this->sublayout('footer', $displayData); ?>
        </div>
</div>

Which is a main invoice layout calling sublayouts. So users can just override the header of the invoice without dealing with the rest of the system.

When calling a sublayout the systems tries to find a folder called like this layout with the sublayouts inside. In this example we would have a main layout invoice.php and in the same folder a folder called invoice containing the sublayouts (shopper.php, header.php, products.php & footer.php).

Sublayouts will inherit any setting passed to the parent layout. So they search in the same include paths, in the same client, in the same component and for the same suffixies.

Debug mode

When you start dealing with layouts in various include paths, clients, components... You can easily end not knowing where is the system loading the layouts from. This is why I included a UBU (Useful But Ugly :D ) debug system. To enable it you only have to pass the option debug to true like

$layout = new JLayoutFile('joomla.content.tags', null, array('suffixes' => array('j3x', 'j25'), 'debug' => true));
echo $layout->render($this->item->tags->itemTags);

You will see something like:

JLayout new debug mode

Where do not use layouts?

Layouts are a cool thing when used properly but a terrible thing when used in the wrong places. And now everybody wants to convert everything to layouts. This is why you can easily find layouts that are used by articles, banners, contacts, clients, weblinks, newsfeeds. You would say "but that's cool no? We are saving code!". No! We are just creating a single layout with tons of if statements inside it to deal with all the differences between components.

In my opinion there are no benefits in using a layout to render 4 fields. That's something that has to be done in the template and is not going to be ever reused outside the core. And that's my advice: if it can be reused outside the core then it makes sense as layout. Examples of layouts: render a list, a button, a menu, a field, pagination.... Another good advice: if it cannot be overriden is better in a layout.

Layouts are not the solution for everything.

Other uses

Imagine the current jQuery loading in core. This is done from JHtml classes. Why not make JHtml search for a overrides automatically in the component folder? So I can have my own custom jQuery version tested with the other libraries in my component. Yes, it can conflict with other module requirements but isn't that what we already have? At least we will avoid loading jQuery multiple times. This is not really a task for layouts but to apply the same concept to JHtml.

Now imagine that top menu contains a bug on a button or in something that is inside a layout. You can fix it inside a layout override on your own component and wait until the core fixes it. Same if you have a library that conflicts with core. Lot of support saved explaining that the issue is a core bug.

This are only some of the ideas behind the concept.

Conclusion

Layouts are a really powerful tool and is one of those things that can help us to improve the relations between coders vs desginers. There are a lot of places to use layouts. Some of them still not discovered, some of them waiting there for someone to do the job. Do you want to help us?