Hitchhiker's guide to Neos Fusion - Part 1

Neos CMS is a fresh look at content management and was rebuilt from the start with a clean architecture. The actual output of a site is largely driven by a special rendering language called Fusion instead of being hard-coded in PHP. While learning a new language might seem a lot of work, it gives you numerous advantages and provides great flexibility. This article starts at the very beginning and will guide you through the concepts.

So, what is Fusion?

There is a clear idea of what Fusion in Neos should be and what it shouldn't be:

Fusion is a declarative and extensible language to integrate components in the view.

The main use case in Neos is the rendering of your content. Both in the backend for editing and on the frontend for the users of a site. With the very same concepts you are able to customize the editing experience and the look and feel of a site.

But it can do more than just rendering of websites: Imagine an e-commerce application that should be re-used and extended by others. Instead of having templates that are used in a fixed way, a Fusion driven view would be able to control the output in a fine-grained and extensible way.

Hello world

Let's just write the first line of Fusion and shout out a nice welcome:

output = 'Hello world!'

Output

Hello world!

That looks familiar with a lot of programming languages. You could write the same line in Python, Ruby or JavaScript. But what does it mean here? We declare a path output that is assigned a string value of Hello world.

Variables in Fusion are called properties. These are evaluated from the outside by specifying a path to a property. For the examples we just take the name output as the path we are rendering.

Let there be objects

Simple values alone are too simplistic for the problems we have at hand. So Fusion offers objects as the main building blocks and for re-using code. Think of them as components that can be rendering instructions for a complete page, a menu or just one node type like a Headline.

To combine the result of different properties, we can use the Array object:

output = Array {
    hello = 'Hello'
    world = 'world!'
}

Output

Helloworld!

The Array object and all other objects in Fusion allow nesting. The Array is evaluated by rendering all its children and combining their output. This is done literally, so we miss a space between the words here.

The two declarations hello and world are properties of the object in this case. We can set an individual property with the following syntax:

output.world = ' world!'

Doing that after the previous declaration should yield the expected output Hello World!:

output = Array {
    hello = 'Hello'
    world = 'world!'
}
output.world = ' world!'

As you can see we can override a previous declaration.

Fusion object notation

Whenever you see the brackets { or } in Fusion, it's only a shorter form of writing the paths in the full form. So all these are the same:

output = Array {
	hello = 'Hello'
	world = 'world!'
}
output.world = ' world!'

# ---

output = Array
output.hello = 'Hello'
output.world = 'world!'
output.world = ' world!'

# ---

output = Array
output.hello = 'Hello'
output.world = ' world!'

All three parts in the example are exactly the same. That's because of the declarative nature of Fusion. It will figure out the final definition of all the properties and apply any overridden value before the evaluation of the output.

There is only a handful of other object types besides Array that are included in the Fusion core. This makes the language comprehensible and easy to learn.

Express yourself

With objects we can declare structures. And with simple values we can declare values like strings, booleans or numbers.

Yet, sometimes we want to have something more expressive at our hands to change a behavior depending on some condition or to do some processing of our data.

output = ${Array.join(['Hello', 'world!'], ' ')}

Output

Hello world!

Wow, that looks like we are actually programming here!

Indeed there is a new expression language called Eel in Fusion. It's the perfect companion for the tricky and individual cases where we wished for something more than objects and simple values. It's a subset of the JavaScript language that allows to write any kind of expression. That means no manipulation of variables or functions, but anything that expresses a value.

In the example we use a helper with name Array that comes pre-defined with Neos (not to be confused with the Array object). These helper objects provide a standard library of functions to work with strings, arrays, dates, numbers and other data in the system. And it's easy to write custom helpers to extend the functions that are available in Eel.

We need a context

Expressions are really powerful if we have to deal with data coming from nodes or other objects. But where does that data come from?

For the frontend rendering in Neos we have the node that was fetched by a URL like /home/about-us.html. This node is given to the Fusion in the so called context and is accessible in expressions. Let's assume we have a Node of type Page under the name node in the context:

Context

Fusion

output = ${'<title>' + node.properties.title + '</title>'}

Output

<title>About us</title>

The node context variable is of type Node and has properties that can be accessed. As in the Fluid template language, a path of properties can be separated by dots. The operation to join strings is a +, just like in JavaScript.

Working with nodes

In the content repository of Neos everything is a Node. Nodes are nested and form a tree. Navigating between the nodes using the API of the content repository can become complicated. Also it provides only basic functionality, so for example finding the closest node of some type would require some more code to write.

It would be great to have some simple tools for finding, filtering and navigating through the nodes in the content repository. With FlowQuery the ideas of the widely known jQuery JavaScript library are ported to the server-side right into Fusion. The operations in FlowQuery can be used to find nodes of a special type or of a position and extract content from them. With a combination of different operations the content in Neos can be accessed in a very flexible way. This allows for a customized rendering of nodes without PHP programming or writing queries in SQL.

Let's have a look at an example expression that uses FlowQuery to get the title of the parent page:

Context

Fusion

output = ${q(node).parent().property('title')}

Output

Home

This expression finds the direct parent of the node and gets the title property. We use the function q to wrap the context variable node in a FlowQuery result. That result supports many operations for going through the node structure or extracting content from a node. Here we use parent() to get the direct parent node of the current result. This will return yet another FlowQuery result where we can apply another operation. The last operation in this example will get the title property from the node and return the string value "Home".

A full list of operations that are included in Neos and Fusion can be found on neos.readthedocs.io.

Processors

Sometimes we're just not happy with what we have. This seems to be the human nature, but Fusion has a solution. It's called processor and can be applied to any property to modify its value.

Imagine this expression for rendering the title of the direct parent of a node:

output = ${q(node).parent().property('title')}

What if we wanted to wrap this with a <strong> tag in our site package?

output = ${'<strong>' + q(node).parent().property('title') + '</strong>'}

This works, but we need to copy the whole expression. A processor can be applied to a property to modify the value after evaluation:

output.@process.strongTag = ${'<strong>' + value + '</strong>'}

The name strongTag is arbitrary here and could be just a number. It's desirable though to give processors a distinct name to override their declaration later. The evaluated result of the current property can be accessed with the special value variable inside the expression.

We can also completely replace the current value by using a helper function in the expression:

output.@process.uppercaseTitle = ${String.toUpperCase(value)}

The String.toUpperCase function is another predefined helper that comes with the TYPO3.Eel package that will uppercase the given value. There is currently no good overview of the available helpers other than the PHP classes, so until a reference will be available on docs.typo3.org, they are a good source to see all the available functions.

If multiple processors are declared on a property, they will be combined to process the value multiple times. Each processor will get the result of the previous processor or the original value, if it's the first one.

A page test drive

So how could we define the full page output of Neos using Fusion? We have the power of Fluid available with the Template object, so we could just use the node context variable in the template. For that we have to define it as a property of a Template object which can be used as a variable of the same name inside Fluid.

Context

Fusion

page = Template {
    templatePath = 'Main.html'
    node = ${node}
    childNodes = ${q(node).find('main').children()}
}

Main.html

<html>
<title>{node.properties.title}</title>
<body>
	<h1>{node.properties.title}</h1>
	<f:for each="{childNodes}" as="childNode">
		<h2>{childNode.properties.title}</h2>
		<p>{childNode.properties.text}</p>
	</f:for>
</body>
</html>

Output

<html>
<title>About us</title>
<body>
	<h1>About us</h1>
		<h2>Hello world!</h2>
		<p></p>
		<h2></h2>
		<p>Welcome to Fusion...</p>
</body>
</html>

This renders the basic HTML for the page and the nodes in the main content collection. But of course it only works if all the child nodes have a title and text property, so we get some empty tags. What about supporting multiple node types and rendering them differently?

We need to look at some additional features of Fusion to have a fully working page rendering like it's set up by default in Neos. But that will be part of a second guide to Fusion.

To be continued...

In Part 2 we will have a look at:

  • How to create custom Fusion objects with prototypes
  • How to use the Collection object to render any collection using Fusion
  • Using the Case object to use conditional rendering
  • The power of prototypes and context dependent overrides

I hope you enjoyed this tour to the Neos and Fusion universe.


Update (March, 2018): Renamed TypoScript 2 to Fusion

Update (April, 7th): The section about processors got a new example and the special meaning of "value" is explained.

comments powered by Disqus