Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Custom view helpers

The built-in view helpers — url(), escapeHtml(), partial() — cover common tasks. When your own output logic repeats across view scripts — shorten a text, format a date, show a price — it is moved into a helper of your own. It is a class called in a view script just like the built-in ones: $this->excerpt($text).

The helper class

A module's helpers go into module/<Module>/src/View/Helper. Create the folders and the file by hand:

// module/Blog/src/View/Helper/Excerpt.php
namespace Blog\View\Helper;
 
use Laminas\View\Helper\AbstractHelper;
 
class Excerpt extends AbstractHelper
{
    public function __invoke(string $text, int $length = 100): string
    {
        if (mb_strlen($text) <= $length) {
            return $text;
        }
 
        return mb_substr($text, 0, $length) . '…';
    }
}

The class extends AbstractHelper, and the __invoke() method runs when the helper is called. This helper shortens a long text to a given number of characters.

Registration

Helpers are described in the view_helpers section of module/Blog/config/module.config.php:

// module/Blog/config/module.config.php
'view_helpers' => [
    'factories' => [
        View\Helper\Excerpt::class => InvokableFactory::class,
    ],
    'aliases' => [
        'excerpt' => View\Helper\Excerpt::class,
    ],
],

It works like controller plugins: factories creates the class, aliases sets the name used in view scripts.

Calling it in a view script

<!-- module/Blog/view/blog/post/index.phtml -->
<ul>
    <?php foreach ($this->posts as $post): ?>
        <li>
            <strong><?= $this->escapeHtml($post->title) ?></strong>
            <p><?= $this->escapeHtml($this->excerpt($post->body, 80)) ?></p>
        </li>
    <?php endforeach; ?>
</ul>

Under every title the first 80 characters of the post appear, followed by an ellipsis. The helper returns plain text, so the result is wrapped in escapeHtml().

A helper that outputs HTML

If a helper returns markup itself, the values must be escaped inside it. Other helpers are available through getView():

// module/Blog/src/View/Helper/PostLink.php
namespace Blog\View\Helper;
 
use Blog\Model\Post;
use Laminas\View\Helper\AbstractHelper;
 
class PostLink extends AbstractHelper
{
    public function __invoke(Post $post): string
    {
        $view = $this->getView();
 
        return sprintf(
            '<a href="%s">%s</a>',
            $view->url('blog/view', ['id' => $post->id]),
            $view->escapeHtml($post->title),
        );
    }
}

Once registered with the alias postLink, writing <?= $this->postLink($post) ?> prints a ready link to the post.

A helper with a service

A helper can receive a service through its constructor — for example ReadingTime from the service manager lesson:

// module/Blog/src/View/Helper/ReadingMinutes.php
namespace Blog\View\Helper;
 
use Blog\Service\ReadingTime;
use Laminas\View\Helper\AbstractHelper;
 
class ReadingMinutes extends AbstractHelper
{
    public function __construct(private ReadingTime $readingTime) {}
 
    public function __invoke(string $text): int
    {
        return $this->readingTime->minutes($text);
    }
}

In view_helpers.factories it gets ReflectionBasedAbstractFactory::class. The reading time is now calculated right in the view script: $this->readingMinutes($post->body).

Common mistake

A view helper is no place for database queries. Calling a table inside a helper that sits in a loop runs a query for every row of the list, and the page becomes slow. The controller prepares the data and passes it to the ViewModel; a helper only formats what it was given.

Thanks for reading!

Donate to the site — we work hard for you.