Tecnología alienígena para accesorios: Fundición y Falsificació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 SubscribeEstamos utilizando src/DataFixtures/AppFixtures.php para crear datos de fixture falsos. Esto funciona bien. Pero, ¿dónde está lo guay y divertido? ¿Realmente queremos escribir código manual para añadir docenas o más entidades? Puntos para ti si has respondido: ¡diablos, no!
Para que esto pase de tedioso a terrorífico, busca tu terminal y ejecuta:
Instalar Foundry y Faker
composer require --dev foundry
Desplázate hacia arriba para ver lo que se ha instalado. Los paquetes importantes son zenstruck/foundry -como forma de crear muchas entidades rápidamente- y fakerphp/faker -una biblioteca para crear datos falsos de forma que no tengamos que depender de lorem ipsum y de nuestra propia falta de creatividad-.
Ejecuta
git status
para ver lo que hicieron las recetas: habilitó un bundle y añadió un archivo de configuración. Esa configuración funciona bien desde el principio, así que no hace falta mirarla.
Crear una fábrica de naves estelares
Con Foundry, cada entidad puede tener una clase fábrica. Para ponerlas en marcha, ejecuta:
symfony console make:factory
Esto lista todas las entidades que aún no tienen una fábrica. Elige Starship y... ¡éxito! Se ha creado una nueva clase StarshipFactory. Ve a verla:src/Factory/StarshipFactory.php.
Esta clase será muy buena para crear objetos Starship, muy útil en caso de que vuelvan los Borg. Primero, mira este método class(). Indica a Foundry con qué clase de entidad ayuda esta fábrica. En defaults() es donde definimos los valores por defecto que utilizaremos al crear naves estelares. Te recomiendo que añadas valores por defecto para todos los campos obligatorios: te hará la vida más fácil.
¡Echa un vistazo a estas llamadas a self::faker()! Así es como generamos datos aleatorios. Paraname, captain y class, es texto aleatorio, status, es unStarshipStatusEnum aleatorio y arrivedAt por defecto es cualquier fecha aleatoria Dado que aún no se ha inventado el viaje en el tiempo, sustituye self::faker()->dateTime() por self::faker()->dateTimeBetween('-1 year', 'now'):
| // ... lines 1 - 11 | |
| final class StarshipFactory extends PersistentProxyObjectFactory | |
| { | |
| // ... lines 14 - 111 | |
| protected function defaults(): array|callable | |
| { | |
| return [ | |
| 'arrivedAt' => \DateTimeImmutable::createFromMutable(self::faker()->dateTimeBetween('-1 year', 'now')), | |
| // ... lines 116 - 119 | |
| ]; | |
| } | |
| // ... lines 122 - 131 | |
| } |
El método text() de Faker nos dará un texto aleatorio, pero no necesariamente interesante. En lugar de servir bajo el Capitán "desayuno de tarta de manzana", en el directorio tutorial/, copia estas constantes y pégalas en la parte superior de la clase fábrica:
| // ... lines 1 - 11 | |
| final class StarshipFactory extends PersistentProxyObjectFactory | |
| { | |
| private const SHIP_NAMES = [ | |
| 'Nebula Drifter', | |
| 'Quantum Voyager', | |
| 'Starlight Nomad', | |
| // ... lines 18 - 44 | |
| ]; | |
| // ... line 46 | |
| private const CLASSES = [ | |
| 'Eclipse', | |
| 'Vanguard', | |
| 'Specter', | |
| // ... lines 51 - 57 | |
| ]; | |
| // ... line 59 | |
| private const CAPTAINS = [ | |
| 'Orion Stark', | |
| 'Lyra Voss', | |
| 'Cassian Drake', | |
| // ... lines 64 - 90 | |
| ]; | |
| // ... lines 92 - 131 | |
| } |
Entonces, para captain utiliza randomElement(self::CAPTAINS). Paraclass, randomElement(self::CLASSES) y para name, randomElement(self::SHIP_NAMES):
| // ... lines 1 - 11 | |
| final class StarshipFactory extends PersistentProxyObjectFactory | |
| { | |
| // ... lines 14 - 111 | |
| protected function defaults(): array|callable | |
| { | |
| return [ | |
| // ... line 115 | |
| 'captain' => self::faker()->randomElement(self::CAPTAINS), | |
| 'class' => self::faker()->randomElement(self::CLASSES), | |
| 'name' => self::faker()->randomElement(self::SHIP_NAMES), | |
| // ... line 119 | |
| ]; | |
| } | |
| // ... lines 122 - 131 | |
| } |
Utilizar la fábrica de naves estelares
¡Es hora de utilizar esta fábrica! En src/DataFixtures/AppFixtures.php, en load(), escribe StarshipFactory::createOne(). Pásale una matriz de valores de propiedades para la primera nave: copia estos del código existente: name, class, captain, statusy arrivedAt:
| // ... lines 1 - 9 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| StarshipFactory::createOne([ | |
| 'name' => 'USS LeafyCruiser (NCC-0001)', | |
| 'class' => 'Garden', | |
| 'captain' => 'Jean-Luc Pickles', | |
| 'status' => StarshipStatusEnum::IN_PROGRESS, | |
| 'arrivedAt' => new \DateTimeImmutable('-1 day'), | |
| ]); | |
| // ... lines 21 - 36 | |
| } | |
| } |
Y elimina el código antiguo:
| // ... lines 1 - 9 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| // ... lines 14 - 21 | |
| StarshipFactory::createOne([ | |
| 'name' => 'USS Espresso (NCC-1234-C)', | |
| 'class' => 'Latte', | |
| 'captain' => 'James T. Quick!', | |
| 'status' => StarshipStatusEnum::COMPLETED, | |
| 'arrivedAt' => new \DateTimeImmutable('-1 week'), | |
| ]); | |
| StarshipFactory::createOne([ | |
| 'name' => 'USS Wanderlust (NCC-2024-W)', | |
| 'class' => 'Delta Tourist', | |
| 'captain' => 'Kathryn Journeyway', | |
| 'status' => StarshipStatusEnum::WAITING, | |
| 'arrivedAt' => new \DateTimeImmutable('-1 month'), | |
| ]); | |
| } | |
| } |
¡Bonus! Elimina las llamadas a persist() y flush(): ¡Foundry se encarga de eso por nosotros!
¡Veamos qué hace esto! Recarga los accesorios:
symfony console doctrine:fixtures:load
Elige yes y... ¡éxito! Vuelve atrás, actualiza y... parece lo mismo. ¡Buena señal! Ahora, ¡vamos a crear una flota de naves!
Crear muchas naves estelares
Para las tres primeras, pasamos una matriz de valores... pero no necesitamos hacerlo. Si no pasamos un valor, utilizará el método StarshipFactory::defaults(). Fíjate en lo peligroso que nos hace esto: ¿acaba de aparecer un cubo Borg? Prepara 20 naves nuevas con StarshipFactory::createMany(20):
| // ... lines 1 - 9 | |
| class AppFixtures extends Fixture | |
| { | |
| public function load(ObjectManager $manager): void | |
| { | |
| // ... lines 14 - 37 | |
| StarshipFactory::createMany(20); | |
| } | |
| } |
De vuelta en el terminal, carga de nuevo los accesorios:
symfony console doctrine:fixtures:load
Y en la aplicación, actualiza y... ¡fíjate! Ahora hay toda una flota de naves, y sí, ¡todas tienen datos aleatorios!
Ahora que los datos falsos parecen más reales, me pregunto: ¿y si nuestra aplicación se ejecutara en una enorme base estelar con cientos o miles de naves? Esto sería una página muy larga. A continuación, paginaremos estos resultados en trozos más pequeños.
11 Comments
Hi,
Let's say we have two tables such that User and Lablist. And we created two factory for these tables.
How will we send data to these tables by using AppFixtures without affection both of them? If I add LablistFactory under UserFactory in below code, and if we load, all tables will be refreshed?
Hey @Mahmut-A,
Factory relations is what you're looking for. There are a couple ways to achieve but using your code above, it could be something like this:
I hope this helps!
--Kevin
Hi Kevin,
Maybe I explained wrong. There are two tables. one is user, second is lablist. I want to load their data to tables. As far as I understand, I can use only AppFixtures to load data. However, if I use UserFactory and LablistFactory in AppFixtures, then if I load by using fixtures:load command, both tables will be refreshed at each run.
I want to only load lablist, since user is alread loaded , I do not want to refresh it.
I want to add something. I created two Fixtures. AppFixtures and LablistFixtures by using command "symfony console make:fixtures LablistFixtures."
then, I am applying "php bin/console doctrine:fixtures:load --group=LablistFixtures" command to load data to lablist table. however, when I apply this, user table is being deleted.
if I use "php bin/console doctrine:fixtures:load --group=LablistFixtures --group=AppFixtures, all tables are refreshed and IDs are regenerated.
I want to only load to spesific tables not all of them.
Ok, I understand. You can have multiple fixture files: perhaps in your case, a
UserFixturesandLablistFixtures. By default, both will by loaded, but you can group them so they can be loadedindependently. Check the "Splitting Fixtures into Separate Files" documentation to see your options.
Hi,
Actually,splitting also refreshing. But I fixed my problem as below:
php bin/console doctrine:fixtures:load --group=LablistFixtures --appendwith this command, User tables is not refreshing.
However, we should be careful to not duplicate data in Lablist since we used --append. To prevent this, I added below lines in LabList.php class.
so now everything is perfect. thank you
`use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[UniqueEntity('labname')]
`
Glad to hear you have it working!
I think adjusting the purging behaviour could be used as an alternative to
--append.Hello,
I want to ask that how can I add new mail address to existing user table. As you see, there are mahmut.aydin mail adress and random 10 mail address. I want to add new one withouth deleting old data.
thank you
Hey Mahmut,
Then you did it right :) Did you try to reload your fixtures? It will create a mahmut.aydin@netsys.com.tr user and 10 more random users.
Or are you trying to add only that mahmut.aydin@netsys.com.tr without resetting your DB? Well, if so, that's a bit trickier. First of all, in theory, you should not care too much about the data locally, so you can just reload your fixtures that will do the trick eventually, but yeah, it will reset the DB so for example all your additional data you add to the DB after loaded the fixtures will gone. Alternative solution in your specific case - just register a new user with that email on the registration page if you have it 🤷♂️ That's the easiest way when you need to add just one more specific user. And finally, if you still need to do it with fixtures - run the same
bin/console doctrine:fixtures:loadcommand but with--appendoption:It will try to append new data w/o clearing the DB before. But it may cause some duplicated constraints fails, depends on how good your randomness is :) I would recommend you to comment out everything you don't need before running that command, e.g. comment out that
// UserFactory::createMany(10);.I hope that helps :)
Cheers!
thank you Victor. Actually, randomness is not important. I only wanted to learn how to add new data to existing table. I see need to use --append parameter to command.
thank you
Hey Mahmut,
Great! About randomness - it depends on your unique constraints in the DB. When you append data but if somehow during it the same email that already exists in your DB will be generated - the whole data load command will fail, that's just something you should keep in mind.
Cheers!
"Houston: no signs of life"
Start the conversation!