Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Diseños compartidos

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Abre base.html.twig y mueve el {% block layout %} para que esté alrededor de todo. Así, pon el inicio justo dentro de la etiqueta body... y el final justo antes de la etiqueta de cierrebody:

<!DOCTYPE html>
<html>
... lines 3 - 16
<body>
{% block layout %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
... lines 20 - 45
</nav>
{% block body %}{% endblock %}
<div class="container mt-5">
... lines 51 - 60
</div>
{% endblock %}
</body>
</html>

Si ahora actualizamos la página de inicio... ¡se destruye! La parte superior nav y footer han desaparecido. ¿Por qué he hecho esto? ¡Porque me encanta el caos! Es broma, lo he hecho porque nos da el poder, dentro de los layouts, de diseñar páginas totalmente personalizadas: incluso páginas sin los tradicionales navigation y footer, tal vez como una página de aterrizaje temporal para una promoción.

Pero seamos sinceros, el 99% de las veces, querremos los nav y footer. No hay problema, vuelve a base.html.twig. Recuerda: añadir bloques nos da más flexibilidad. Así que, encima de la navegación, añade un nuevo bloque llamado navigation, con {% endblock %} después. Luego, aquí abajo, otro llamado footer... y{% endblock %}:

<!DOCTYPE html>
<html>
... lines 3 - 16
<body>
{% block layout %}
{% block navigation %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
... lines 21 - 46
</nav>
{% endblock %}
{% block body %}{% endblock %}
{% block footer %}
<div class="container mt-5">
... lines 54 - 63
</div>
{% endblock %}
{% endblock %}
</body>
</html>

Apuesto a que sabes lo que voy a hacer a continuación. En el administrador del diseño, ahora podemos añadir un bloque Twig en la parte superior que muestre navigation... y otro aquí abajo en la parte inferior. No es necesario que esté en esta última zona... pero tiene sentido allí. Renderiza footer.

¡Vamos a probarlo! Pulsa "Publicar y continuar editando" y... actualiza. ¡Ya estamos de vuelta!

Crear una segunda maqueta

Vamos a crear un segundo diseño, esta vez para la página /recipes. Si miras en RecipeController, verás que ya he hecho todo el trabajo para consultar las recetas y pasarlas a esta plantilla:

... lines 1 - 12
class RecipeController extends AbstractController
{
#[Route('/recipes/{page<\d+>}', name: 'app_recipes')]
public function recipes(RecipeRepository $recipeRepository, int $page = 1): Response
{
$queryBuilder = $recipeRepository->createQueryBuilderOrderedByNewest();
$adapter = new QueryAdapter($queryBuilder);
/** @var Recipe[]|Pagerfanta $pagerfanta */
$pagerfanta = Pagerfanta::createForCurrentPageWithMaxPerPage($adapter, $page, 4);
return $this->render('recipes/list.html.twig', [
'pager' => $pagerfanta,
]);
}
... lines 27 - 34
}

Y en esa plantilla, hacemos un bucle y renderizamos cada una, con paginación:

... lines 1 - 4
{% block body %}
<div class="hero-wrapper">
<h1>Doggone Good Recipes</h1>
<p>Recipes your pup will love!</p>
</div>
... lines 10 - 31
{% endblock %}

Y así, definitivamente quiero incluir todo este trabajo personalizado en el nuevo diseño.

De vuelta al área de administración, pulsaré "Publicar diseño" como una forma fácil de volver a la lista de diseños. A continuación, pulsa en nuevo diseño, elegiré mi diseño favorito 2 y lo llamaré "Diseño de la lista de recetas". Para empezar, añade un nuevo bloque llamado "Vista completa"... y arrástralo a cualquier parte de la página, ¡vaya! Ya está.

¿Qué es esta "Vista completa"? No es nada especial, de hecho, ¡es un poco redundante! No es más que un "bloque Twig" que renderiza el bloque llamado body. Así que, sí, podríamos haber hecho esto fácilmente utilizando el bloque Twig normal y escribiendo body.

Publica este diseño... y luego ve a "Mapeos de diseño". Añade una nueva... y esta vez la enlazaré primero... a "Diseño de la lista de recetas". Luego haz clic en "Detalles". Como la última vez, podríamos mapear esto a través del nombre de la ruta. Pero para ver algo diferente, utiliza "Información de la ruta", que, de nuevo, es sólo una palabra elegante para la URL, pero sin ningún parámetro de consulta. Haz que coincida con /recipes... "Guarda los cambios" y... ¡bien!

Cuando probamos la página... ¡se ve genial! Excepto que, ¡me olvidé del nav y del pie de página! Añadir esos dos bloques al "Diseño de la lista de recetas" es fácil. Pero ¿qué pasa si, más adelante, decidimos que cada página debe mostrar tanto el bloque de navegación en la parte superior como un banner dinámico, quizá para una venta que estemos realizando? Si eso ocurriera, tendríamos que editar cada diseño para añadir manualmente ese nuevo banner.

Diseños compartidos

Afortunadamente, hay una forma mejor de manejar elementos de diseño repetidos como éste.

Pulsa "Descartar" para volver a la lista de diseños, y luego haz clic en "Diseños compartidos" y "Nuevo diseño compartido". Como siempre, el tipo de diseño no importa mucho, así que usaré el normal... y lo llamaré "Diseño de navegación y pie de página".

Este no va a ser un diseño real que esté vinculado a ninguna página. No, sólo va a ser una maqueta de la que robaremos piezas. En la zona superior, crea un bloque de Twig que se muestre en navigation... e incluso lo etiquetaré como "Top Nav" para que quede más claro. A continuación, en cualquier otra zona -puedes ponerla en la parte inferior, pero no es necesario-, añade otro bloque Twig que renderice footer y se etiquete como Pie de página.

¡Genial! Pulsa "Publicar diseño". Ahora tenemos un diseño compartido. De nuevo, no están pensados para ser asignados a páginas: están pensados para que los utilicemos en otros diseños reales.

Compruébalo: edita el "Diseño de la lista de recetas". En la parte inferior izquierda de la pantalla, escondido detrás de la barra de herramientas de depuración web -la cerraré temporalmente- hay un botón para vincular una zona con una zona de diseño compartido. Haz clic en él y selecciona la zona superior... llamada zona "Cabecera", aunque ese nombre no es importante.

Ahora, podemos encontrar una zona compartida desde un diseño compartido... y sólo tenemos una. Pulsa "Seleccionar zona" y... ¡ya está! La zona superior de nuestro diseño será ahora igual al bloque o bloques que haya en la zona superior de ese diseño compartido. Si añadimos más cosas a esa zona en el diseño compartido, aparecerán automáticamente aquí.

Hazlo una vez más: selecciona la última zona para que el pie de página aparezca definitivamente en la parte inferior, selecciona la zona compartida y... ¡listo!

Publica eso, muévete, actualiza y... ¡la página completa está de vuelta! Repitamos rápidamente esto para la "Disposición de la página de inicio". Pero esto es complicado porque pongo todos mis bloques dentro de esta zona superior. En general, estas zonas no importan, pero en este caso, para evitar sobrescribir todo esto, arrastraré todo excepto el bloque Twig de navegación hacia abajo. Podemos arreglar el orden más tarde.

Y ahora, configura la zona superior para que utilice la del diseño compartido. Sí Reemplaza lo que teníamos allí antes. A continuación, enlaza también la zona inferior con el diseño compartido.

¡Perfecto! Comprobemos el orden de nuestros bloques... que es lo bueno de los diseños. Si no me gusta el orden de lo que hay en mi página, ¡siempre puedo cambiarlo! Eso es mejor. Publica el diseño, vuelve a la página de inicio en el frontend y... ¡bien!

Siguiente: hagamos que nuestra página de la lista de recetas sea más flexible permitiendo que esta zona superior de h1se construya y personalice desde dentro de los diseños... en lugar de que esté codificada en la plantilla.

Leave a comment!

4
Login or Register to join the conversation

Not sure if you made a mistake or not, but you are referring to a body twig block when using the Full View block, but this is not correct, it should be content. In the text:

What is this "Full View". It's nothing special, in fact, it's kind of redundant! It's nothing more than a "Twig block" that renders the block called body. So, yes, we could have just as easily done this by using the normal Twig block and typing in body.

The full view twig template (in my version at least, file: @NetgenLayoutsStandard/nglayouts/themes/standard/block/full_view.html.twig) shows:

{% extends '@nglayouts/block/block.html.twig' %}

{% block content %}
   {{ twig_content|raw }}
{% endblock %}
Reply
weaverryan Avatar weaverryan | SFCASTS | edin | posted hace 8 meses | HIGHLIGHTED

Hey edin!

This is tricky stuff! When I saw your well-researched comment, I was thinking "he's right! How did I mess that up?".

But actually, the text is correct. But... you are ALSO correct :D. Here's what happens internally:

1) You're right that the template that is rendered for the full_view.html.twig looks like this:

{% block content %}
    {{ twig_content|raw }}
{% endblock %}

But this doesn't mean that this reads from your block called content. It means that it prints the twig_content variable (we'll talk about where this comes from in a minute) into a block called content. Then, because this extends block.html.twig - that template. - https://github.com/netgen-layouts/layouts-core/blob/master/bundles/LayoutsBundle/Resources/views/nglayouts/themes/standard/block/block.html.twig#L9-L19 - just takes the content block and renders it out.

So this is all a really fancy way of basically just saying {{ twig_content }} outside of any block.

2) So where does twig_content come from? First, each block has a "handler" class. The one for full view is this - https://github.com/netgen-layouts/layouts-standard/blob/master/lib/Block/BlockDefinition/Handler/Twig/FullViewHandler.php - nothing too important there, except that it implements TwigBlockDefinitionHandlerInterface.

3) When the blocks are rendering an event is fired. One of the listeners is this class: https://github.com/netgen-layouts/layouts-core/blob/master/bundles/LayoutsBundle/EventListener/BlockView/GetTwigBlockContentListener.php - notice this checks to see if the block handler implements TwigBlockDefinitionHandlerInterface. If it does, it takes whatever the current template is that's rendering (it's an object, stored in that twig_template parameter), loops over $blockDefinition->getTwigBlockNames($block) and returns the contents of the first matching block. These contents become the twig_content variable rendered in the template.

4) So then, which block does $blockDefinition->getTwigBlockNames($block) return? Well, if you look back at FullViewHandler:

class FullViewHandler ... {
    public function __construct(array $twigBlockNames)
    {
        $this->twigBlockNames = $twigBlockNames;
    }

    public function getTwigBlockNames(Block $block): array
    {
        return $this->twigBlockNames;
    }
}

That doesn't tell us. It just says that the block names are injected via the constructor. Where is this service configured? Well, the argument is configured here: https://github.com/netgen-layouts/layouts-standard/blob/14af051a3f0bdc1f2be89014169a8b84148f1225/bundle/Resources/config/services/block_definitions.yaml#L21

So in the end, EITHER the content block or body block would be used (though the fact that you see a block called content inside ful_view.html.twig is coincidental and not part of this process). If you had both a content and body block, content would win, so I suppose your answer is slightly more correct than mine, though I think I'll keep the text as it is :).

Cheers!

2 Reply

Oh wow, thank you for the detailed explanation. I feel a bit guilty not doing this myself.
I am going to dig into this matter more myself when the full tutorial gets released.

For now, I am not able to render anything besides content Twig block in my template.
I probably missed something as I am using my own playground project for this, but as I said, I will investigate this further with the tutorial project.

Thank you.

Reply

Hey edin!

No worries - I love to dig in, and your conclusion seemed logical to me too originally! Weird that only content Twig block is being used - let me know what you find out when you have a chance to investigate further.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1.0",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.7", // v3.7.0
        "doctrine/doctrine-bundle": "^2.7", // 2.7.0
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.13", // 2.13.3
        "easycorp/easyadmin-bundle": "^4.4", // v4.4.1
        "netgen/layouts-contentful": "^1.3", // 1.3.2
        "netgen/layouts-standard": "^1.3", // 1.3.1
        "pagerfanta/doctrine-orm-adapter": "^3.6",
        "sensio/framework-extra-bundle": "^6.2", // v6.2.8
        "stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
        "symfony/console": "5.4.*", // v5.4.14
        "symfony/dotenv": "5.4.*", // v5.4.5
        "symfony/flex": "^1.17|^2", // v2.2.3
        "symfony/framework-bundle": "5.4.*", // v5.4.14
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/proxy-manager-bridge": "5.4.*", // v5.4.6
        "symfony/runtime": "5.4.*", // v5.4.11
        "symfony/security-bundle": "5.4.*", // v5.4.11
        "symfony/twig-bundle": "5.4.*", // v5.4.8
        "symfony/ux-live-component": "^2.x-dev", // 2.x-dev
        "symfony/ux-twig-component": "^2.x-dev", // 2.x-dev
        "symfony/validator": "5.4.*", // v5.4.14
        "symfony/webpack-encore-bundle": "^1.15", // v1.16.0
        "symfony/yaml": "5.4.*", // v5.4.14
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.3
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
        "symfony/debug-bundle": "5.4.*", // v5.4.11
        "symfony/maker-bundle": "^1.47", // v1.47.0
        "symfony/stopwatch": "5.4.*", // v5.4.13
        "symfony/web-profiler-bundle": "5.4.*", // v5.4.14
        "zenstruck/foundry": "^1.22" // v1.22.1
    }
}
userVoice