Templating and Icons in CakePHP

There is a new plugin on the block: Templating
Check it out.

PS: This is a direct successor of www.dereuromark.de/2022/12/19/font-icons-in-cakephp-apps.
So all of the functionality around icons is directly ported, as well. Please read that article for some history on the motivation and benefits of the Icon helper.

I want to quickly showcase some of the new shiny things it ships with here.

HtmlStringable interface

This interface can be used for any HTML (stringish) template your helpers and template elements generate.

use Templating\View\HtmlStringable;

class SvgGraph implements HtmlStringable { ... }

// in your templates
$icon = new SvgIcon($name);
$this->Html->link($icon, '/my/url');

Use can also use the Html value object directly:

use Templating\View\Html;

$html = Html::create('<i>text</i>');
$this->Html->link($html, '/my/url');

The neat part about this when using it in combination with such value objects:
No more 'escapeTitle' overhead needed.

You can use the helpers shipped with this plugin, you can add the traits yourself to your helpers or just write your own
3-liner for it.

// in your AppView::initialize()
$this->addHelper('Templating.Html');
$this->addHelper('Templating.Form');

Before:

$icon = $this->Icon->render('delete');

$this->Form->postLink($icon, ['action' => 'delete', $id], ['escapeTitle' => false]);

After:

$icon = $this->Icon->render('delete');

$this->Form->postLink($icon, ['action' => 'delete', $id]);

Note that when using declare(strict_types=1); you need to manually cast when passing this to methods that only accept string:

$icon = new SvgIcon($name);
// CustomHelper::display(string $html) does not accept Stringable
$this->Custom->display((string)$icon);

When not using strict_types this is optional.

It is recommended to adjust this helper and method on project level then, adding the interface into the signature
as done for Html and Form helpers.

public function display(string|HtmlStringable $icon, array $options = []): string {
    if ($icon instanceof HtmlStringable) {
        $options['escapeTitle'] = false;
        $icon = (string)$icon;
    }

    return parent::display($icon, $options);
}

When not using PHP templating, but e.g. Twig or alike, you can also easily write your own helper methods there that would internally do this and accept those stringable icons.

(Font) icons

The plugin ships with a helper to handle most common font icons and contains useful convenience wrappers.
On top of the former way using plain strings (see linked article and Tools plugin) it now provides the icons as value objects (using HtmlStringable interface).

The main advantages have been outlined above. This aims to provide a very convenient way of using (font) icons now in your templates.

Setup

// in your AppView::initialize()
$this->addHelper('Templating.Icon');

Make sure to set up at least one icon set:

  • Bootstrap: npm package bootstrap-icons
  • FontAwesome v4/v5/v6: npm package fontawesome-free for v6
  • Material: npm package material-symbols
  • Feather: npm package feather-icons

Or add your custom Icon class.

The icon set config you want to use can be passed to the helper – or stored as default config in Configure key 'Icon' (recommended).
You can add as many icon sets as you want.

E.g.

'Icon' => [
    'sets' => [
        'bs' => \Templating\View\Icon\BootstrapIcon::class,
        ...
    ],
],

For some Icon classes, there is additional configuration available:

  • namespace: Some fonts offer different traits (light, bold, round, …)

In this case make sure to use an array instead of just the class string:

'Icon' => [
    'sets' => [
        'material' => [
            'class' => \Templating\View\Icon\MaterialIcon::class,
            'namespace' => 'material-symbols-round',
        ],
        ...
    ],
],

Don’t forget to also set up the necessary stylesheets (CSS files), fonts and alike for each active set.

Usage

render()

Display font icons using the default namespace or an already prefixed one.

echo $this->Html->link(
    $this->Icon->render('view', $options, $attributes),
    $url,
);

Especially if you have multiple icon sets defined, any icon set after the first one would require prefixing:

echo $this->Html->link(
    $this->Icon->render('bs:view', $options, $attributes),
    $url,
);

You can alias them via Configure for more usability:

// In app.php
'Icon' => [
    'map' => [
        'view' => 'bs:eye',
        'translate' => 'fas:language',
        ...
    ],
],

// in the template
echo $this->Icon->render('translate', [], ['title' => 'Translate this']);

This way you can also rename icons (and map them in any custom way).

I personally like to rename them to a more speaking way to the action they are supposed to show.
So I use e.g.

    'details' => 'fas:chevron-right',
    'yes' => 'fas:check',
    'no' => 'fas:times',
    'prev' => 'fas:arrow-left',
    'next' => 'fas:arrow-right',
    'translate' => 'fas:language',
    ...

They are then way easier to remember than the actual (cryptic) icon image/name.

names()

You can get a nested list of all configured and available icons.

For this make sure to set up the path config to the icon meta files as per each collector.
E.g.:

'Icon' => [
    // For being able to parse the available icons
    'sets' => [
        'fa' => [
            ...
            'path' => '/path/to/font-awesome/less/variables.less',
        ],
        'bs' => [
            ...
            'path' => '/path/to/bootstrap-icons/font/bootstrap-icons.json',
        ],
        'feather' => [
            ...
            'path' => '/path/to/feather-icons/dist/icons.json',
        ],
        'material' => [
            ...
            'path' => '/path/to/material-symbols/index.d.ts',
        ],
        ...
    ],
],

You can then use this to iterate over all of them for display:

$icons = $this->Icon->names();
foreach ($icons as $iconSet => $list) {
    foreach ($list as $icon) {
        ...
    }
}

Configuration

You can enable checkExistence to ensure each icon exists or otherwise throws a warning in logs:

'Icon' => [
    'checkExistence' => true,
    ...
],

Auto-complete

Now for the most powerful feature and probably most helpful one:
Let your IDE (e.g. PHPStorm) provide you the available icons when you type $this->Icon->render( and quick-select from the dropdown list.

In order for this to work you just need to add the IconRenderTask shipped with this plugin and you are all set.

    'IdeHelper' => [
        ...
        'generatorTasks' => [
            \Templating\Generator\Task\IconRenderTask::class,
        ],

Demo

See the examples in the sandbox. The source code can be opened via GitHub.
Real life examples you can also run locally by spinning up the sandbox in a local docker container.

4.00 avg. rating (83% score) - 1 vote

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.