Skip to content

Abstract template layer into its own dedicated module #11322

@GuySartorelli

Description

@GuySartorelli

https://github.com/silverstripeltd/product-issues/issues/875 (private repo) outlines a POC for abstracting the template layer into its own module. This issue is about taking that POC and implementing it properly for CMS 6.

This will provide the following benefits:

  • improved separation between the view and model layers
  • improved architecture for the view layer
  • opens the door to further decouple the CMS from the template layer over time (e.g. could move all templates out into an optional 'admin-theme', and allow projects to introduce their own full 'admin-theme' similar to how the Drupal backend works)
  • ability to swap out the rendering engine easily in the future, or per project, if that's desired

Related issues

Check if these can be sensibly resolved as part of this work

Notes

  • Check everywhere that uses ThemeResourceLoader::findTemplate() and SSViewer::get_templates_by_class() if those end up being moved or significantly refactored
  • Don't forget to check what's going on with ViewableData_Customised and ViewableData_Debugger - at a minimum they need the same renaming treatment that ViewableData is getting.
  • ContentNegotiator has some weird regex related to the output of <% base_tag %>

Acceptance criteria

  • Code related to rendering .ss templates is moved into a new repository called silverstripe/silverstripe-template-renderer
    • To the extent that it's feasible, commit history for that code is copied into the new repository
    • The new module is a direct dependency of silverstripe/framework since that contains .ss templates.
  • SSViewer remains in framework, and has the following responsibilities:
    • It delegates template rendering (i.e. you call process() on the SSViewer instance, which instantiates a template renderer and asks for the rendered HTML)
    • It keeps track of themes
    • It injects js/css from the Requirements API into the rendered HTML result
    • It wraps the rendered HTML (string) in DBHtmlText
  • The content for the base tag and functionality for rewriting anchor links are either both retained in SSViewer or both moved to the new module
  • There is an interface in framework that defines the minimum API required for SSViewer to abstractly interact with a template rendering engine
  • If feasible with minimal upgrade pain, redefine how template candidates are structured (currently an esoteric associative array)
  • In PHP for core and supported modules, templates are never referenced with the .ss extension
  • A new ViewLayerData class is created.
    • All values must be wrapped in this class before being passed to the template renderer.
    • This class is responsible for the following:
      • Casting values to the appropriate DBField instance (plus arrays to either ArrayList or ArrayData)
      • Determining whether a value "exists" (most likely delegating to ModelData::exists() where that has been implemented)
  • ViewableData is renamed to ModelData, since it really represents a "Model" in the MVC sense.
  • Documentation is updated as appropriate
    • Any references to the API are updated
    • There's docs indicating how to replace the template renderer (and clearly highlighting the drawbacks of doing so)
    • As appropriate, distinguish between the model layer and the view layer in the docs
  • Overall test coverage is not reduced
  • There is a clear upgrade path

Explicitly excluded

Strong-typing PRs

Adding some strong typing to some existing API before I start the refactor so I have more confidence in the types of the values I'm dealing with and passing through to the template layer.

Kitchen sink CI run

Renaming PRs

CMS 5

CMS 6

kitchen sink CI
The CI failure is unrelated to these PRs and was caused by a fluent PR being merged before the code it relies on.

Refactor PRs

CMS 5

CMS 6

Kitchen sink CI run: https://github.com/creative-commoners/recipe-kitchen-sink/actions/runs/11584927001

I've made an example of what a twig module may look like, including twig versions of the simple theme templates.
https://github.com/GuySartorelli/silverstripe-twig
The readme notes some ways template creators will need to workaround the way the abstraction works - notably having to use getRawDataValue() for conditional checks.
If you want to try it out to test the abstraction, follow the instructions in the readme to set it as the renderer for PageController

Migration PRs

CMS 5

CMS 6

Kitchen sink CI

The above PRs should be merged before any others, since CI won't be happy on the others until the new repo is in packagist, and it can't be added to packagist until composer.json is added.
Supported modules PR is needed for GHA to generate a matrix so we can actually test the module itself.

Metadata

Metadata

Assignees

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions