Hitchhiker's guide to TypoScript 2 - 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 TypoScript 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 TypoScript 2?

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

TypoScript 2 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 TypoScript 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 TypoScript 2 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 ofHello world.

Variables in TypoScript 2 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 TypoScript 2 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 TypoScript 2 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.

TypoScript object notation

Whenever you see the brackets { or } in TypoScript, 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 TypoScript. 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 TypoScript 2 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 TypoScript 2. 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 TypoScript 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

TypoScript

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 TypoScript. 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

TypoScript

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 TypoScript can be found on docs.typo3.org/.../FlowQueryOperationReference.html.

Processors

Sometimes we're just not happy with what we have. This seems to be the human nature, but TypoScript 2 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 TypoScript? 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

TypoScript

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 TypoScript...</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 TypoScript 2 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 TypoScript 2.

To be continued...

In Part 2 we will have a look at:

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

I hope you enjoyed this tour to the TypoScript 2 universe.


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

comments powered by Disqus