My vision for TYPO3 content/page integrations
Estimated 15 minute read
Continuing from my vision for Fluid I'd like to describe another vision related to it. Where the first article is mainly about what we could do with the Fluid template engine outside of TYPO3, this article focuses on what we can do with it in TYPO3. It assumes the previous vision was realised.
The vision can be summarised as follows:
We should have a way to drop-in replace and add content templates, instead of requiring massive amounts of boilerplate configuration/code. And we should have a method for managing nested content which is provided by the core itself.
It actually is a lot simpler than it may initially seem. We already have in place the conventions that let us do this - all we need to do, is decide how we combine those conventions to give the best result for users as well as developers.
I'm envisioning a few simple but vital changes to TYPO3.
Content types and templating
I suggest the following approach to templating for content elements in TYPO3 by instituting these conventions:
- Every content element is associated with a Fluid template
- The associated template is named by
CTypeand format, e.g.
- The content template expects to be rendered by a
ContentControllera. This can be done with one controller per
CType, or, b. This can be done with a shared controller and one action per
CTypec. The actual controller class to use can be selectively overridden; using metadata, configuration, convention+detection, etc.
- The default templates are provided in
CTypes are detected by scanning configured
- An overlay procedure is used; if a certain
CTypehas more than one template, the first one detected is used
sys_templateTCA gets a select field renderMode=checkboxes to enable or disable content types, filled with detected CTypes
By implementing this approach we become able to add new content types by simply dropping in a Fluid template in any one of multiple paths that can be configured to contain templates. We no longer detect which possible content types you can use by analysing the configuration that was manually added (although, for legacy we still support it of course!) - instead, we detect which templates would be possible to render and provide these as our types when editors add new content elements. We allow replacing existing renderings by simply dropping in a template named like a core type - and our template gets used instead. Should we require even further custom processing, we can create a controller class that will handle the View initialisation, among other things.
Not only this, we also make it possible to create separate templates for different formats. Such as including XML or plaintext renderings of native content types, as Fluid templates, and have the template to use become resolved by request format.
Most of what we require for such a solution is already present. Compatibility with existing methods of configuration is possible (with that legacy configuration behaving as fallback option). But what we do need is a way to define metadata in the template files themselves - if we are to achieve the true "drop in replace" capability with a single file. Flux is able to serve exactly this purpose by embedding as much metadata in the template as is required. The structure for fields that editors use when configuring instances of the content element (the TCA columns) can also be configured here - although we would need some way to apply schema changes or use some relational storage that will not require schema changes. Anyways, those are technical details that do not affect the vision as a whole. On a side note: Flux also consumes TS that can define the metadata for templates, letting the integrator override every aspect without modifying files (unless he chooses to put his TS in a file, in which case, gold star for him!).
By providing the mentioned selector for
sys_template records we give the site administrator an ideal option to either white- or blacklist certain content types in any place in the page tree in a visual way (as opposed to, just as example, white- or blacklisting CTypes using TS parameters with CSV).
And of course we make it possible to completely replace every single aspect of how our content gets rendered in any page of our site, by just configuring an additional path to templates that contain overrides. And since TS is inherited to child pages, we can repeat this any number of times to keep overriding content types the further down we go.
A side note about plugins that are not unique CTypes
In TYPO3 we have the "plugin" content type which shares the
CType value with other plugins and uses the
list_type field as a secondary type indicating which plugin to render. It is possible to achieve a resolving by deeper nesting, e.g.
EXT:myext/Resources/Private/Templates/Content/ListType/SpecialPlugin.html (effectively sub-package) but this is still less than ideal. What I instead propose is to (silently) begin moving away from the practice of even creating "plugins" and instead, approach a solution that gives packages a conventions-based method of allowing content elements to be created which will call the controllers of the package (security concerns such as restricting access all handled "somehow", of course - that's not in the scope of this article).
This relates to another vision I have: Responsibilities. Still not written down but in essence, involves a way to use (composer, EM, whatever) metadata to instruct TYPO3 that package X is able to fulfil this and that Responsibility, with Responsibilities such as authentication, service, backend, content, page, assets etc. and integrations happening automatically based on assigned Responsibilities. Example: give your package the "content" responsibility and TYPO3 will automatically detect content elements' templates from that package's default template paths thus removing the need to add TypoScript. Basically, ways to make TYPO3 detect and automatically integrate certain features by just expecting a class name, a template file, etc.
Taking the exact same pattern from above and applying it to page templates, we can institute the following conventions:
- Every page "display type" is associated with a Fluid template
- The associated template can be freely named by uses
- The page template expects to be rendered by a
PageController(same logic as with
- The default templates are provided in
- Available "display types" are detected by scanning
- As with content, an overlay/override procedure is used
sys_templateTCA gets a field similar to the one for content, with white- and blacklisting options
Essentially we apply the very same convention from content type detection and use it for page "display type". So, what
CType is for content elements, the "display type" is for page rendering. The term "display type" is used here because that does not necessarily have to be what it is called - but there is some collision considerations to be taken, given that the field that currently exists is called "Layout" which is easy to confuse with the Fluid Layout with which it should not be confused.
And like the content integration, most of the things we require already exist - and indeed are easier to handle in terms of compatibility and legacy than are the content types.
The combination of the suggestions above are, in essence, the key features from
Nested content structures
- We need something compatible with current
- Preferably something also compatible with current
DataProviderpattern for said procedure
- Something which can be edited by a user quite easily (meaning UI)
- Something which can be implemented by anything: a core content type, a plugin, a custom content element or even a record (case in point: the EXT:news capability of relating to
tt_contentrecords could be served by a grid instead)
We currently store such definitions in a TS-like format in DB columns. I'll catch some flak for this, surely, but IMHO this storage method is perfectly fine at least for the time being. Ideally we would have some integration for truly relational records (see above) but that's not a requirement for this particular feature to be useful.
What I suggest is that we create a new field type in TCA, one which automatically applies the required boilerplate TCA to define a UI field that is edited visually and stores the required definition. I believe we already have a popup wizard that should serve nicely - it just has to be embedded to be truly useful. This takes care of being able to attach a grid structure to any record, including
be_layout and others, and to edit the definition through the backend.
Then, when the page module detects that a record (in this case exclusively
tt_content) has an associated grid, a sub-rendering is performed which uses a secondary column position markers to indicate 1) the parent content element and 2) the grid column. I suggest handling the grid columns by
lowerCamelCase names but in theory, they could be fully automated and just plain old integers like we're used to - effectively separating the child content from any root level columns' content. The technical solution here is less important than the end result; being able to re-use the page module's grid rendering mode in a sub-container.
The visions described above would make it extremely easy to handle templating tasks in TYPO3 - you would have an extremely powerful template engine and integration happening automatically. Another bonus benefit of this is that we get rid of that bad "page type is not defined!" default experience when user does not load a distribution - instead, he would be met by a nice default template (which could even instruct him how to go about adding additional ones). We make the user experience much nicer in the bare frontend and we improve it in the backend by giving him a nice selector that changes the "page template" - something he's probably used to from extensions already. And we integrate it with
sys_template for a nicer administrator experience. The developer experience is quite obviously much, much better than it was before. And depending on how much we also automate (for example, we might even try to detect
Page templates from all installed extensions and use their load order as override order; removing the need to add paths in TypoScript) we can evne further improve the developer experience.
Finally, by choosing to utilise Flux as metadata engine for the templates we get truly drop-in replacement capability. A developer or integrator would only need to create a new template file and edit it to his likings and upload it - and TYPO3 magically creates all the boring boilerplate you normally had to do.
And in the extremely long term we make it possible to finally deprecate a lot of content rendering related code in the core which deals exclusively with allowing this manual registration of
CTypes etc; removing it in favor of specific ways to integrate packages with TYPO3.
Oh yeah, there's one more thing...
There is absolutely nothing at all preventing us from then shipping such content and page template packages as purely composer dependencies; without all the TYPO3 specific metadata surrounding it. And rewinding to my vision for integrating Fluid with other frameworks as well, who is to say it won't also be possible to create adapters for those frameworks that apply the metadata extracted from templates to make it possible to use those templates through similar, convention-based integrations in other frameworks too. E.g. a "TYPO3 page/content driver for Flow" and whatnot.
Thanks for taking time to read my thoughts!