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:
Note
Desde DoctrineBundle 3.0, el comando pasó a llamarse symfony console dbal:run-sql
symfony console doctrine:query:sql 'SELECT * FROM starship_part'
Y ¡voilá! Ahí está nuestra pieza única, felizmente vinculada a starship_id 75. Búscalo:
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.
4 Comments
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
Hey @giorgiocba
Sorry for my late response. I believe you didn't update your database after adding the
starshiprelationship. Could you take a look?Cheers!
When you do :
I cannt see part_id is 1.
Even when I try to do the query myself, I dont see part_id anywhere... Did I miss something?
Hey Bart,
You may have a different ID locally, depending on how many times you reloaded your fixtures. First of all, make sure you reloaded the fixtures. Then, you can find the correct ID with this command:
And finally use the ID from the output in that last command:
If you still don't see it - make sure you have the exact same code in
AppFixtures.phpas on the video, probably you missed theflush()call there?. You can expand the full code block here: https://symfonycasts.com/screencast/doctrine-relations/setting-relation#codeblock-e528363598Cheers!
"Houston: no signs of life"
Start the conversation!