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.