Attention
TYPO3 v11 has reached end-of-life as of October 31th 2024 and is no longer being maintained. Use the version switcher on the top left of this page to select documentation for a supported version of TYPO3.
Need more time before upgrading? You can purchase Extended Long Term Support (ELTS) for TYPO3 v11 here: TYPO3 ELTS.
Developing a custom ViewHelper
This chapter will demonstrate how to write a custom Fluid ViewHelper in TYPO3.
A "Gravatar" ViewHelper is created, which uses an email address as parameter and shows the picture from gravatar.com if it exists.
The official documentation of Fluid for writing custom ViewHelpers can be found within Fluid documentation at Creating ViewHelpers.
Contents of this page
Fluid
The custom ViewHelper is not part of the default distribution. Therefore a
namespace import is necessary to use this ViewHelper. In the following example,
the namespace \My
is imported with the
prefix blog
. Now, all tags starting with blog:
are interpreted as
ViewHelper from within this namespace:
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
xmlns:blog="http://typo3.org/ns/MyVendor/BlogExample/ViewHelpers"
data-namespace-typo3-fluid="true"
>
<!-- ... -->
</html>
The ViewHelper should be given the name "gravatar" and only take an email address as a parameter. The ViewHelper is called in the template as follows:
<blog:gravatar emailAddress="username@example.org" />
AbstractViewHelper implementation
Every ViewHelper is a PHP class. For the Gravatar ViewHelper, the name of the
class is \My
.
<?php
namespace MyVendor\BlogExample\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
final class GravatarViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
protected $escapeOutput = false;
public function initializeArguments()
{
// registerArgument($name, $type, $description, $required, $defaultValue, $escape)
$this->registerArgument('emailAddress', 'string', 'The email address to resolve the gravatar for', true);
}
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
// this is improved with the TagBasedViewHelper (see below)
return '<img src="http://www.gravatar.com/avatar/' .
md5($arguments['emailAddress']) .
'" />';
}
}
AbstractViewHelper
line 7
Every ViewHelper must inherit from the class
\TYPO3Fluid\
.
A ViewHelper can also inherit from subclasses of Abstract
, e.g.
from \TYPO3Fluid\
.
Several subclasses are offering additional functionality. The
Tag
will be explained later on in this chapter in detail.
Escaping of output
line 11
By default, all output is escaped by htmlspecialchars
to prevent cross
site scripting.
Setting the property $escape
to false is necessary to prevent
escaping of ViewHelper output.
By setting the property $escape
to false, escaping of the tag
content (its child nodes) can be disabled. If this is not set explicitly,
the value will be determined automatically: If $escape
: is true,
$escape
will be disabled to prevent double escaping. If
$escape
: is false, $escape
will be enabled unless
disabled explicitly.
Passing in children is explained in Prepare ViewHelper for inline syntax.
initializeArguments()
line 13
The Gravatar
ViewHelper must hand over the email address which
identifies the Gravatar. Every ViewHelper has to declare which parameters are
accepted explicitly. The registration happens inside initialize
.
In the example above, the ViewHelper receives the argument email
of
type string
. These arguments can be accessed
through the array $arguments
, which is passed into the render
method (see next section).
Tip
Sometimes arguments can take various types. In this case, the type mixed
should be used.
renderStatic()
line 19
The method render
is called once the ViewHelper is rendered. The
return value of the method is rendered directly.
- line 9*
The trait Compile
must be used if the class implements
render
.
Creating HTML/XML tags with the AbstractTagBasedViewHelper
For ViewHelpers which create HTML/XML tags, Fluid provides an enhanced base
class: \TYPO3Fluid\
. This
base class provides an instance of
\TYPO3Fluid\
that can be used to create
HTML-tags. It takes care of the syntactically correct creation and, for example,
escapes single and double quotes in attribute values.
Attention
Correctly escaping the attribute values is mandatory as it affects security and prevents cross-site scripting attacks.
Because the Gravatar ViewHelper creates an img
tag the use of the
\TYPO3Fluid\
is advised:
<?php
namespace MyVendor\BlogExample\ViewHelpers;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
class GravatarViewHelper extends AbstractTagBasedViewHelper
{
protected $tagName = 'img';
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('alt', 'string', 'Alternative Text for the image');
$this->registerArgument('emailAddress', 'string', 'The email address to resolve the gravatar for', true);
}
public function render()
{
$this->tag->addAttribute(
'src',
'http://www.gravatar.com/avatar/' . md5($this->arguments['emailAddress'])
);
return $this->tag->render();
}
}
What is different in this code?
The attribute $escape
is no longer necessary.
AbstractTagBasedViewHelper
line 6
The ViewHelper does not inherit directly from Abstract
but
from Abstract
, which provides and initializes the tag builder.
$tagName
line 8
There is a class property $tag
which stores the name of the tag to be
created (<img>
).
$this->tag->addAttribute()
line 20
The tag builder is available at property $this->tag
. It offers the method
add
to add new tag attributes. In our example the attribute
src
is added to the tag.
$this->tag->render()
line 24
The GravatarViewHelper creates an img tag builder, which has a method named
render
. After configuring the tag builder instance, the rendered tag
markup is returned.
Note
As $this->tag
is an object property, render
is used to
generate the output. render
would have no access. For further
information take a look at The different render methods.
$this->registerTagAttribute()
Furthermore the Tag
offers assistance for ViewHelper
arguments that should recur directly and unchanged as tag attributes. These
must be registered with the method $this->register
within initialize
.
If support for the <img>
attribute alt
should be provided in the ViewHelper, this can be done by initializing this in
initialize
in the following way:
public function initializeArguments()
{
// registerTagAttribute($name, $type, $description, $required = false)
$this->registerTagAttribute('alt', 'string', 'Alternative Text for the image');
}
For registering the universal attributes id, class, dir, style, lang, title,
accesskey and tabindex there is a helper method
register
available.
If support for universal attributes should be provided and in addition to the
alt
attribute in the Gravatar ViewHelper the following
initialize
method will be necessary:
public function initializeArguments()
{
parent::initializeArguments();
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('alt', 'string', 'Alternative Text for the image');
}
Insert optional arguments
An optional size for the image can be provided to the Gravatar ViewHelper. This size parameter will determine the height and width in pixels of the image and can range from 1 to 512. When no size is given, an image of 80px is generated.
The render
method can be improved like this:
public function initializeArguments()
{
$this->registerArgument('emailAddress', 'string', 'The email address to resolve the gravatar for', true);
$this->registerArgument('size', 'integer', 'The size of the gravatar, ranging from 1 to 512', false, 80);
}
public function render()
{
$this->tag->addAttribute(
'src',
'http://www.gravatar.com/avatar/' .
md5($this->arguments['emailAddress']) .
'?s=' . urlencode($this->arguments['size'])
);
return $this->tag->render();
}
With this setting of a default value and setting the fourth argument to false
,
the size
attribute becomes optional.
Prepare ViewHelper for inline syntax
So far, the Gravatar ViewHelper has focused on the tag structure of the ViewHelper. The call to render the ViewHelper was written with tag syntax, which seemed obvious because it itself returns a tag:
<blog:gravatar emailAddress="{post.author.emailAddress}" />
Alternatively, this expression can be written using the inline notation:
{blog:gravatar(emailAddress: post.author.emailAddress)}
One should see the Gravatar ViewHelper as a kind of post-processor for an email address and would allow the following syntax:
{post.author.emailAddress -> blog:gravatar()}
This syntax places focus on the variable that is passed to the ViewHelper as it comes first.
The syntax {post.
is an alternative
syntax for <blog:
. To
support this, the email address comes either from the argument email
or, if it is empty, the content of the tag should be interpreted as email
address.
This is typically used with formatting ViewHelpers. These ViewHelpers all support both tag mode and inline syntax.
Depending on the implemented method for rendering, the implementation is different:
With renderStatic()
To fetch the content of the ViewHelper, the argument
$render
is available. This returns the evaluated object
between the opening and closing tag.
Lets have a look at the new code of the render
method:
<?php
namespace MyVendor\BlogExample\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class GravatarViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
protected $escapeOutput = false;
public function initializeArguments()
{
$this->registerArgument('emailAddress', 'string', 'The email address to resolve the gravatar for');
}
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$emailAddress = $renderChildrenClosure();
return '<img src="http://www.gravatar.com/avatar/' .
md5($emailAddress) .
'" />';
}
}
With render()
To fetch the content of the ViewHelper the method render
is
available in the Abstract
. This returns the evaluated object
between the opening and closing tag.
Lets have a look at the new code of the render
method:
<?php
namespace MyVendor\BlogExample\ViewHelpers;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
class GravatarViewHelper extends AbstractTagBasedViewHelper
{
protected $tagName = 'img';
public function initializeArguments()
{
$this->registerArgument('emailAddress', 'string', 'The email address to resolve the gravatar for', false, null);
}
public function render()
{
$emailAddress = $this->arguments['emailAddress'] ?? $this->renderChildren();
$this->tag->addAttribute(
'src',
'http://www.gravatar.com/avatar/' . md5($emailAddress)
);
return $this->tag->render();
}
}
Handle additional arguments
If a ViewHelper allows further arguments which have not been explicitly
configured, the handle
method can be implemented.
The \TYPO3Fluid\
makes use
of this, to allow setting any data-
argument for tag based ViewHelpers.
The method will receive an array of all arguments, which are passed in addition to the registered arguments. The array uses the argument name as the key and the argument value as the value. Within the method, these arguments can be handled.
For example, the Abstract
implements the following:
public function handleAdditionalArguments(array $arguments)
{
$unassigned = [];
foreach ($arguments as $argumentName => $argumentValue) {
if (strpos($argumentName, 'data-') === 0) {
$this->tag->addAttribute($argumentName, $argumentValue);
} else {
$unassigned[$argumentName] = $argumentValue;
}
}
parent::handleAdditionalArguments($unassigned);
}
To keep the default behavior, all unwanted arguments should be passed to the
parent method call parent::
, to
throw exceptions accordingly.
The different render methods
ViewHelpers can have one or more of the following three methods for implementing the rendering. The following section will describe the differences between all three implementations.
compile()
-Method
This method can be overwritten to define how the ViewHelper should be compiled. That can make sense if the ViewHelper itself is a wrapper for another native PHP function or TYPO3 function. In that case, the method can return the call to this function and remove the need to call the ViewHelper as a wrapper at all.
The compile
has to return the compiled PHP code for the ViewHelper.
Also the argument $initialization
can be used to add further PHP
code before the execution.
Note
The render
method still has to be implemented for the non
compiled version of the ViewHelper. In the future, this should no longer be
necessary.
Example implementation:
<?php
namespace MyVendor\BlogExample\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class StrtolowerViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
public function initializeArguments()
{
$this->registerArgument('string', 'string', 'The string to lowercase.', true);
}
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
return strtolower($arguments['string']);
}
public function compile(
$argumentsName,
$closureName,
&$initializationPhpCode,
ViewHelperNode $node,
TemplateCompiler $compiler
) {
return 'strtolower(' . $argumentsName. '[\'string\'])';
}
}
renderStatic()
-Method
Most of the time, this method is implemented. It's the one that is called by default from within the compiled Fluid.
It is, however, not called on AbstractTagBasedViewHelper implementations. With these classes
you still need to use the render
method since that is the only way you can access $this->tag
which contains the tag builder that generates the actual XML tag.
As this method has to be static, there is no access to object properties such as
$this->tag
(in a subclass of Abstract
) from within
render
.
Note
This method can not be used when access to child nodes is necessary. This is
the case for ViewHelpers like if
or switch
which need to access their
children like then
or else
. In that case, render
has to be used.
render()
-Method
This method is the slowest one. Only use this method if it is necessary, for example if access to properties is necessary.