Persistir en cascada
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 SubscribeEcha 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 entidadStarshipDroid
.
Déjame que te lo traduzca:
Oye, estás guardando este
Starship
y tiene unStarshipDroid
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:
// ... 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
:
// ... 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:
// ... 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 ago
para darle estilo:
// ... 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!
Thank you Ryan! It's wonderful to be able to accompany you in this tutorial. It's always nice and I always learn something new or at least refresh some knowledge. You are a great motivation and inspiration for me - and have been for all these great years.