Legacy: Fluid Templating

Our Classic Template Language

For new projects use AFX

For new projects we recommend to use AFX, since it's more powerful and the current best practise.

Fluid has several goals in mind:

  • Simplicity
  • Flexibility
  • Extensibility
  • Ease of use

This templating engine should not be bloated, instead, we try to do it “The Zen Way” - you do not need to learn too many things, thus you can concentrate on getting your things done, while the template engine handles everything you do not want to care about.

#What Does it Do?

In many MVC systems, the view currently does not have a lot of functionality. The standard view usually provides a render method, and nothing more. That makes it cumbersome to write powerful views, as most designers will not write PHP code.

That is where the Template Engine comes into play: It “lives” inside the View, and is controlled by a special TemplateView which instantiates the Template Parser, resolves the template HTML file, and renders the template afterwards.

Below, you’ll find a snippet of a real-world template displaying a list of blog postings. Use it to check whether you find the template language intuitive:

fluid
{namespace f=Neos\FluidAdaptor\ViewHelpers}

Blog

Blog Postings

{posting.title}

{posting.author.name} {posting.author.email}

{posting.teaser}

  • The Namespace Import makes the \Neos\FluidAdaptor\ViewHelper namespace available under the shorthand f.
  • The essentially corresponds to foreach ($postings as $posting) in PHP.
  • With the dot-notation ({posting.title} or {posting.author.name}), you can traverse objects. In the latter example, the system calls $posting->getAuthor()->getName().
  • The tag is a so-called ViewHelper. It calls arbitrary PHP code, and in this case renders a link to the “details”-Action.

There is a lot more to show, including:

  • Layouts
  • Custom View Helpers
  • Boolean expression syntax

#Basic Concepts

This section describes all basic concepts available. This includes:

#Namespaces

Fluid can be extended easily, thus it needs a way to tell where a certain tag is defined. This is done using namespaces, closely following the well-known XML behavior.

Namespaces can be defined in a template in two ways:

{namespace f=NeosFluidAdaptorViewHelpers}

This is a non-standard way only understood by Fluid. It links the f prefix to the PHP namespace \Neos\FluidAdaptor\ViewHelpers.

The standard for declaring a namespace in XML. This will link the foo prefix to the URI http://some/unique/namespace and Fluid can look up the corresponding PHP namespace in your settings (so this is a two-piece configuration). This makes it possible for your XML editor to validate the template files and even use an XSD schema for auto completion.

A namespace linking f to \Neos\FluidAdaptor\ViewHelpers is imported by default. All other namespaces need to be imported explicitly.

If using the XML namespace syntax the default pattern http://typo3.org/ns/ is resolved automatically by the Fluid parser. If you use a custom XML namespace URI you need to configure the URI to PHP namespace mapping. The YAML syntax for that is:

yaml
Neos:
  Fluid:
    namespaces:
      'http://some/unique/namespace': 'My\Php\Namespace'

#Variables and Object Accessors

A templating system would be quite pointless if it was not possible to display some external data in the templates. That’s what variables are for.

Suppose you want to output the title of your blog, you could write the following snippet into your controller:

php
$this->view->assign('blogTitle', $blog->getTitle());

Then, you could output the blog title in your template with the following snippet:

fluid

This blog is called {blogTitle}

Now, you might want to extend the output by the blog author as well. To do this, you could repeat the above steps, but that would be quite inconvenient and hard to read.

Note

The semantics between the controller and the view should be the following: The controller instructs the view to “render the blog object given to it”, and not to “render the Blog title, and the blog posting 1, …”.
 Passing objects to the view instead of simple values is highly encouraged!

That is why the template language has a special syntax for object access. A nicer way of expressing the above is the following:

php
// This should go into the controller:
$this->view->assign('blog', $blog);
fluid

This blog is called {blog.title}, written by {blog.author}

Instead of passing strings to the template, we are passing whole objects around now - which is much nicer to use both from the controller and the view side. To access certain properties of these objects, you can use Object Accessors. By writing {blog.title}, the template engine will call a getTitle() method on the blog object, if it exists. Besides, you can use that syntax to traverse associative arrays and public properties.

Tip

Deep nesting is supported: If you want to output the email address of the blog author, then you can use {blog.author.email}, which is roughly equivalent to $blog->getAuthor()->getEmail().

#View Helpers

All output logic is placed in View Helpers.

The view helpers are invoked by using XML tags in the template, and are implemented as PHP classes (more on that later).

This concept is best understood with an example:

fluid
{namespace f=Neos\FluidAdaptor\ViewHelpers}
Administration

The example consists of two parts:

  • Namespace Declaration as explained earlier.
  • Calling the View Helper with the ... tag renders a link.

Now, the main difference between Fluid and other templating engines is how the view helpers are implemented: For each view helper, there exists a corresponding PHP class. Let’s see how this works for the example above:

The tag is implemented in the class \Neos\FluidAdaptor\ViewHelpers\Link\ActionViewHelper.

Note

The class name of such a view helper is constructed for a given tag as follows:

  1. The first part of the class name is the namespace which was imported (the namespace prefix f was expanded to its full namespace Neos\FluidAdaptor\ViewHelpers)
  2. The unqualified name of the tag, without the prefix, is capitalized (Link), and the postfix ViewHelper is appended.

The tag and view helper concept is the core concept of Fluid. All output logic is implemented through such ViewHelpers / tags! Things like if/else, for, … are all implemented using custom tags - a main difference to other templating languages.

Note

Some benefits of the class-based approach approach are:

  • You cannot override already existing view helpers by accident.
  • It is very easy to write custom view helpers, which live next to the standard view helpers
  • All user documentation for a view helper can be automatically generated from the annotations and code documentation.

Most view helpers have some parameters. These can be plain strings, just like in..., but as well arbitrary objects. Parameters of view helpers will just be parsed with the same rules as the rest of the template, thus you can pass arrays or objects as parameters.

This is often used when adding arguments to links:

fluid

  ... read more

Here, the view helper will get a parameter called arguments which is of type array.

Warning

Make sure you do not put a space before or after the opening or closing brackets of an array. If you type arguments="{singleBlog: blogObject}" (notice the space before the opening curly bracket), the array is automatically casted to a string (as a string concatenation takes place).
 
This also applies when using object accessors:  and  are substantially different: In the first case, the view helper will receive an object as argument, while in the second case, it will receive a string as argument.
 
This might first seem like a bug, but actually it is just consistent that it works that way.

#Boolean Expressions

Often, you need some kind of conditions inside your template. For them, you will usually use the ViewHelper. Now let’s imagine we have a list of blog postings and want to display some additional information for the currently selected blog posting. We assume that the currently selected blog is available in {currentBlogPosting}. Now, let’s have a look how this works:

fluid

  ... some special output here ...

In the above example, there is a bit of new syntax involved: {post} == {currentBlogPosting}. Intuitively, this says “if the post I’‘m currently iterating over is the same as currentBlogPosting, do something.”

Why can we use this boolean expression syntax? Well, because the IfViewHelper has registered the argument condition as boolean. Thus, the boolean expression syntax is available in all arguments of ViewHelpers which are of type boolean.

All boolean expressions have the form X Y, where:

  • is one of the following: ==, >, >=, <, <=, % (modulo)
  • X and Y are one of the following:
    • a number (integer or float)
    • a string (in single or double quotes)
    • a JSON array
    • a ViewHelper
    • an Object Accessor (this is probably the most used example)
    • inline notation for ViewHelpers

#Inline Notation for ViewHelpers

In many cases, the tag-based syntax of ViewHelpers is really intuitive, especially when building loops, or forms. However, in other cases, using the tag-based syntax feels a bit awkward – this can be demonstrated best with the - ViewHelper, which is used to reference static files inside the Public/ folder of a package. That’s why it is often used inside