Login to bookmark this video
Buy Access to Course
21.

Persistencia de la relación muchos-muchos más compleja

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Hemos refactorizado nuestra relación de muchos a muchos para incluir una entidad de unión llamada StarshipDroid, en lugar de confiar en que Doctrine cree la tabla de unión por nosotros. Recarga nuestras instalaciones, pero no te quites el sombrero:

symfony console doctrine:fixtures:load

¡Error!

Propiedad no definida: App\Entity\Starship::$droids

Este error está siendo escupido desde Starship línea 205. ¿El culpable? Nuestro métodogetDroids(). Pues duh, ¡acabamos de eliminar la propiedad droids! La solución rápida, duh de nuevo, simplemente coméntalo:

59 lines | src/DataFixtures/AppFixtures.php
// ... lines 1 - 12
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// ... lines 17 - 52
StarshipFactory::createMany(100, fn() => [
//'droids' => DroidFactory::randomRange(1, 5),
]);
// ... line 56
}
}

Y ¡hurra! Las fijaciones vuelven a funcionar:

symfony console doctrine:fixtures:load

Creación de la entidad de unión

Para descubrir el arreglo correcto, hagamos algunas cosas manualmente:$ship = StarshipFactory, podríamos utilizar createOne(), pero en su lugar cojamos uno al azar. Utiliza también el truco de _real() para obtener el objeto real, no un proxy. Luego haz lo mismo con$droid = DroidFactory, de nuevo cogiendo uno al azar y llamando a_real() con él:

63 lines | src/DataFixtures/AppFixtures.php
// ... lines 1 - 12
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// ... lines 17 - 57
$ship = StarshipFactory::random()->_real();
$droid = DroidFactory::random()->_real();
// ... line 60
}
}

Relacionar a través de la Entidad de Unión

Antes podíamos utilizar $ship->addDroid($droid) para añadir un droide aStarship. Pero ¡ya no! Está haciendo referencia a la obsoleta propiedad droids. Ahora se llama starshipDroids, y como habrás adivinado, es una colección de entidades StarshipDroid. Deshazte de$ship->addDroid() y en su lugar di que $starshipDroid es igual anew StarshipDroid(), luego $starshipDroid->setDroid(), no $ship sino $droid. Y pon $starshipDroid->setStarship($ship).

Estamos creando manualmente la entidad y estableciendo esas relaciones de muchos a uno. Por último, como las estamos ensamblando a mano, necesitamos persistirlas y vaciarlas utilizando $manager->persist($starshipDroid), y $manager->flush():

68 lines | src/DataFixtures/AppFixtures.php
// ... lines 1 - 5
use App\Entity\StarshipDroid;
// ... lines 7 - 13
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// ... lines 18 - 58
$ship = StarshipFactory::random()->_real();
$droid = DroidFactory::random()->_real();
$starshipDroid = new StarshipDroid();
$starshipDroid->setStarship($ship);
$starshipDroid->setDroid($droid);
$manager->persist($starshipDroid);
$manager->flush();
}
}

Sin duda es más trabajo, pero es bastante sencillo. Dale una vuelta a los accesorios:

symfony console doctrine:fixtures:load

Y echa un vistazo a la base de datos con:

symfony console doctrine:query:sql "SELECT * FROM starship_droid"

Estamos seleccionando en esa tabla de unión y ¡sí! Una entrada para el Starship, y otra para el Droid. Hasta aquí, todo bien. Actualiza la página de inicio. ¡Otro error!

[Error semántico] línea 0, col 55 cerca de 'droids WHERE': La clase App\Entity\Starship no tiene ninguna asociación llamada droids.

Parece que tenemos un problema de consulta entre manos.

Solucionar el problema de consulta

Es hora de arremangarse y sumergirse en src/Repository/StarshipRepository. Nuestra unión está teniendo un pequeño problema. Nos estamos uniendo a s.droids, pero la propiedad droidsha abandonado el edificio. Tenemos que unirnos a starshipDroids. Cambia s.droidspor s.starshipDroids. Y para mayor claridad, llámalo starshipDroid, porque es lo que realmente es. Ahora cuéntalos en lugar del inexistente droids:

69 lines | src/Repository/StarshipRepository.php
// ... lines 1 - 14
class StarshipRepository extends ServiceEntityRepository
{
// ... lines 17 - 24
public function findIncompleteOrderedByDroidCount(): Pagerfanta
{
$query = $this->createQueryBuilder('s')
// ... line 28
->orderBy('COUNT(starshipDroid)', 'ASC')
->leftJoin('s.starshipDroids', 'starshipDroid')
// ... lines 31 - 36
}
// ... lines 38 - 67
}

Una vez solucionado esto, actualizamos la página de inicio y... ¡otro error! Es:

Advertencia: Propiedad no definida: App\Entity\Starship::$droids.

Esto viene de ship.droidNames en la plantilla de la página de inicio. Sabemos que cuando llamamos a ship.droidNames, éste llama a $starship->getDroidNames()y seguimos haciendo referencia a la propiedad droids:

259 lines | src/Entity/Starship.php
// ... lines 1 - 15
class Starship
{
// ... lines 18 - 223
public function getDroidNames(): string
{
return implode(', ', $this->droids->map(fn(Droid $droid) => $droid->getName())->toArray());
}
// ... lines 228 - 257
}

Ocultar esa Entidad Unida

A continuación, vamos a ocultar la entidad de unión para que funcione exactamente igual que la relación MuchosMuchos que teníamos antes. ¡Magia!