Work In Progress

This book is currently work in progress. Some sections are not yet written. Thank you for your understanding!

HTML helper service

Joomla has long provided a mechanism for creating your own HTML helper classes. These are meant to provide relatively short methods which return HTML snippets based on data which requires more than a line or two of PHP code to be processed and/or is called frequently enough throughout your component that it makes no sense having to the same thing over and over again. For example, you could have methods which return item associations, format dates, load a layout for presenting user information in backend grid views etc.

Joomla 4 continues to offer this possibility and further enhances it. Your key take-aways are:

  • Joomla 4 HTML helpers are services registered through your extension class instead of pure static classes which had to be registered with \Joomla\CMS\HTML\HTMLHelper::addIncludePath.

  • Joomla 4 HTML helpers have non-static methods whereas Joomla 3 HTML helpers had static methods.

  • In Joomla 4 you can register whichever prefix you want, not necessarily the same as the name of the class(es) providing your HTML helper service(s).

  • You still call your HTML helpers through the \Joomla\CMS\HTML\HTMLHelper::_() method.

How we did things in Joomla 3

Back in Joomla 3 we'd create a class file in our helpers/html folder, e.g. administrator/components/helpers/html/example.php. It would contain a pure static class with a name starting with JHtml, e.g JHtmlExample. Here's a trivial example:

<?php
class JHtmlExample {
  public static function hello($who) {
    $who = htmlspecialchars($who);
    
    return "<p>Hello, {$who}!</p>";
  }
}

Within our component the helper was automatically registered and we could use it like this:

<?php echo \Joomla\CMS\HTML\HTMLHelper::_('example.hello', 'world') ?>

This would result in

<p>Hello, world!</p>

Outside our component we'd have to remember to register this helper manually:

\Joomla\CMS\HTML\HTMLHelper::addIncludePath(JPATH_ADMINISTRATOR .
  '/components/com_example/helpers/html');

When we forgot to do that, e.g. in a module of ours? Hilarity ensued…

This approach also had other problems. What if there are two helpers, one of ours and one of another component, using the same class name? PHP Fatal Error! What happens if we use a name for our helper which “shades” a name used by Joomla itself? We break things! What happens if the service we need to use is not available as a static method? Ugly workarounds! Generally speaking, it was a bad idea but, like most things in older versions of Joomla, was better than nothing which made it good enough.

The way to do it in Joomla 4

In Joomla 4 we will create a service. It sounds big and scary but... let me tell you a secret. We are just going to create a regular PHP class. It does not extend from anything, it does not have any special requirements. That's right, we are writing the simplest possible PHP class.

<?php
namespace Acme\Component\Example\Administrator\Service\Html;

class Example {
  public function hello($who) {
    $who = htmlspecialchars($who);

    return "<p>Hello, {$who}!</p>";
  }
}

Now we can instantiate it in our component's extension class's boot method.

<?php
namespace Acme\Component\Example\Administrator\Extension;

use Acme\Component\Example\Administrator\Service\Html\Example;

class Example extends MVCComponent implements BootableExtensionInterface {

  public function boot(ContainerInterface $container)
  {
    $this->getRegistry()->register('example', new Example());
  }
}

Pushing services

In the Joomla 3 paradigm the HTML helper class was static, meaning that any services it needs would have to be fetched with static or otherwise global calls as well. For example, getting the database driver object would require doing \Joomla\CMS\Factory::getDbo(). In Joomla 4 this kind of static calls is deprecated. So, how do you do it?

The trick is that you can push services using the HTML helper class's constructor. For example, pushing the database:

<?php
namespace Acme\Component\Example\Administrator\Service\Html;

class Example {
  private $db;

  public function __construct(\Joomla\Database\DatabaseDriver $db) {
    $this->db = $db;
  }

  public function hello($who) {
    $who = htmlspecialchars($who);

    return "<p>Hello, {$who}!</p>";
  }
}

We also need to change the initialisation of our HTML service:

<?php
namespace Acme\Component\Example\Administrator\Extension;

use Acme\Component\Example\Administrator\Service\Html\Example;

class Example extends MVCComponent implements BootableExtensionInterface {
  public function boot(ContainerInterface $container)
  {
    $db = $container->get('DatabaseDriver');
    $this->getRegistry()->register('example', new Example($db));
  }
}

You can push any service, including your component's MVCFactory service which means that you can get instances of any Model of your component to retrieve database data in your HTML helper. Or, maybe, Joomla's UserFactory service so you can create instances of any user given their ID or username to get and present information about them. You get the idea!