Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Vista de artículos del frontend

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.

Es hora de crear la vista de artículos Recipe para el frontend. Esto empieza casi exactamente igual. De hecho, copia la configuración del admin... y pégala. En Layouts, sabemos que la clave app significa la sección "admin". Y resulta que default se utiliza para significar el frontend:

netgen_layouts:
... lines 2 - 12
view:
item_view:
... lines 15 - 21
# default = frontend
default:
# this key is not important
recipes_default:
... lines 26 - 29

Frontend (por defecto) item_view & Template

Una vez más, este nombre interno no es importante, para la plantilla, utiliza la misma ruta pero frontend... y mantén match exactamente igual:

netgen_layouts:
... lines 2 - 12
view:
item_view:
... lines 15 - 21
# default = frontend
default:
# this key is not important
recipes_default:
template: 'nglayouts/frontend/recipe_item.html.twig'
match:
item\value_type: 'doctrine_recipe'

¡Me encanta cuando las cosas son aburridas y fáciles! Vamos a crear esa plantilla. En nglayouts/, haz el directorio frontend/... y dentro, recipe_item.html.twig.

A esta plantilla le pasaremos las mismas variables que a la plantilla de elementos de administración. Esto significa que podemos, una vez más, utilizar {{ item.object }} para acceder a nuestro objeto Recipe. Vamos a imprimir la clave name para ver si las cosas funcionan:

Y... están funcionando. ¡Está vivo!

Comprobando las plantillas en el perfil de Twig

Una de mis cosas favoritas cuando empiezo a trabajar con plantillas dentro de Layouts es hacer clic en el elemento Twig de la barra de herramientas de depuración web. Aquí podemos ver realmente cómo está renderizando Layouts. Sí, renderiza layout_2.html.twig... y luego empieza a renderizar cada zona. Renderiza nuestro bloque navigation, el bloque hero, y luego, finalmente aquí abajo, la cuadrícula. Puedes ver que está usando grid/3_columns.html.twig. Esto es algo que podemos controlar en el área de administración. Haz clic en la cuadrícula. A la derecha, vemos la pestaña "Contenido". Pero también hay una pestaña "Diseño". Cambia esto a "4 columnas"... y le doy a "Publicar y continuar editando".

Si ahora refrescamos y volvemos a cargar el perfilador Twig, veremos que se renderiza4_columns.html.twig. Entonces, ¡eh! Dentro de cada columna, renderiza nuestrorecipe_item.html.twig. Esto es realmente genial de ver, y vamos a ver esto de nuevo más tarde cuando hablemos de anular las plantillas del núcleo.

CSS de Bootstrap 4

Una cosa que tengo que mencionar es que nuestra aplicación está utilizando la versión 4 de Bootstrap, no Bootstrap 5. La razón es que, en este momento, la plantilla de cuadrícula muestra el marcado de la versión 4 de Bootstrap. Si quisieras usar Bootstrap 5, es totalmente posible, pero tendrías que anular estas plantillas de columnas -como4_columns.html.twig - para modificar las clases. Anular las plantillas del núcleo es en realidad súper fácil, y hablaremos de cómo hacerlo pronto.

Personalizar nuestra plantilla frontal

Bien, ¡vamos a dar vida a esta vista frontal! Abre la plantilla de la página de inicio:main/homepage.html.twig... y desplázate hacia arriba hasta el lugar en el que hacemos un bucle con las últimas recetas. Perfecto. Lo que básicamente quiero hacer es robar el marcado de uno de estos mosaicos de recetas... y pegarlo en la plantilla del frontend:

<a href="{{ path('app_recipes_show', { slug: recipe.slug }) }}" class="text-center recipe-container p-3">
<div class="p-3 entity-img">
<img src="{{ asset(recipe.imageUrl) }}" width="auto" height="115" alt="{{ recipe.name }} image">
</div>
<h3 class="mt-3">{{ recipe.name }}</h3>
<small>{{ recipe.timeAsWords }} (prep & cook)</small>
</a>

Ahora sólo tenemos que ajustar algunas variables: en lugar de recipe.slug, tiene que ser item.object.slug. Haré una búsqueda y sustitución: sustituye recipe. poritem.object.:

<a href="{{ path('app_recipes_show', { slug: item.object.slug }) }}" class="text-center recipe-container p-3">
<div class="p-3 entity-img">
<img src="{{ asset(item.object.imageUrl) }}" width="auto" height="115" alt="{{ item.object.name }} image">
</div>
<h3 class="mt-3">{{ item.object.name }}</h3>
<small>{{ item.object.timeAsWords }} (prep & cook)</small>
</a>

Envolver los bloques en un contenedor

¡Muy bien! Veamos si ha funcionado. Muévete, refresca... ¡y lo hizo! Esto parece el frontend. ¡Somos increíbles! Excepto que falta el "canalón" que tenemos en el original. Inspecciona el elemento. Ah, la diferencia es que las columnas originales estaban dentro de un div container, que añade el margen. En el nuevo código, estamos dentro de una fila... pero no de un container.

Para arreglar esto en Layouts, vamos a añadir nuestro bloque de utilidad favorito: ¡una columna! Mueve la cuadrícula dentro de esa columna. Entonces, podríamos añadir una clase CSS como hicimos antes en la zona del héroe. Pero en su lugar, toma un atajo y marca "Envolver en contenedor".

Pulsa "Publicar y continuar editando" y actualiza. Vaya, página equivocada. Vuelve a la página de inicio y... ¡se ve muy bien! ¡Ahora está dentro de un elemento con una clasecontainer!

Esta "envoltura en el contenedor" es súper útil: añade literalmente un divextra alrededor de tu bloque con class="container" y todos los bloques lo admiten. Diablos, ni siquiera necesitamos una columna: podríamos haber marcado simplemente la opción "Envolver en contenedor" en la propia cuadrícula.

La única razón por la que he puesto esto dentro de una columna es para que también podamos añadir allí la cabecera "Últimas recetas". Arrastra un nuevo bloque "Título" dentro de la columna. ¡Sal de aquí, Apple! Dentro, escribe "Últimas recetas" y cambia a un h2.

Pulsa nuestro favorito "Publicar y continuar editando", actualiza y... ¡aún más cerca! Sólo tenemos que centrar esto... y quizás darle un pequeño margen superior. Añade dos clases al título: text-center y my-5 para darle un poco de margen vertical: ambas clases provienen de Bootstrap. Sólo estoy repitiendo las clases que mi diseñador ya utilizaba en la plantilla.

Publica eso... y cuando lo probamos... coincide exactamente. ¡Guau! Pero ahora, ¡tenemos un control total sobre las recetas que hay dentro! Podemos cambiar a una consulta diferente, cambiar el número de elementos o, dentro de un rato, podemos optar por seleccionar manualmente las recetas exactas a mostrar. Ahora también podemos incrustar listas y cuadrículas de recetas en cualquier lugar que queramos del sitio.

¡Limpieza!

Para celebrarlo, elimina todo el bloque Twig de latest_recipes:

... lines 1 - 14
{% block latest_recipes %}
<div class="container">
<h2 class="text-center my-5">Latest Recipes</h2>
<div class="row">
{% for recipe in latestRecipes %}
<div class="col-3">
<a href="{{ path('app_recipes_show', { slug: recipe.slug }) }}" class="text-center recipe-container p-3">
<div class="p-3 entity-img">
<img src="{{ asset(recipe.imageUrl) }}" width="auto" height="115" alt="{{ recipe.name }} image">
</div>
<h3 class="mt-3">{{ recipe.name }}</h3>
<small>{{ recipe.timeAsWords }} (prep & cook)</small>
</a>
</div>
{% endfor %}
</div>
<div class="text-center mt-5 text-underline"><u><a href="#">Show More</a></u></div>
</div>
{% endblock %}
... lines 34 - 70

Y, arriba en MainController, elimina la consulta, la variable, el argumento del repositorio y la declaración use:

... lines 1 - 2
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MainController extends AbstractController
{
#[Route('/', name: 'app_homepage')]
public function homepage(): Response
{
return $this->render('main/homepage.html.twig', [
]);
}
}

Cuando actualicemos, sólo tendremos una sección "Últimas recetas" procedente de nuestro bloque dinámico. Ah, pero fíjate en que en el admin de layouts, seguimos mostrando el bloquelatest_recipes... ¡aunque ya no exista! Layouts es bastante indulgente con los usuarios administradores: en lugar de lanzar un error, simplemente no renderiza nada.

Pero borremos eso... luego publiquemos... y echemos un último vistazo. ¡Me encanta!

A continuación: ahora que tenemos esta cuadrícula dentro de Layouts, podemos hacer algunas cosas interesantes con ella, como activar la paginación con Ajax.

Leave a comment!

0
Login or Register to join the conversation
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