How to organize skins in symfony

This is a short note on how you can organize the use of skins for branding pages in Twig using the example of Symfony. This solution is not tied to Symfony. By analogy, you can implement skins in any project using Twig.


You have an online store, an online movie theater, a poster for events, a catalog of TV shows, etc. One fine day, you get the task of branding a catalog page to attract users and increase sales for some kind of action. How to do this if all products in the catalog are equivalent for the engine?


The simplest solution is to hardcode the product ID from the catalog. You can add a condition to the template and impose bodyan additional CSS class on the tag, according to which then you can stylize the page in general styles.


{% block body_class -%}
    {{ parent () }} product-{{ product.id }}
{%- endblock %}

body.product-12345 {
   # custom style
}

Styles can do a lot, especially if you use flex, but styles are not omnipotent. Sometimes the possibilities of styles are not enough for branding a page and you need to change the HTML markup (layout) of the page, and this is done by analogy with styles.


{% if product.id == 12345 %}
    {# custom code #}
{% else %}
    {# original code #}
{% endif %}

, , , (YAGNI KISS). — . , , .


, , — . , . . , . , . , - (DRY).


. , , , . , , .


product/show.html.twig. product/skin/<skin_name>/, <skin_name> — . default product/skin/default/show.html.twig. , .


public function show(Product $product): Response
{
    return $this->render(sprintf('product/skin/%s/show.html.twig', $product->skin), [
        'product' => $product,
    ]);
}

, , .


{# product/skin/custom_skin/show.html.twig #}

{% extends 'product/skin/default/show.html.twig' %}

{% blocksome_block %}
    {{ parent() }}
    {# customise something #}
{% endblock %}

, . , , , , . . , . , .


{# product/skin/default/show.html.twig #}

{% extends 'product/skin/' ~ product.skin ~ '/layout.html.twig' %}

{# ... #}

{# product/skin/custom_skin/layout.html.twig #}

{% extends 'product/skin/default/layout.html.twig' %}

{# ... #}

:


  • product/skin/<skin_name>/show.html.twig ️▼
  • product/skin/default/show.html.twig ️▼
  • product/skin/<skin_name>/layout.html.twig ️▼
  • product/skin/default/layout.html.twig ️▼
  • ...

— .


  • default
    • layout.html.twig
    • show.html.twig
    • qa.html.twig
    • similar.html.twig
  • first_skin
    • layout.html.twig
    • show.html.twig
    • qa.html.twig
    • similar.html.twig
  • second_skin
    • layout.html.twig
    • show.html.twig
    • qa.html.twig
    • similar.html.twig

, . Twig.


public function show(Product $product, Twig $twig): Response
{
    $template = $twig->resolveTemplate([
        sprintf('product/skin/%s/show.html.twig', $product->skin),
        'product/skin/default/show.html.twig',
    ]);
    $content = $template->render([
        'product' => $product,
    ]);

    return new Response($content);
}

resolveTemplate() . , , — . , Symfony , .


product/skin/default/show.html.twig , layout.html.twig. , Twig extends , extends resolveTemplate() . - .


{# product/skin/default/show.html.twig #}

{% extends [
    'product/skin/' ~ product.skin ~ '/layout.html.twig',
    'product/skin/default/layout.html.twig',
] %}

{# ... #}

Another good solution would be to place the styles of specific skins in separate files and connect only on a branded page. So we will not litter the main styles with garbage, which is used only on a couple of pages. If anyone is interested, then in a separate article I can tell you how to configure gulp to build a large number of skins together and separately. We have over 300 skins on the project.


All Articles