Obtener los datos de una relació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 SubscribeNavega hasta la página de inicio y haz clic en cualquiera de las naves estelares con estado "En curso".
Ya tenemos una lista de las piezas... más o menos... ¡todas están codificadas!
Ahora, ¿cómo obtenemos las piezas relacionadas con esta nave?
Abre el controlador de esta página: src/Controller/StarshipController.php
Consulta de piezas relacionadas como cualquier otra propiedad
Para consultar las piezas, lo normal sería autoconectar el StarshipPartRepository
. Comienza de la misma manera aquí con un argumento StarshipPartRepository $partRepository
:
// ... lines 1 - 5 | |
use App\Repository\StarshipPartRepository; | |
// ... lines 7 - 12 | |
class StarshipController extends AbstractController | |
{ | |
'/starships/{slug}', name: 'app_starship_show') | (|
public function show( | |
// ... lines 17 - 18 | |
StarshipPartRepository $partRepository, | |
): Response { | |
// ... lines 21 - 25 | |
} | |
} |
A continuación, establece $parts
, en $partRepository->findBy()
:
Esto es bastante estándar: si quieres consultar dónde una propiedad es igual a un valor, utiliza findBy()
y pasa el nombre de la propiedad y el valor. Cuando se trata de relaciones, ¡es lo mismo!
$parts = $partRepository->findBy(['starship' => $ship])
.
Y no, no vamos a hacer Starship ID
ni nada por el estilo. ¡Mantén los identificadores fuera de esto! En su lugar, pasa el propio objeto Starship
. Puedes pasarle id
si te da pereza, pero en el espíritu de la Doctrine, las relaciones y el pensamiento sobre objetos, lo mejor es pasarle el objeto Starship
entero.
Vamos a depurar y a ver qué tenemos: dd($parts)
:
// ... lines 1 - 12 | |
class StarshipController extends AbstractController | |
{ | |
// ... line 15 | |
public function show( | |
// ... lines 17 - 18 | |
StarshipPartRepository $partRepository, | |
): Response { | |
$parts = $partRepository->findBy(['starship' => $ship]); | |
dd($parts); | |
// ... lines 23 - 25 | |
} | |
} |
Actualiza, y ¡voilá! Una matriz de 10 objetos StarshipPart
, todos relacionados con este Starship
. Impresionante, ¿verdad? Si piensas así, agárrate los pantalones.
Cómo coger las partes relacionadas fácilmente
Sustituye $parts
por $ship->getParts()
:
// ... lines 1 - 12 | |
class StarshipController extends AbstractController | |
{ | |
// ... line 15 | |
public function show( | |
// ... lines 17 - 19 | |
): Response { | |
// ... line 21 | |
dd($ship->getParts()); | |
// ... lines 23 - 25 | |
} | |
} |
¡Actualiza! En lugar de una matriz de objetos StarshipPart
, obtenemos un objetoPersistentCollection
que parece... vacío. ¿Recuerdas el ArrayCollection
que make:entity
añadió a nuestro constructorStarship
? PersistentCollection
y ArrayCollection
forman parte de la misma familia de colecciones. Son objetos pero actúan como matrices. Genial... pero ¿por qué esta colección parece vacía? Es porque Doctrine es inteligente: no consulta las partes hasta que las necesitamos. Realiza un bucle sobre $ship->getParts()
y vuelca $part
:
// ... lines 1 - 12 | |
class StarshipController extends AbstractController | |
{ | |
// ... line 15 | |
public function show( | |
// ... lines 17 - 19 | |
): Response { | |
// ... lines 21 - 22 | |
foreach ($ship->getParts() as $part) { | |
dump($part); | |
} | |
// ... lines 26 - 29 | |
} | |
} |
De repente, esa colección de aspecto vacío está llena de los 10 objetos StarshipPart
. ¡Mágico!
Consultas perezosas de relación
Aquí hay dos consultas en juego. La primera es para el Starship
, y la segunda es para todos sus StarshipPart
s. La primera proviene de la consulta de Symfony para el Starship
basada en el slug. La segunda es más interesante: ocurre en el momento en que foreach
pasa por encima de parts
. En ese preciso instante Doctrine dice:
Acabo de acordarme: En realidad no tengo los datos de
StarshipPart
s para esteStarship
. Voy a buscarlos.
¿No es increíble? Me dan ganas de hacer una fiesta para Doctrine.
Ordenar y repasar partes
Deshazte por completo de la variable parts
... y elimina StarshipPartRepository
: era demasiado trabajo. En su lugar, establece una variable parts
en $ship->getParts()
:
// ... lines 1 - 12 | |
class StarshipController extends AbstractController | |
{ | |
'/starships/{slug}', name: 'app_starship_show') | (|
public function show( | |
#[MapEntity(mapping: ['slug' => 'slug'])] | |
Starship $ship, | |
): Response { | |
return $this->render('starship/show.html.twig', [ | |
'ship' => $ship, | |
'parts' => $ship->getParts(), | |
]); | |
} | |
} |
Ahora que tenemos nuestra nueva y brillante variable parts
, haz un bucle sobre ella en la plantilla. Abre templates/starship/show.html.twig
y sustituye la parte codificada por nuestro bucle: for part in parts
, part.name
, part.price
,part.notes
, endfor
:
// ... lines 1 - 4 | |
{% block body %} | |
// ... lines 6 - 19 | |
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8"> | |
// ... lines 21 - 25 | |
<div class="space-y-5"> | |
<div class="mt-8 max-w-xl mx-auto"> | |
<div class="px-8 pt-8"> | |
// ... lines 29 - 61 | |
<ul class="text-sm font-medium text-slate-400 tracking-wide"> | |
{% for part in parts %} | |
<li class="border-b border-slate-600 py-2"> | |
<span class="block text-white font-semibold"> | |
{{ part.name }} (✦ {{ part.price }}) | |
</span> | |
<span class="text-xs text-slate-500 italic"> | |
{{ part.notes }} | |
</span> | |
</li> | |
{% endfor %} | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
¿Sigue siendo demasiado trabajo?
¡Y lo hemos conseguido! Hemos conseguido mostrar las 10 partes relacionadas, sin hacer mucho trabajo gracias a $ship->getParts()
.
¿Pero sabes una cosa? Incluso esto es demasiado trabajo. Deshazte por completo de la variable parts
:
// ... lines 1 - 12 | |
class StarshipController extends AbstractController | |
{ | |
// ... line 15 | |
public function show( | |
// ... lines 17 - 18 | |
): Response { | |
return $this->render('starship/show.html.twig', [ | |
'ship' => $ship, | |
]); | |
} | |
} |
for part in ship.parts
:
// ... lines 1 - 4 | |
{% block body %} | |
// ... lines 6 - 19 | |
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8"> | |
// ... lines 21 - 25 | |
<div class="space-y-5"> | |
<div class="mt-8 max-w-xl mx-auto"> | |
<div class="px-8 pt-8"> | |
// ... lines 29 - 61 | |
<ul class="text-sm font-medium text-slate-400 tracking-wide"> | |
{% for part in ship.parts %} | |
// ... lines 64 - 71 | |
{% endfor %} | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Y... ¡todavía no se ha roto! Para divertirnos, mostremos también el número de piezas de esta nave. ship.parts|length
:
// ... lines 1 - 4 | |
{% block body %} | |
// ... lines 6 - 19 | |
<div class="md:flex justify-center space-x-3 mt-5 px-4 lg:px-8"> | |
// ... lines 21 - 25 | |
<div class="space-y-5"> | |
<div class="mt-8 max-w-xl mx-auto"> | |
<div class="px-8 pt-8"> | |
// ... lines 29 - 58 | |
<h4 class="text-xs text-slate-300 font-semibold mt-2 uppercase"> | |
Parts ({{ ship.parts|length }}) | |
</h4> | |
// ... lines 62 - 73 | |
</div> | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
Seguimos teniendo dos consultas, pero Doctrine, una vez más, es inteligente. Sabe que ya hemos consultado todas las StarshipPart
s, así que cuando las contemos, en realidad no necesitamos hacer otra consulta de recuento.
A continuación hablaremos de un tema a menudo incomprendido en las relaciones Doctrine: el lado propio y el inverso de cada relación.