Establecer la relación
Vale, pero ¿cómo establecemos realmente la relación? ¿Cómo decimos
¿Este
StarshipPartpertenece a esteStarship?
Hasta ahora, hemos estado trabajando en AppFixtures con Foundry. Volveremos a Foundry dentro de un rato, pero volvamos a la vieja escuela por un minuto para ver cómo funciona todo esto.
Empieza con new Starship()... luego pegaré algo de código para establecer las propiedades necesarias. A continuación, añade $manager->persist($starship):
| // ... lines 1 - 4 | |
| use App\Entity\Starship; | |
| // ... lines 6 - 12 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| StarshipFactory::createOne([ | |
| // ... lines 18 - 22 | |
| ]); | |
| $starship = new Starship(); | |
| $starship->setName('USS Taco Tuesday'); | |
| $starship->setClass('Tex-Mex'); | |
| $starship->checkIn(); | |
| $starship->setCaptain('James T. Nacho'); | |
| $manager->persist($starship); | |
| // ... lines 31 - 55 | |
| } | |
| } |
A continuación crea un nuevo StarshipPart y al igual que antes, pegaré código para rellenar las propiedades. A continuación, asegúrate de que esto se guarda con $manager->persist($part), y por último, $manager->flush():
| // ... lines 1 - 4 | |
| use App\Entity\Starship; | |
| use App\Entity\StarshipPart; | |
| // ... lines 7 - 12 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| // ... lines 17 - 24 | |
| $starship = new Starship(); | |
| $starship->setName('USS Taco Tuesday'); | |
| $starship->setClass('Tex-Mex'); | |
| $starship->checkIn(); | |
| $starship->setCaptain('James T. Nacho'); | |
| $manager->persist($starship); | |
| $part = new StarshipPart(); | |
| $part->setName('spoiler'); | |
| $part->setNotes('There\'s no air drag in space, but it looks cool.'); | |
| $part->setPrice(500); | |
| $manager->persist($part); | |
| $manager->flush(); | |
| // ... lines 38 - 55 | |
| } | |
| } |
Foundry suele llamar a persist() y flush() por nosotros. Pero como estamos en modo manual, tenemos que hacerlo nosotros.
Tenemos un Starship y un StarshipPart, pero aún no están relacionados. Pff, intenta cargarlos de todas formas. Dirígete a tu terminal y ejecuta:
symfony console doctrine:fixtures:load
Rutro:
starship_idno puede ser nulo en la tablastarship_part.
¿Por qué es necesaria esa columna? En StarshipPart, la propiedad starship tiene un atributo ManyToOney otro JoinColumn():
| // ... lines 1 - 10 | |
| class StarshipPart | |
| { | |
| // ... lines 13 - 28 | |
| #[ORM\ManyToOne(inversedBy: 'parts')] | |
| #[ORM\JoinColumn(nullable: false)] | |
| private ?Starship $starship = null; | |
| // ... lines 32 - 84 | |
| } |
Esto nos permite controlar la columna de clave foránea: nullable: false significa que todoStarshipPart debe pertenecer a un Starship.
Asignación de la pieza a la nave
Entonces, ¿cómo decimos que esta pieza pertenece a este Starship? La respuesta es maravillosamente sencilla. En cualquier lugar antes de flush(), decimos$part->setStarship($starship):
| // ... lines 1 - 12 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| // ... lines 17 - 36 | |
| $part->setStarship($starship); | |
| $manager->flush(); | |
| // ... lines 39 - 56 | |
| } | |
| } |
Eso es todo. Con Doctrine, no establecemos ninguna propiedad starship_id ni siquiera pasamos un ID, como $starship->getId(). No Fijamos objetos. Doctrine se encarga de los aburridos detalles de la inserción: primero guarda el Starship, luego utiliza su nuevo id para establecer la columna starship_id en la tabla starship_part.
¡Qué listo!
Prueba de nuevo las fijaciones:
symfony console doctrine:fixtures:load
¡Sin errores! Comprueba las cosas:
symfony console doctrine:query:sql 'SELECT * FROM starship_part'
¡Et voila! Ahí está nuestra pieza única, felizmente vinculada a starship_id 75. Compruébalo:
symfony console doctrine:query:sql 'SELECT * FROM starship WHERE id = 75'
Ahí está: Starship id 75 tiene un StarshipPart id 1. ¡Somos geniales!
Doctrine: trabaja con objetos, no con IDs
Estas son las conclusiones: con las relaciones Doctrine, estás en el mundo de los objetos. Olvídate de los ID. Doctrine se encarga de esa parte por ti. Tú fijas el objeto y Doctrine hace el resto.
Pero ugh, esto es mucho trabajo en AppFixtures para crear un únicoStarship y un único StarshipPart. Así que, a continuación, volvamos a utilizar Foundry para crear una flota de naves y un montón de piezas y enlazarlas todas de una sola vez. Aquí es donde Foundry brilla de verdad.
Hi, in Windows 10 and docker engine $part->setStarship($starship); not work.
Error
An exception occurred while executing a query: SQLSTATE[42703]: Undefined column: 7 ERROR: column "starship_id" of
relation "starship_part" does not exist
LINE 1: ...part (name, price, notes, created_at, updated_at, starship_i...
The first line in StarshipPart table does not exist