J3.x

Sharing layouts across views or extensions with JLayout

From Joomla! Documentation

Revision as of 23:18, 21 September 2023 by Cmb (talk | contribs) (Several markup changes.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Other languages:
English • ‎español • ‎français
Joomla! 
3.x
series

It is common that parts of pages are replicated across several views inside an extension, or even across several extensions. Examples might be:

  • An extension sharing some display layouts between Frontend and Backend views, or with one or more modules
  • Backend extensions that have some common settings, and thus have to replicate common display layouts to allow users to change them

Up until Joomla! 3.0, the loading (and template overriding) of layout files was restricted to a given view. The JLayout interface and set of classes was added to Joomla! 3.0 to help solve this very problem. It encapsulates a layout and the data required to display it so that they can be reused across views and extensions. At the time of writing, JLayout is used by the JHtmlSidebar class to display the submenu and filters found in most Backend extensions pages.

JLayout consists in an interface and two classes:

  • JLayout interface defines escape and render methods, much like the JView class of Joomla! platform
  • JLayoutBase implements a basic layout class, where layout may be hard-coded in the class itself, for instance
  • JLayoutFile, the most commonly used class, wraps and render a layout stored in a file, checking for overrides in template before doing so.

Here is a basic usage example of JLayoutFile:

// Create a layout object and ask it to render the sidebar
$layout      = new JLayoutFile('joomla.sidebars.submenu', $basePath = null);
$sidebarHtml = $layout->render($data);

Executing this code creates a JLayoutFile object, wrapping a layout file, and passing in a $data structure that may be required for the display itself.

The first parameter, 'joomla.sidebars.submenu' is the file identifier. The last part ('submenu')is the file name, everything before ('joomla.sidebars') is the relative path. An optional $basePath parameter can be prepended to the relative path. If $basePath is missing, layout files will be searched inside the /layouts directory.

Our example:

new JLayoutFile('joomla.sidebars.submenu', $basePath = null)

will result in the 'submenu.php' layout file being loaded from the '/layouts/joomla/sidebars' directory.

Passing in a non-empty base path allows accessing and storing layout files anywhere on the site directory structure, for instance:

$layout = new JLayoutFile('my_layout', JPATH_ROOT .'/components/com_something/layouts');
$html = $layout->render($data);

will load and render the 'my_layout.php' layout file found in JPATH_ROOT .'/components/com_something/layouts' directory.

Setting the client parameter as 3rd parameter allows loading layout files from the frontend directory also in the backed, for instance:

$layout = new JLayoutFile('my_layout', NULL, array('client' => 0));
$html = $layout->render($data);

will load and render the 'my_layout.php' layout file found in JPATH_ROOT .'/components/com_something/layouts' directory but could still be overridden in the template.

Template Overrides

When executing the render() method, that is actually loading the layout file, JLayoutFile will check for the existence of an override in the currently selected template, inside a 'layouts' directory. Looking back at our initial example, should you want to override the sidebar layout of all backend Joomla! extensions, you should place a 'submenu.php' file under: /administrator/templates/{currently_selected_template}/html/layouts/joomla/sidebars/

Note: JLayoutFile will check the currently selected template for overrides. As layouts can be shared across both the Frontend and Backend, if you need to override a layout file in both cases, you'll have to put an override file in both Backend and Frontend template.

Storing Layouts

Layouts files can basically be stored anywhere, as a full base path can be specified when using them (and still allow template override). However, for consistency and to avoid name clashes, we would recommend the following:

  • default to storing layout files in your extension own admin layouts folder. For instance: /administrator/components/com_example/layouts
  • if your extension doesn't have an administrative folder (front-end only component, module, plugin), then use a layouts folder such as:
    • /components/com_example/layouts
    • /plugins/content/example/layouts
    • /modules/mod_example/layouts

In addition, note that the folder itself must be named layouts. Using the root /layouts folder is normally reserved for Joomla! itself, and possibly other "official" applications in the future such as the installer. Lastly, though not mandatory, I advise you to put all layout files under an additional com_example or mod_example subfolder inside the main layouts folder. This serves no purpose for your extension, however, should a template override layouts for several extensions, this would avoid possible name collisions. For instance, if two extensions use the "filters.search_all" JLayout, then a template cannot provide a separate override for each. A single override will be used for both extension, which may not be desirable. Using "com_example_1.filters.search_all" and "com_example_2.filters.search_all" allows templates to provide overrides for layouts, even if they have the same id.

Examples

Here is a simple example.

Create a file, /layouts/joomla/content/helloworld.php:

<?php
defined('JPATH_BASE') or die;

?>
<div id="helloworld">
  <h1>Hello World!</h1>
</div>

Then in any layout file, such as components/com_content/views/article/tmpl/default.php add:

<?php
$layout      = new JLayoutFile('joomla.content.helloworld');
echo $layout->render();
 ?>

View an article on the front end and you will see Hello World!. Of course often you will want to pass some data via the render() method.

A simple example of that might be:

<?php
defined('JPATH_BASE') or die;

?>
<div id="helloworld">
  <h1>Hello <?php echo $displayData['name']; ?>!</h1>
</div>

(Note that the variable passed in to the layout is called $displayData)

With the corresponding PHP

<?php
$layout      = new JLayoutFile('joomla.content.helloworld');
$data = array('name' => 'Bob');
echo $layout->render($data);
 ?>

Now when you view your article you should see Hello Bob!

For additional information, you can also read this article on the Joomla! Community Magazine™: JLayout Improvements for Joomla! 3.2 by Roberto Segura.