DEV Community

Cover image for Split your Symfony views into multiple controllers with NumberNine CMS
William Arin for NumberNine CMS

Posted on • Edited on

Split your Symfony views into multiple controllers with NumberNine CMS

When building a simple page with Symfony, we usually end up having a controller and a view.

Simple controller

Oftentimes, things get complicated and one controller is not a viable solution anymore, therefore we need to split the view like this:

Multiple controllers

Symfony offers several ways to split your views into multiple controllers. NumberNine CMS offers two more. We'll see which cons and pros each method provides.

  • using Twig partials with include
{% include 'my_partial.html.twig' with {message: 'hello'} %}
Enter fullscreen mode Exit fullscreen mode
  • using Twig partials with embed
{% embed 'my_partial.html.twig' %}
    {% block message %}hello{% endblock %}
{% endembed %}
Enter fullscreen mode Exit fullscreen mode
{{ render(controller('MyController', {message: 'hello'})) }}
Enter fullscreen mode Exit fullscreen mode
{{ component('my_component', {message: 'hello'}) }}
Enter fullscreen mode Exit fullscreen mode
  • using NumberNine components
{{ N9_component('MyComponent', {message: 'hello'}) }}
Enter fullscreen mode Exit fullscreen mode
  • using NumberNine shortcodes
{{ N9_shortcode('[my_shortcode message="hello"]') }}
Enter fullscreen mode Exit fullscreen mode

Pros and cons of each technique

Twig includes and embeds are great and flexible but their data come from above, which makes the root controller bloated. Of course there are workarounds such as Twig extensions or global variables to access a service, but a view shouldn't retrieve data that the controller didn't send to it.

Embedded controllers are a good solution if they're used sporadically, as they make a new Request and will negatively impact performance.

Twig Components is a better approach than embedded controllers, although they have some limitations. Components are non shared services, which means a new instance is created at every call of {{ component('my_component') }}, erasing previously stored data. This means that if your component registers an event listener, it won't work as the instance is created on the fly.

NumberNine components

NumberNine Components solve the problem of Twig Component's separate instances by creating a new set of template parameters everytime the component is called, but the instance itself of the component stays the same.

A NumberNine Component is a shared service, hence event listeners registered will catch events as they should. Parameters injection is done through setters.

Here's a concrete example.

# templates/page/show.html.twig

{{ N9_component('MyComponent', {example: 'string variable'}) }}
Enter fullscreen mode Exit fullscreen mode
// src/Component/MyComponent/MyComponent.php

final class MyComponent implements ComponentInterface
{
    private string $example;

    public function setExample(string $example): void
    {
        $this->example = $example;
    }

    public function getTemplateParameters(): array
    {
        return [
            'example' => $this->example,
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode
// src/Component/MyComponent/template.html.twig

<p>Displaying custom variable: {{ example }}.</p>
Enter fullscreen mode Exit fullscreen mode

Moreover, just like every other view in NumberNine, component templates are overridable. The theme you use probably uses components, and you'll probably want to adapt the design.

Read more about components on the documentation page.

NumberNine shortcodes

While components are developer-oriented, shortcodes are a user-oriented way to inject templates in the view.

Also known as BBcodes, shortcodes and have been widely used in forums and CMS for years. They are represented by a string that the user can input in his editor.

They have a short syntax:

[my_shortcode message="hello"]
Enter fullscreen mode Exit fullscreen mode

Or an extended syntax:

[my_shortcode]hello[/my_shortcode]
Enter fullscreen mode Exit fullscreen mode

Extended syntax allows for nested shortcodes:

[my_shortcode]
    [my_nested_shortcode message="hello"]
[/my_shortcode]
Enter fullscreen mode Exit fullscreen mode

NumberNine handles shortcodes as services. Each shortcode has its own class which acts like a controller.

As an example, we'll analyze this page header:

Page header

Now look at how it's rendered behind the scene:

Rendered shortcodes

This is not the result of a manual input in an editor, although it can. To know more about how it was rendered, see the last section of this article about the page builder.

Back to our view splitting. To put it simply, this header is composed of 9 controllers and 9 views, with no negative performance impact.

Let's take the my_account_link shortcode and see how it's built, as it's a very basic shortcode. Its purpose is to automatically link to the user-defined "My account" page:

/**
 * @Shortcode(name="my_account_link", label="My Account Link")
 */
final class MyAccountLinkShortcode extends AbstractShortcode
{
    public function configureParameters(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'loggedOutText' => 'Login / Register',
            'loggedInText' => 'My account',
        ]);
    }

    public function processParameters(array $parameters): array
    {
        return [
            'loggedOutText' => $parameters['loggedOutText'],
            'loggedInText' => $parameters['loggedInText'],
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

While its template looks like this:

<a href="{{ N9_path(PAGE_FOR_MY_ACCOUNT) }}">
    {%- if is_granted('IS_AUTHENTICATED_REMEMBERED') -%}
        {{ loggedInText|trans }}
    {%- else -%}
        {{ loggedOutText|trans }}
    {%- endif -%}
</a>
Enter fullscreen mode Exit fullscreen mode

Now it can be used either by the developer with {{ N9_shortcode('[my_account_link]') }}, or directly by the user in a text editor with [my_account_link].

That's all! Our views can be split in many ways and still keep a high performance. The main controller stays very light, and every partial controllers handle their own responsability. This makes the code highly reusable and easier to use for both developers and users.

Read more about shortcodes on the documentation page.

Shortcodes and the page builder

NumberNine's page builder relies on shortcodes to build the view. Each shortcode can be defined as an editable shortcode, which will appear in the page builder's list of available components to the user.

This will be the topic of an other article. Meanwhile, check out the documentation to see how to build a page builder component with a shortcode.

Read more

Top comments (0)