Listado de piezas
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeNueva misión: necesitamos una página que enumere todas las piezas disponibles. Nuestro equipo de ventas ferengi la utilizará para el clásico upselling. Ya sabes, lo de siempre:
Oye, acabas de comprar una nave estelar, ¿qué tal unos nuevos y relucientes organizadores de cristal de dilitio o estabilizadores de portavasos?
Utilicemos MakerBundle para adelantarnos. Busca tu terminal y ejecuta:
symfony console make:controller
Llámalo... espera... PartController
. Brillante! Para mantener las cosas centradas, di no a las pruebas.
¡Listo! Una clase y una plantilla. Hasta aquí, todo perfecto. Echa un vistazo al nuevo PartController
:
// ... lines 1 - 2 | |
namespace App\Controller; | |
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\Routing\Attribute\Route; | |
final class PartController extends AbstractController | |
{ | |
'/part', name: 'app_part') | (|
public function index(): Response | |
{ | |
return $this->render('part/index.html.twig', [ | |
'controller_name' => 'PartController', | |
]); | |
} | |
} |
No hay mucho que ver: renderiza una plantilla. Vaya.
Cambia la URL a /parts
, y renómbrala a app_part_index
:
// ... lines 1 - 8 | |
final class PartController extends AbstractController | |
{ | |
'/parts', name: 'app_part_index') | (|
public function index(): Response | |
// ... lines 13 - 16 | |
} | |
} |
Copia el nombre de la ruta para que podamos enlazar con ella... y abre base.html.twig
.
Enlace a la página de piezas
¿Recuerdas el enlace "Acerca de" que está ahí sin hacer nada? Cámbialo y conviértelo en un enlace "Piezas". Establece href
en {{ path('app_part_index') }}
:
// ... line 1 | |
<html> | |
// ... lines 3 - 13 | |
<body class="text-white" style="background: radial-gradient(102.21% 102.21% at 50% 28.75%, #00121C 42.62%, #013954 100%);"> | |
<div class="flex flex-col justify-between min-h-screen relative"> | |
<div> | |
<header class="h-[114px] shrink-0 flex flex-col sm:flex-row items-center sm:justify-between py-4 sm:py-0 px-6 border-b border-white/20 shadow-md"> | |
// ... lines 18 - 20 | |
<nav class="flex space-x-4 font-semibold"> | |
// ... lines 22 - 24 | |
<a class="hover:text-amber-400 pt-2" href="{{ path('app_part_index') }}"> | |
Parts | |
</a> | |
// ... lines 28 - 33 | |
</nav> | |
</header> | |
// ... line 36 | |
</div> | |
// ... lines 38 - 40 | |
</div> | |
</body> | |
</html> |
Dirígete a la página de inicio, haz clic en nuestro recién estrenado enlace y... bueno, no es lo más bonito, ¡pero funciona!
Antes de celebrarlo, deberíamos cambiar el título de la poco inspiradora Hello PartController
. Abre templates/part/index.html.twig
. Ya estamos modificando el bloque title
, así que hagámoslo algo interesante como Parts
:
// ... lines 1 - 2 | |
{% block title %}Parts!{% endblock %} | |
// ... lines 4 - 21 |
Añadir algo de sustancia: Recorrer las partes en bucle
Para hacer un bucle sobre las piezas, en PartController
, necesitamos consultar todas las piezas.
Añade un argumento StarshipPartRepository
para autoinstalarlo. Llámalo como quieras, como $leeroyJenkins
o... $repository
. Para obtener todas las piezas, es sencillo: $parts = repository->findAll()
:
// ... lines 1 - 4 | |
use App\Repository\StarshipPartRepository; | |
// ... lines 6 - 9 | |
final class PartController extends AbstractController | |
{ | |
'/parts', name: 'app_part_index') | (|
public function index(StarshipPartRepository $repository): Response | |
{ | |
$parts = $repository->findAll(); | |
// ... lines 16 - 19 | |
} | |
} |
Imprimir piezas en la plantilla
Ahora que tenemos esta variable parts
en nuestra plantilla, podemos hacer un bucle sobre ella:
// ... lines 1 - 9 | |
final class PartController extends AbstractController | |
{ | |
'/parts', name: 'app_part_index') | (|
public function index(StarshipPartRepository $repository): Response | |
{ | |
$parts = $repository->findAll(); | |
return $this->render('part/index.html.twig', [ | |
'parts' => $parts, | |
]); | |
} | |
} |
Para animar las cosas, pegaré esta plantilla:
{% extends 'base.html.twig' %} | |
{% block title %}Parts!{% endblock %} | |
{% block body %} | |
<div class="space-y-5 mx-5"> | |
{% for part in parts %} | |
<div class="bg-[#16202A] rounded-2xl p-5 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between items-center"> | |
<div class="flex items-center"> | |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" | |
class="h-[83px] w-[84px] fill-current {{ cycle(['text-red-400', 'text-blue-400', 'text-green-400', 'text-purple-400', 'text-yellow-400'], loop.index0) }}" | |
><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M308.5 135.3c7.1-6.3 9.9-16.2 6.2-25c-2.3-5.3-4.8-10.5-7.6-15.5L304 89.4c-3-5-6.3-9.9-9.8-14.6c-5.7-7.6-15.7-10.1-24.7-7.1l-28.2 9.3c-10.7-8.8-23-16-36.2-20.9L199 27.1c-1.9-9.3-9.1-16.7-18.5-17.8C173.9 8.4 167.2 8 160.4 8l-.7 0c-6.8 0-13.5 .4-20.1 1.2c-9.4 1.1-16.6 8.6-18.5 17.8L115 56.1c-13.3 5-25.5 12.1-36.2 20.9L50.5 67.8c-9-3-19-.5-24.7 7.1c-3.5 4.7-6.8 9.6-9.9 14.6l-3 5.3c-2.8 5-5.3 10.2-7.6 15.6c-3.7 8.7-.9 18.6 6.2 25l22.2 19.8C32.6 161.9 32 168.9 32 176s.6 14.1 1.7 20.9L11.5 216.7c-7.1 6.3-9.9 16.2-6.2 25c2.3 5.3 4.8 10.5 7.6 15.6l3 5.2c3 5.1 6.3 9.9 9.9 14.6c5.7 7.6 15.7 10.1 24.7 7.1l28.2-9.3c10.7 8.8 23 16 36.2 20.9l6.1 29.1c1.9 9.3 9.1 16.7 18.5 17.8c6.7 .8 13.5 1.2 20.4 1.2s13.7-.4 20.4-1.2c9.4-1.1 16.6-8.6 18.5-17.8l6.1-29.1c13.3-5 25.5-12.1 36.2-20.9l28.2 9.3c9 3 19 .5 24.7-7.1c3.5-4.7 6.8-9.5 9.8-14.6l3.1-5.4c2.8-5 5.3-10.2 7.6-15.5c3.7-8.7 .9-18.6-6.2-25l-22.2-19.8c1.1-6.8 1.7-13.8 1.7-20.9s-.6-14.1-1.7-20.9l22.2-19.8zM112 176a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zM504.7 500.5c6.3 7.1 16.2 9.9 25 6.2c5.3-2.3 10.5-4.8 15.5-7.6l5.4-3.1c5-3 9.9-6.3 14.6-9.8c7.6-5.7 10.1-15.7 7.1-24.7l-9.3-28.2c8.8-10.7 16-23 20.9-36.2l29.1-6.1c9.3-1.9 16.7-9.1 17.8-18.5c.8-6.7 1.2-13.5 1.2-20.4s-.4-13.7-1.2-20.4c-1.1-9.4-8.6-16.6-17.8-18.5L583.9 307c-5-13.3-12.1-25.5-20.9-36.2l9.3-28.2c3-9 .5-19-7.1-24.7c-4.7-3.5-9.6-6.8-14.6-9.9l-5.3-3c-5-2.8-10.2-5.3-15.6-7.6c-8.7-3.7-18.6-.9-25 6.2l-19.8 22.2c-6.8-1.1-13.8-1.7-20.9-1.7s-14.1 .6-20.9 1.7l-19.8-22.2c-6.3-7.1-16.2-9.9-25-6.2c-5.3 2.3-10.5 4.8-15.6 7.6l-5.2 3c-5.1 3-9.9 6.3-14.6 9.9c-7.6 5.7-10.1 15.7-7.1 24.7l9.3 28.2c-8.8 10.7-16 23-20.9 36.2L315.1 313c-9.3 1.9-16.7 9.1-17.8 18.5c-.8 6.7-1.2 13.5-1.2 20.4s.4 13.7 1.2 20.4c1.1 9.4 8.6 16.6 17.8 18.5l29.1 6.1c5 13.3 12.1 25.5 20.9 36.2l-9.3 28.2c-3 9-.5 19 7.1 24.7c4.7 3.5 9.5 6.8 14.6 9.8l5.4 3.1c5 2.8 10.2 5.3 15.5 7.6c8.7 3.7 18.6 .9 25-6.2l19.8-22.2c6.8 1.1 13.8 1.7 20.9 1.7s14.1-.6 20.9-1.7l19.8 22.2zM464 304a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"/></svg> | |
<div class="ml-5"> | |
<h4 class="text-[22px] font-semibold"> | |
<a class="hover:text-slate-200" href="#"> | |
{{ part.name }} <span class="text-sm text-slate-400">(assigned to SHIP NAME)</span> | |
</a> | |
</h4> | |
<div class="text-lg text-green-400 font-medium">✦{{ part.price }}</div> | |
<p class="text-slate-400 text-sm">{{ part.notes }}</p> | |
</div> | |
</div> | |
</div> | |
{% endfor %} | |
</div> | |
{% endblock %} |
Es sólo un montón de cosas para que quede bonito. Puedes obtener este código del bloque de código de esta página.
Actualiza y... ¡mucho mejor!
Un pequeño truco: Utilizar la función Ciclo
Una cosa interesante que estoy utilizando aquí es la función cycle()
:
// ... lines 1 - 4 | |
{% block body %} | |
<div class="space-y-5 mx-5"> | |
{% for part in parts %} | |
<div class="bg-[#16202A] rounded-2xl p-5 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between items-center"> | |
<div class="flex items-center"> | |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" | |
class="h-[83px] w-[84px] fill-current {{ cycle(['text-red-400', 'text-blue-400', 'text-green-400', 'text-purple-400', 'text-yellow-400'], loop.index0) }}" | |
// ... lines 12 - 21 | |
</div> | |
</div> | |
{% endfor %} | |
</div> | |
{% endblock %} |
Quería dar a cada engranaje un color aleatorio para que tuviera un aspecto más atractivo. La función cycle()
nos permite pasar un montón de cadenas, y luego loop.index 0
las recorre en ciclos. Es un pequeño toque, pero añade ese toque que tanto gusta a los ferengis.
Por último, sustituye assigned to SHIP NAME
por {{ part.ship }}
- esta vez, no estoy utilizando ship.part
, sino el otro lado de la relación,part.ship.name
. Uy, me equivoqué, debería ser part.starship.name
:
// ... lines 1 - 4 | |
{% block body %} | |
<div class="space-y-5 mx-5"> | |
{% for part in parts %} | |
<div class="bg-[#16202A] rounded-2xl p-5 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between items-center"> | |
<div class="flex items-center"> | |
// ... lines 10 - 12 | |
<div class="ml-5"> | |
<h4 class="text-[22px] font-semibold"> | |
<a class="hover:text-slate-200" href="#"> | |
{{ part.name }} <span class="text-sm text-slate-400">(assigned to {{ part.starship.name }})</span> | |
</a> | |
</h4> | |
// ... lines 19 - 20 | |
</div> | |
</div> | |
</div> | |
{% endfor %} | |
</div> | |
{% endblock %} |
Y... ¡ya está!
A continuación, hablaremos de las uniones. ¡Únete! Lo siento, no pude resistirme.