Establecer relaciones en la Fundición
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 SubscribeVale, tenemos un par de piezas y unas cuantas naves estelares, pero para llenar nuestra flota de datos de pruebas: Quiero mucho más. Éste es un trabajo perfectamente adecuado para nuestro buen amigo Foundry. Elimina el código manual y, en cualquier lugar, di:StarshipPartFactory::createMany(100)
:
// ... lines 1 - 8 | |
use App\Factory\StarshipPartFactory; | |
// ... lines 10 - 12 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 17 - 41 | |
StarshipPartFactory::createMany(100); | |
} | |
} |
Y prueba las instalaciones:
symfony console doctrine:fixtures:load
¡Uh-oh!
starship_id
no puede ser nulo enstarship_part
.
Esto se remonta hasta StarshipPartFactory
, en el método defaults()
. Estos son los datos que se pasan a cada nuevo StarshipPart
cuando se crea. La regla de oro es hacer que defaults()
devuelva una clave para cada propiedad necesaria del objeto. Ahora mismo, obviamente nos falta la propiedad starship
, así que vamos a añadirla. Establece starship
, no starship_id
, en un método ingenioso llamado Starship::randomOrCreate()
y pásale un array:
// ... lines 1 - 11 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
// ... lines 14 - 45 | |
protected function defaults(): array|callable | |
{ | |
// ... lines 48 - 50 | |
return [ | |
// ... lines 52 - 54 | |
'starship' => StarshipFactory::randomOrCreate([ | |
// ... line 56 | |
]), | |
]; | |
} | |
// ... lines 60 - 69 | |
} |
Preparando el escenario para las piezas de la nave estelar
En la página de inicio, sólo vamos a listar las naves estelares con estado "en curso" o "en espera". Para asegurarnos de que estas piezas están relacionadas con una nave con estado "en progreso", añade una clave status
en el array configurado como StarshipStatusEnum::IN_PROGRESS
:
// ... lines 1 - 5 | |
use App\Entity\StarshipStatusEnum; | |
// ... lines 7 - 11 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
// ... lines 14 - 45 | |
protected function defaults(): array|callable | |
{ | |
// ... lines 48 - 50 | |
return [ | |
// ... lines 52 - 54 | |
'starship' => StarshipFactory::randomOrCreate([ | |
'status' => StarshipStatusEnum::IN_PROGRESS, | |
]), | |
]; | |
} | |
// ... lines 60 - 69 | |
} |
Este randomOrCreate()
es un método impresionante: primero busca en la base de datos un Starship
que coincida con estos criterios (una nave "en progreso"). Si encuentra una, la utiliza. Si no, crea una con ese estado.
Prueba ahora los accesorios.
symfony console doctrine:fixtures:load
¡No hay errores! Comprueba la base de datos:
symfony console doctrine:query:sql "SELECT * FROM starship_part"
Fíjate bien... Vale! tenemos 100 piezas vinculadas cada una a un Starship
aleatorio, que debería ser un Starship
con estado "en curso". ¡Creo que han sido mis 5 minutos más productivos!
Tomar el control en Foundry
Pero, ¿y si necesitamos más control? ¿Y si queremos asignar las 100 piezas a la misma nave? Sé que suena un poco inútil, pero nos ayudará a entender mejor Foundry y las relaciones.
Empieza por obtener una variable de nave: $ship = StarshipFactory::createOne()
:
// ... lines 1 - 12 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 17 - 32 | |
$ship = StarshipFactory::createOne([ | |
// ... lines 34 - 38 | |
]); | |
// ... lines 40 - 44 | |
} | |
} |
Luego, en StarshipPartFactory::createMany()
, pasa un segundo argumento para especificar que queremos que starship
se establezca en esta nave concreta:
// ... lines 1 - 12 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 17 - 32 | |
$ship = StarshipFactory::createOne([ | |
// ... lines 34 - 38 | |
]); | |
// ... lines 40 - 41 | |
StarshipPartFactory::createMany(100, [ | |
'starship' => $ship, | |
]); | |
} | |
} |
Vuelve a cargar los accesorios.
symfony console doctrine:fixtures:load
Y ¡listo! Ahora todas las piezas están relacionadas con la misma nave. Y si consultamos el Starship
, tenemos 23: las 20 de abajo, más las 3 extra que hemos añadido. ¡Todo está encajando!
El giro argumental de la Fundición
Aquí es donde las cosas se ponen interesantes. En StarshipPartFactory
, en lugar de utilizar randomOrCreate()
, cambia a createOne()
:
// ... lines 1 - 11 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
// ... lines 14 - 45 | |
protected function defaults(): array|callable | |
{ | |
// ... lines 48 - 50 | |
return [ | |
// ... lines 52 - 54 | |
'starship' => StarshipFactory::createOne([ | |
// ... line 56 | |
]), | |
]; | |
} | |
// ... lines 60 - 69 | |
} |
Carga de nuevo las instalaciones.
symfony console doctrine:fixtures:load
Y... consulta todas las naves.
symfony console doctrine:query:sql "SELECT * FROM starship"
Vaya, ¡de repente tenemos una flota! 123 naves para ser exactos. ¿Qué ha ocurrido?
Para cada pieza, se llama a defaults()
. Así que para las 100 piezas, se está activando esta línea, que crea y guarda un Starship
, aunque nunca utilicemos ese Starship
porque lo anulamos momentos después.
¿La solución? Cambiar esto por StarshipFactory::new()
:
// ... lines 1 - 11 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
// ... lines 14 - 45 | |
protected function defaults(): array|callable | |
{ | |
// ... lines 48 - 50 | |
return [ | |
// ... lines 52 - 54 | |
'starship' => StarshipFactory::new([ | |
// ... line 56 | |
]), | |
]; | |
} | |
// ... lines 60 - 69 | |
} |
Esta es la salsa secreta: crea una nueva instancia de la fábrica, no un objeto en la base de datos. Pruébalo:
symfony console doctrine:fixtures:load
Consulta las naves.
symfony console doctrine:query:sql "SELECT * FROM starship"
¡Perfecto! Volvemos a tener 23.
Las fábricas son recetas de objetos
¡Dato curioso! Podemos utilizar estas instancias de fábrica como recetas para crear objetos.StarshipFactory::new(['status' => StarshipStatusEnum::STATUS_IN_PROGRESS])
no crea un objeto en la base de datos. No: new()
significa una nueva instancia de la fábrica. Y cuando pasas una fábrica para una propiedad, Foundry retrasa la creación de ese objeto hasta que se necesite, si es que se necesita. Por tanto, sólo si no se anula Starship
, creará un nuevo Starship
con el estado "en curso" y lo guardará. En realidad, ésta es la mejor práctica a la hora de establecer relaciones en Foundry: establecerlas en una instancia de fábrica.
Limpia nuestras instalaciones eliminando la anulación:
// ... lines 1 - 12 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
// ... lines 17 - 41 | |
StarshipPartFactory::createMany(100); | |
} | |
} |
Y... vuelve a randomOrCreate()
// ... lines 1 - 11 | |
final class StarshipPartFactory extends PersistentProxyObjectFactory | |
{ | |
// ... lines 14 - 45 | |
protected function defaults(): array|callable | |
{ | |
// ... lines 48 - 54 | |
'starship' => StarshipFactory::randomOrCreate([ | |
// ... line 56 | |
]), | |
]; | |
} | |
// ... lines 60 - 69 | |
} |
Porque, seamos sinceros, es un método bastante útil.
Recarga las instalaciones una última vez para asegurarte de que no hemos roto nada
symfony console doctrine:fixtures:load
No Nos esforzaremos más la próxima vez.
there's no code snippet in this tutorial visible, but you're saying:
"Set starship, not starship_id, to a nifty method called Starship::randomOrCreate() and pass an array."
then
symfony console doctrine:fixtures:load
works OK