JLayout Improvements for Joomla!
From Joomla! Documentation
This article introduces you to the new JLayouts features in Joomla! 3.2.
Most of the changes are focused on improving the flexibility of the system for third party developers.
A Bit of History[edit]
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 (what we still use for views) rendering that would require you to copy and paste the same HTML rendering in as many places as you wanted to display your tags. That's not so hard is it? Okay 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... The system becomes difficult 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[edit]
Thanks to the JLayout system you can render the tags of an item with:
echo JLayoutHelper::render('joomla.content.tags', $itemTags);
That will echo the output of the file in /layouts/joomla/content/tags.php passing $itemTags as parameter 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[edit]
We only have to maintain one layout. Designers only have to customise a layout.
Untie Data and Design[edit]
Another of the benefits of JLayouts is to become another tool for developers 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 the Joomla! core. If you load a JavaScript file you should do it in a place that a designer can override it if they decide to not use your JavaScript but replace it with another library. This is one of the most important problems we have in Joomla! and the reason most designers run scared after trying to collaborate for some time.
Easy Integration with 2.5.x[edit]
All the layout system is just four files. You can include them in your library for 2.5 support and/or extend them in your own classes.
Previous JLayout System[edit]
With the previous JLayout system a call like:
$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[edit]
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 they want 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[edit]
After my initial proposal Joomla! magic started and people came with more suggestions. The final result is a system much better that my initial proposal. The magic of open source.
As a clue of the new improvements this is a sample call to layouts now:
$layout = new JLayoutFile('joomla.content.tags', null, array('debug' => true, 'client' => 1, 'component' => 'com_tags'));
Component Layout Override[edit]
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[edit]
The previous example automatically detects the component when the layout has been called. 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[edit]
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 parameter supports these values:
- 0, 'site' > frontend
- 1, 'admin' > backend
Adding Include Paths[edit]
Let's say that you have a custom folder for layouts but 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[edit]
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[edit]
One of the things I found bad 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 all the options again. Let's see another example: customisable invoices. This way you can have a global call:
echo JLayoutHelper::render('invoice', $invoiceData);
And then inside the layout:
<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. 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 layout folder 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 and footer.php).
Sublayouts will inherit any setting passed to the parent layout. They search in the same include paths, in the same client, in the same component and for the same suffixes.
Debug Mode[edit]
When you start dealing with layouts in various include paths, clients, components, you can easily end not knowing where the system is loading the layouts from. This is why I included a UBU (Useful But Ugly) debug system. To enable it you only have to pass the debug option as true:
$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:
Where Not To Use Layouts?[edit]
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 four fields. That's something that has to be done in the template and is not going to ever be reused outside the core. 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 piece of advice: if it cannot be overridden it is better in a layout.
Layouts are not the solution for everything.
Other Uses[edit]
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? 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[edit]
Layouts are a really powerful tool and is one of those things that can help us to improve the relations between coders vs designers. 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?