27/05 2013
New stuff: Controllers

A brief look at the new FluxController concept in Flux 6.0

Dear developers

It's time to revive the blog after finally finishing the new major versions. It has been almost two months of intense work - during which there have been no blog posts. Sorry about that; let's do something to rectify that.

Today I will give you a brief tour of a new concept in Flux which makes it much easier to use custom Controllers with fluid content and pages. The intention is to make fluid content and pages much more like actual Extbase controllers - in case you need this. If you don't need this, simply ignore the new feature and everything still works as you're used to. But for those developers who require a Controller for pages or content (or even fluid backend modules - I'll explain more about this another day), here is the quick and dirty version.

Why - or rather when - would I need this?

Simply this: if you don't use a custom Controller for pages or content, EXT:fluidpages and EXT:fluidcontent have no ways to use Services, foreign Repositories, do SQL queries, forward to other actions, using FE forms, accepting form inputs etc. (okay, there are tricks for this using VHS, but you surely get the point regardless). When you use a custom Controller for example for content elements, you can do all these things for each content element - but still let the content element function, in every aspect, like a fluid content element always has.

Perhaps an example using a theoretical "shop" type extension would explain it best. Such an extension might contain controllers for Product, Category, Customer etc. but might also need to contain tiny FE output plugins such as "hot right now" and "on offer" lists. Traditionally you would add a controller or use an existing one, then define an Extbase plugin for the controller+action, then add a FlexForm, then (if so desired) add a wizard so it can be selected in the "new content" wizard.

That's a lot to do. Instead, you can add an register a special ContentController, add a "Content" templateRootPath subdirectory and begin adding templates which are then recognised and displayed as usable content elements. Provide each template with a Flux form (and enable any Flux features you need, such as LLL auto-rewriting and -labelling) and the result is extremely speedy creation of tiny bits of content that would otherwise be a demanding task.

Building on the example of a "shop" type extension the shop might also want to provide special receipt page templates, newsletter page templates and other nifty stuff which, although just page rendering is implied, still require some pre-processing of values. For example the "receipt" page template's controller action might want to 1) disable any caching by force, 2) verify user login, 3) verify order number and status codes, 4) determine payment method, 5) send an email (once per order, of course). Instead of requiring the integrator to create a complicated plugin setup and a custom page template, he can simply select the page template which renders the complete receipt - and the "shop" extension's PageController would do the work usually done by a plugin. Not only this: because the selected page template is configured in the record, looking up (by SQL) the target receipt page does not have to involve TypoScript settings or added columns on the "pages" table and the ability to use "group, internalType db" fields to select pages with a clause applied that only allows selecting pages which have the proper template selected. The same goes for content elements (although for tt_content you would always have the list_type and CType fields to use in clauses that allow selecting a specific type of element).

Hopefully this clarified the intention of the new Controller concept in Flux: as with everything else Flux-related, the intention is to increase your productivity by not having to deal with manually performing all the small configurations that are normally necessary - instead, you can rely on Flux's conventions to make everything slide into place and just fill in the gaps - as you need, when you need to. Like all other Flux features your templates are the center and you build the interaction you need around them or inside them.

Now, to explain a few of the details of the Controller feature.

Registering a custom Controller

Until now you would add template paths sets using TypoScript - now, you can register a Controller name (fx Content or Pages) along with your extension key, which has the following impact:

  1. TypoScript is read from plugin.tx_myext.view as is done by Extbase plugins in general. There is one exception to this: backend controllers will use module.tx_myext.view instead. This means you must no longer use the special plugin.tx_fed.fce.myext or plugin.tx_fluidcontent.collections.myext but instead use the conventional view configuration paths. 
  2. The Controller must be placed in the location required by conventions: Classes/Controller - it's perfectly fine to use namespaces and of course vendor keys are also supported when registering your extension+controller.
  3. The template files must be placed in the path configured in TypoScript and must be placed in a subdirectory named the same as your controller: if you register a "Content" Controller, the class must be named "ContentController" and the (default) template files must be placed in be plugin.tx_myext.view.templateRootPath + Content/*.html location.

Other than this, everything is as it was before. You still need a flux:flexform in each template, they are still detected automatically and they are still rendered the same way - unless you continue customising by refining your Controller class.

What does this new Controller type do?

First of all, your new Controller must subclass Tx_Flux_Controller_AbstractFluxController - this is a hard requirement. This class is still fully compatible with traditional Extbase plugin rendering which means you can even configure actual plugins which use one or more of your Controller's actions. I will assume you already know how to do such a thing.

Not much is different from a traditional Controller in Extbase, except for how the View is initialised (and which class name is used for the View but that's another story for another time) and how variables are retrieved. The main differences are:

  1. You have $this->settings as you normally would*, but in addition you also have $this->getData() which can retrieve the FlexForm variables which apply to the exact record and field you are targeting. You have $this->setup which contains the TypoScript configured in your "view" location - and you have access to an instance of the ConfigurationProvider which is associated with the record type you are rendering (i.e. "pages" uses a PageConfigurationProvider, "tt_content" uses a ContentConfigurationProvider and so on - but they all share a common Interface).
  2. You do not have to configure any action methods at all. Any action method you do add will be called - and if no custom action is found, the fallback controller is used (example: if your custom Controller is a ContentController the rendering will fall back to EXT:fluidcontent since the "Content" controller base lives there. For a PageController it would be EXT:fluidpages, and so on).
  3. You must not define a renderAction; this method name is reserved and the action is used to render other actions - the name of which is determined by the template file returned by the ConfigurationProvider based on the current record associated with the current Controller. For example, a fluid content element might be rendering the file "Table.html" in which case the Flux base will try to detect and call the "tableAction" method.

* Note: $this->settings contains only settings from TypoScript. In the Fluid template, {settings} is the merged result of TypoScript and FlexForm variables but inside the Controller class you will need to use $this->getData() to retrieve variables from the FlexForm and $this->settings when you need a TypoScript value. This way you will always know the difference between the two.

Note that the last point (about resolving the Controller action name) is the opposite of the traditional Extbase logic: instead of resolving a filename based on the action being rendered, the action is resolved based on the file being rendered. Because it is done this way you are allowed to leave out any actions you don't need, but still be able to both select and use elements contained in templates that don't have corresponding controller actions.

In other words the Controller is completely transparent. Even if it is not used, your extension's scope is still used. When a class is registered but does not exist, Flux ignores it. If the class exists but has no actions, you can add initializeRenderAction, initializeObject, injection methods etc. and finally, if the class exists and the method exists as well, you can fine-tune what goes on when any specific template gets rendered (initiated by Flux and then handed off to your Controller).

But I can do all this with raw Extbase...?

Of course you can - and some will still prefer to do it this way, if requiring special new content element types (there are quite a few guides for creating content element types as Extbase controllers).

The point of Flux is not so much giving you new features but making existing features highly accessible and fast to use by giving you a condensed API with much less work required: not requiring special plugin registrations, huge XML flexforms, numerous LLL files you manually construct, table column changes and what's worse.

The point of Flux is: drop a template file in a directory, register that directory, fill in a few basics in the template and the template gets rendered as a page or content element with an associated form for content editors to use. Refining the template refines the page/content options and behaviour (for example: the backend page module column setup when editing page content) and so forth. It's exactly the same with this feature: it just further extends the ways in which you can refine your template rendering.

I want examples!

Of course you do :) and you shall receive!

Recent versions of fluidcontent_bootstrap and fluidpages_bootstrap both use the new feature. They are perfect for quickly learning how to create and register your Controller:

github.com/FluidTYPO3/fluidcontent_bootstrap

github.com/FluidTYPO3/fluidpages_bootstrap

See the "Classes/Controller" directory in each - and inspect either ext_tables.php or ext_localconf.php to see how a Controller name can be registered for your own extension.

Hope you enjoy!

Cheers,
Claus

PS: Got questions? Ask away in the comments below!