Controladores de alta tecnología: Entidades de Autoinyección
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 SubscribeCuando entramos en una nave desde nuestra página de inicio y vemos su página de presentación, esta URL no es muy bonita ni memorable. Es sólo el ID de la nave. Imagínate que Jean-Luc Picard anunciara que era el capitán del USS 43 en lugar del Enterprise. ¡Penoso!
Cambiemos esto para utilizar en su lugar nuestro nuevo campo slug
. Al igual que id
, es único, por lo que podemos utilizarlo para encontrar un único barco en la base de datos.
Pero antes quiero enseñarte algo superguay. AbreStarshipController::show()
. Estamos inyectando el $id
de nuestro parámetro de ruta y el servicio StarshipRepository
para encontrar el barco a partir de este ID. Luego tenemos la lógica para lanzar un 404 si no se encuentra el barco.
Inyectar directamente Starship
Sustituye todos los argumentos por sólo Starship $ship
, luego, elimina toda esta lógica de encontrar y no encontrar:
// ... lines 1 - 9 | |
class StarshipController extends AbstractController | |
{ | |
'/starships/{id<\d+>}', name: 'app_starship_show') | (|
public function show(Starship $ship): Response | |
{ | |
return $this->render('starship/show.html.twig', [ | |
'ship' => $ship, | |
]); | |
} | |
} |
Esto sí que es un controlador delgado: me encanta. Si estás diciendo "pero Starship no es un servicio", tienes razón, pero ten paciencia conmigo.
De vuelta en la aplicación, estamos en la página del programa Starship. Actualiza... y... ¡aún funciona! Intentemos visitar una nave que sabemos que no existe: una con ID 999. Obtenemos un error 404. Obtenemos un error 404. Seguimos teniendo la misma lógica que antes... ¿Cómo?
Las entidades no son servicios... eso sigue siendo, y siempre, cierto. Mira en nuestro controladorMainController::homepage()
. Estamos inyectando el objeto Request
. Esto tampoco es un servicio. Si intentaras autoconectar esto en el constructor de un servicio, obtendrías un error.
Resolvedores de valores del controlador
Los controladores son especiales. Cuando Symfony llama a un método de controlador, primero mira todos los argumentos y los pasa a través de algo llamado "resolvedores de valores de controlador". Hay varios, y ya hemos utilizado unos cuantos, aunque no lo sabíamos. Hay un RequestValueResolver
para inyectar el objeto Request
y unServiceValueResolver
si un argumento tiene un tipo de servicio.
La integración con Doctrine de Symfony proporciona un EntityValueResolver
. Así es como podemos inyectar la entidad Starship
. Funciona porque hemos indicado Starship
, una entidad Doctrine válida, y tenemos un parámetro de ruta id
. Como toda entidad tiene un id
, el resolver busca automáticamente la entidad y nos la pasa. Si no encuentra la entidad, lanza un 404. ¡Me encanta!
Utilizar slug
en la URL
Volvamos a nuestra misión: utilizar la nave slug
en la URL en lugar del ID. En primer lugar, actualiza el atributo #[Route]
a /starship/{slug}
:
// ... lines 1 - 9 | |
class StarshipController extends AbstractController | |
{ | |
'/starships/{slug}', name: 'app_starship_show') | (|
public function show(Starship $ship): Response | |
// ... lines 14 - 18 | |
} |
A continuación, tenemos que actualizar todos los lugares donde generamos la URL para esta ruta. No te preocupes, sólo hay 2.
Empieza por templates/main/homepage.html.twig
. En la función path
, sustituye id: ship.id
por slug: ship.slug
:
// ... lines 1 - 4 | |
{% block body %} | |
<main class="flex flex-col lg:flex-row"> | |
// ... lines 7 - 8 | |
<div class="px-12 pt-10 w-full"> | |
// ... lines 10 - 17 | |
<div class="space-y-5"> | |
{% for ship in ships %} | |
<div class="bg-[#16202A] rounded-2xl pl-5 py-5 pr-11 flex flex-col min-[1174px]:flex-row min-[1174px]:justify-between"> | |
<div class="flex justify-center min-[1174px]:justify-start"> | |
// ... line 22 | |
<div class="ml-5"> | |
// ... lines 24 - 27 | |
<h4 class="text-[22px] pt-1 font-semibold"> | |
<a | |
class="hover:text-slate-200" | |
href="{{ path('app_starship_show', { slug: ship.slug }) }}" | |
>{{ ship.name }}</a> | |
</h4> | |
// ... lines 34 - 36 | |
</div> | |
</div> | |
// ... lines 39 - 49 | |
</div> | |
{% endfor %} | |
</div> | |
// ... lines 53 - 68 | |
</div> | |
</main> | |
{% endblock %} |
Ahora, abre templates/main/_shipStatusAside.html.twig
, busca "show", y en este path
sustituye id:
myShip.id por slug: myShip.slug
:
<aside | |
// ... lines 2 - 3 | |
> | |
// ... lines 5 - 11 | |
<div> | |
<div class="flex flex-col space-y-1.5"> | |
// ... lines 14 - 17 | |
<h3 class="tracking-tight text-[22px] font-semibold"> | |
<a class="hover:underline" href="{{ path('app_starship_show', { | |
slug: myShip.slug | |
}) }}">{{ myShip.name }}</a> | |
</h3> | |
</div> | |
// ... lines 24 - 34 | |
</div> | |
</aside> |
Vuelve a nuestra aplicación y pulsa "Atrás" para ir a la página de inicio. Pasa el ratón por encima del enlace de un barco y mira la URL. ¡Es mucho más bonita! Haz clic en el enlace.
¡Alerta roja!
No se puede autocablear el argumento $nave...".
El problema es que cuando no hay un comodín de ruta llamado id
, vuelve a intentar autoconectar Starship
como si fuera un servicio. Cuando el comodín de ruta no se llama id
, tenemos que ayudarle un poco.
#[MapEntity]
Atribuye
De vuelta a StarshipController::show()
, mueve Starship $ship
a su propia línea para dejarnos algo de espacio. Sobre él, añade un atributo: #[MapEntity]
con un array con una clave de slug
-este es el nombre del parámetro de ruta- y un valor también de slug
-este es el nombre de la propiedad sobre la que debe consultar-:
// ... lines 1 - 10 | |
class StarshipController extends AbstractController | |
{ | |
// ... line 13 | |
public function show( | |
#[MapEntity(mapping: ['slug' => 'slug'])] | |
Starship $ship, | |
): Response { | |
// ... lines 18 - 20 | |
} | |
} |
Vuelve a la aplicación y actualiza. Vuelve a funcionar, ¡alerta roja cancelada!
Prueba a poner texto aleatorio para el slug y... ¡404! ¡Perfecto!
Ahora las URL de nuestras naves son bonitas, legibles y aptas para el SEO
Volar por el espacio es peligroso. A veces, las naves estelares sufren "desmontajes rápidos no programados"... o, en otras palabras, explotan. Necesitamos una forma de eliminar de nuestra base de datos las naves que ya no... err... existen. A continuación, veremos cómo eliminar entidades con Doctrine.