Login to bookmark this video
Buy Access to Course
24.

Persistir en cascada

|

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

Echa un vistazo a este error: ¡es una pasada!

Se ha encontrado una entidad a través de la relación Starship.droids que no estaba configurada para operaciones de "persistencia en cascada" para la entidad StarshipDroid.

Déjame que te lo traduzca:

Oye, estás guardando este Starship y tiene un StarshipDroid adjunta. Eso es genial, pero te olvidaste de decirme que persistiera la > entidad
StarshipDroid.
¿Qué quieres que haga?

Pero, de nuevo, desde dentro de Starship, no podemos hacer que el gestor de entidades diga $manager->persist($starshipDroid).

Aprovechar el poder de cascade=['persist']

La solución es utilizar algo llamado persistir en cascada.

Desplázate hasta la propiedad $starshipDroids, y busca la opción OneToMany. Añade una nueva opción: cascade. La escribiré manualmente. Establécela como una matriz conpersist dentro:

262 lines | src/Entity/Starship.php
// ... lines 1 - 15
class Starship
{
// ... lines 18 - 53
#[ORM\OneToMany(targetEntity: StarshipDroid::class, mappedBy: 'starship', cascade: ['persist'])]
private Collection $starshipDroids;
// ... lines 56 - 260
}

Estamos creando una especie de efecto dominó. Si alguien persiste en este starship, vamos a enviar en cascada esa persistencia a todas las relaciones adjuntas.

Una advertencia: utiliza este poder con prudencia. Hace que tu código sea más automático, lo cual es estupendo, pero también puede dificultar la detección de errores.

Pero en este caso, es exactamente la solución que necesitamos.

Dale otra vuelta a esas fijaciones:

symfony console doctrine:fixtures:load

Volver a añadir droides

Ya estamos otra vez en marcha. Podemos volver a utilizar ship->addDroid(). Pero aún quiero crear una flota de starships con droids unido a ellos.

Elimina todo el código manual y vuelve a poner la propiedad droids en elStarshipFactory:

60 lines | src/DataFixtures/AppFixtures.php
// ... lines 1 - 13
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
// ... lines 18 - 51
DroidFactory::createMany(100);
StarshipFactory::createMany(100, fn() => [
'droids' => DroidFactory::randomRange(1, 5),
]);
StarshipPartFactory::createMany(100);
}
}

Vuelve a encender los dispositivos:

symfony console doctrine:fixtures:load

¿Adivina qué? ¡Funcionan!

Entre bastidores, Foundry está llamando a addDroid() en cada Starship para cada droid. Y acabamos de demostrar que addDroid() vuelve a funcionar.

¡La creación de la entidad de unión StarshipDroid está ahora oculta a toda nuestra base de código!

Control más fino con assignedAt

Pero, ¿y si quieres añadir un droid a un starship y controlar la propiedad assignedAt? Añade un argumento para addDroid() en Starship: un DateTimeImmutable. Hazlo opcional para mantener la flexibilidad. Entonces, después de crear el StarshipDroid, establece el $assignedAt si lo hemos pasado:

265 lines | src/Entity/Starship.php
// ... lines 1 - 15
class Starship
{
// ... lines 18 - 207
public function addDroid(Droid $droid, \DateTimeImmutable $assignedAt = null): static
{
if (!$this->getDroids()->contains($droid)) {
// ... lines 211 - 213
if ($assignedAt) {
$starshipDroid->setAssignedAt($assignedAt);
}
// ... line 217
}
// ... lines 219 - 220
}
// ... lines 222 - 263
}

Genial... pero hay un pequeño problema. Foundry no nos permite controlar el campo assignedAt. Así que, si quieres asignar algún droids en un momento concreto, tendrás que coger el volante manualmente.

Visualización de assignedAt

Por último, hagamos que ese assignedAt sea visible en nuestro sitio. Para ello necesitaremos el objeto de entidad de uniónStarshipDroid. Es un poco más de trabajo, pero totalmente factible.

Cambia el bucle a for starshipDroid in ship.starshipDroids. A continuación,starshipDroid.droid.name y starshipDroid.assignedAt con el filtro agopara darle estilo:

90 lines | templates/starship/show.html.twig
// ... 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
<p class="text-[22px] font-semibold">
{% for starshipDroid in ship.starshipDroids %}
{{ starshipDroid.droid.name }} (assigned {{ starshipDroid.assignedAt|ago }} {% if not loop.last %}, {% endif %}
// ... lines 65 - 66
{% endfor %}
</p>
// ... lines 69 - 84
</div>
</div>
</div>
</div>
{% endblock %}

Actualiza y... ya podemos ver cuándo se asignó cada droid.

¡Eso es todo, amigos! Hemos explorado los rincones más profundos de las relaciones Doctrine, incluso las esquivas muchos-a-muchos con campos adicionales. Como siempre, si tienes alguna pregunta, déjala en los comentarios a continuación. ¡Estamos todos juntos en esto!