gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Construir accesorios es bastante sencillo, pero algo aburrido. Y sería súper aburrido crear manualmente 25 mezclas dentro del método load()
. Por eso vamos a instalar una impresionante biblioteca llamada "Foundry". Para ello, ejecuta:
composer require zenstruck/foundry --dev
Ejecuta: --dev
porque sólo necesitamos esta herramienta cuando estamos desarrollando o ejecutando pruebas. Cuando termine, ejecuta
git status
para ver que la receta ha habilitado un bundle y también ha creado un archivo de configuración... que no necesitaremos mirar.
En resumen, Foundry nos ayuda a crear objetos de entidad. Es... casi más fácil verlo en acción. En primer lugar, para cada entidad de tu proyecto (ahora mismo, sólo tenemos una), necesitarás una clase fábrica correspondiente. Créala ejecutando
php bin/console make:factory
que es un comando Maker que viene de Foundry. Luego, puedes seleccionar para qué entidad quieres crear una fábrica... o generar una fábrica para todas tus entidades. Nosotros generaremos una para VinylMix
. Y... eso creó un único archivo: VinylMixFactory.php
. Vamos a comprobarlo:src/Factory/VinylMixFactory.php
.
... lines 1 - 10 | |
/** | |
* @extends ModelFactory<VinylMix> | |
* | |
* @method static VinylMix|Proxy createOne(array $attributes = []) | |
* @method static VinylMix[]|Proxy[] createMany(int $number, array|callable $attributes = []) | |
... lines 16 - 27 | |
*/ | |
final class VinylMixFactory extends ModelFactory | |
{ | |
... lines 31 - 37 | |
protected function getDefaults(): array | |
{ | |
return [ | |
// TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories) | |
'title' => self::faker()->text(), | |
'trackCount' => self::faker()->randomNumber(), | |
'genre' => self::faker()->text(), | |
'votes' => self::faker()->randomNumber(), | |
'slug' => self::faker()->text(), | |
'createdAt' => null, // TODO add DATETIME ORM type manually | |
'updatedAt' => null, // TODO add DATETIME ORM type manually | |
]; | |
} | |
... lines 51 - 63 | |
} |
¡Qué bien! Encima de la clase, puedes ver que se describen un montón de métodos... que ayudarán a nuestro editor a saber qué superpoderes tiene esto. Esta fábrica es realmente buena para crear y guardar objetos VinylMix
... o para crear muchos de ellos, o para encontrar uno al azar, o un conjunto al azar, o un rango al azar. ¡Uf!
El único código importante que vemos dentro de esta clase es getDefaults()
, que devuelve los datos por defecto que deben utilizarse para cada propiedad cuando se crea un VinylMix
. Hablaremos más de eso en un minuto.
Pero antes... ¡vamos a avanzar a ciegas y a utilizar esta clase! En AppFixtures
, borra todo y sustitúyelo por VinylMixFactory::createOne()
.
... lines 1 - 5 | |
use App\Factory\VinylMixFactory; | |
... lines 7 - 9 | |
class AppFixtures extends Fixture | |
{ | |
public function load(ObjectManager $manager): void | |
{ | |
VinylMixFactory::createOne(); | |
$manager->flush(); | |
} | |
} |
¡Ya está! Gira y vuelve a cargar los accesorios con:
symfony console doctrine:fixtures:load
Y... ¡falla! Boo
Tipo de argumento esperado "DateTime", "null" dado en la ruta de la propiedad "createdAt"
Nos está diciendo que algo intentó llamar a setCreatedAt()
en VinylMix
... pero en lugar de pasar un objeto DateTime
, pasó null
. Hmm. Dentro deVinylMix
, si te desplazas hacia arriba y abres TimestampableEntity
, ¡sí! Tenemos un métodosetCreatedAt()
que espera un objeto DateTime
. Algo llamado así... pero que pasa null
.
Esto ayuda a mostrar cómo funciona Foundry. Cuando llamamos aVinylMixFactory::createOne()
, crea un nuevo VinylMix
y luego le pone todos estos datos. Pero recuerda que todas estas propiedades son privadas. Así que no establece la propiedad del título directamente. En su lugar, llama a setTitle()
y setTrackCount()
Aquí abajo, para createdAt
y updatedAt
, llamó a setCreatedAt()
y le pasó a null
.
En realidad, no necesitamos establecer estas dos propiedades porque las establecerá automáticamente el comportamiento timestampable.
Si probamos esto ahora...
symfony console doctrine:fixtures:load
¡Funciona! Y si vamos a ver nuestro sitio... impresionante. Esta mezcla tiene 928.000 pistas, un título aleatorio y 301 votos. Todo esto proviene del método getDefaults()
.
Para generar datos interesantes, Foundry aprovecha otra biblioteca llamada "Faker", cuyo único trabajo es... crear datos falsos. Así que si quieres un texto falso, puedes decir self::faker()->
, seguido de lo que quieras generar. Hay muchos métodos diferentes que puedes invocar en faker()
para obtener todo tipo de datos falsos divertidos. ¡Es muy útil!
Nuestra fábrica ha hecho un trabajo bastante bueno... pero vamos a personalizar las cosas para hacerlas un poco más realistas. En realidad, en primer lugar, tener un VinylMix
sigue sin ser muy útil. Así que, dentro de AppFixtures
, cambia esto por createMany(25)
.
... lines 1 - 11 | |
public function load(ObjectManager $manager): void | |
{ | |
VinylMixFactory::createMany(25); | |
... lines 15 - 16 | |
} | |
... lines 18 - 19 |
Aquí es donde Foundry brilla. Si ahora recargamos nuestras instalaciones:
symfony console doctrine:fixtures:load
Con una sola línea de código, ¡tenemos 25 accesorios aleatorios con los que trabajar! Sin embargo, los datos aleatorios podrían ser un poco mejores... así que vamos a mejorar eso.
Dentro de VinylMixFactory
, cambia el título. En lugar de text()
-que a veces puede ser un muro de texto-, cambia a words()
... y utiliza 5 palabras, y pasa true para que lo devuelva como una cadena. De lo contrario, el método words()
devuelve una matriz. Para trackCount
, sí queremos un número aleatorio, pero... probablemente un número entre 5 y 20. Para genre
, vamos a buscar un randomElement()
para elegir aleatoriamente pop
o rock
. Esos son los dos géneros con los que hemos trabajado hasta ahora. Y, vaya... asegúrate de llamar a esto como una función, ya está. Por último, para votes
, elige un número aleatorio entre -50 y 50.
... lines 1 - 28 | |
final class VinylMixFactory extends ModelFactory | |
{ | |
... lines 31 - 37 | |
protected function getDefaults(): array | |
{ | |
return [ | |
'title' => self::faker()->words(5, true), | |
'trackCount' => self::faker()->numberBetween(5, 20), | |
'genre' => self::faker()->randomElement(['pop', 'rock']), | |
'votes' => self::faker()->numberBetween(-50, 50), | |
'slug' => self::faker()->text(), | |
]; | |
} | |
... lines 48 - 60 | |
} |
¡Mucho mejor! Ah, y puedes ver que make:factory
ha añadido aquí un montón de nuestras propiedades por defecto, pero no las ha añadido todas. Una de las que falta esdescription
. Añádela: 'description' => self::faker()->
y luego usa paragraph()
. Por último, para slug
, no la necesitamos en absoluto porque se establecerá automáticamente.
... lines 1 - 37 | |
protected function getDefaults(): array | |
{ | |
return [ | |
... line 41 | |
'description' => self::faker()->paragraph(), | |
... lines 43 - 45 | |
]; | |
} | |
... lines 48 - 62 |
¡Ufff! ¡Vamos a probarlo! Recarga los accesorios:
symfony console doctrine:fixtures:load
Luego dirígete y actualiza. Esto se ve mucho mejor. Tenemos una imagen rota... pero eso es sólo porque la API que estoy utilizando tiene algunas "lagunas"... nada de lo que preocuparse.
Foundry puede hacer un montón de cosas interesantes, así que no dudes en consultar su documentación. Es especialmente útil cuando se escriben pruebas, y funciona muy bien con las relaciones de la base de datos. Así que lo volveremos a ver de forma más compleja en el próximo tutorial.
A continuación, ¡vamos a añadir la paginación! Porque, al final, no podremos listar todas las mezclas de nuestra base de datos de una sola vez.
Hey @Georg-L
That's unexpected. Did you add the Slug attribute to your property? #[Slug(fields: ['title'])]
like shown in this code block https://symfonycasts.com/screencast/symfony-doctrine/sluggable#codeblock-1fd8df0329
Cheers!
Hey @MolloKhan,
thank you for your message. Me stupid! I managed to somehow delete this line and didn't notice.
sorry for bothering you.
Have a nice day
Hey, great library. I need a bit of help with localization. If I want another language, how to do this? In the documentation I see
Faker\Factory::create('fr_FR');
But how to set this in Symfony? Thank you!
Hey Rufnex,
Good question! If you're talking about how to localize Faker with the Foundry bundle - here's an example: https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#faker . As you can see from the docs, you can do it via Symfony configuration:
# config/packages/zenstruck_foundry.yaml
when@dev: # see Bundle Configuration section about sharing this in the test environment
zenstruck_foundry:
faker:
locale: fr_FR # set the locale
I hope this helps!
Cheers!
Hi Victor,
i had already tried this. unfortunately, only the standard language is used. Any further ideas?
Greetings
Rufnex
Hey Rufnex,
Hm, could you clear the cache and try again? It might be a cache issue. If not, please, make sure your current config really has that locally, you can debug it with:
bin/console debug:config zenstruck_foundry
Is the locale set there? Also, make sure you're debugging this config for the environment that your Symfony application is currently using. It might be so that you set the locale for dev but load your app in prod.
Cheers!
HeyHo Victor,
the configuration looks correct, but still only the default language like "et accusamus sunt ipsum non". strange behavioer .. or is it required to download somewhere the language packs?
Current configuration for extension with alias "zenstruck_foundry"
==================================================================
zenstruck_foundry:
auto_refresh_proxies: true
faker:
locale: fr_FR
seed: null
service: null
instantiator:
without_constructor: false
allow_extra_attributes: false
always_force_properties: false
service: null
```
Hey Rufnex,
Ah, ok, it seems correct to me. Well, you don't need to install any extra localization pack, it's implemented via so called Provider in the faker bundle: https://github.com/FakerPHP/Faker/tree/main/src/Faker/Provider - you just need to specify a correct provider for the "locale" option I suppose. OK, could you try that Faker\Factory::create('fr_FR')
instead as described in the docs and try to generate data with it? Are those data really localized? If so, it sounds like a misconfiguration or a bug in the Foundry bundle, I'd recommend you to open an issue there.
Btw, keep in mind that not all the data might be translated. Please, try on the specific methods like name()
, region()
, vat()
, or phoneNumber()
as it's listed as translated in the docs: https://fakerphp.github.io/locales/fr_FR/ . It might be so that the method you're calling always returns Latin text.
I hope this helps!
Cheers!
Sorry, can you explain me, where to add Faker\Factory::create('fr_FR')
? i'm a bit confused ;)
I think Victor talk about something like this:
protected function getDefaults(): array
{
$faker = Factory::create('fr_FR');
return [
'title' => $faker->lastName(),
// 'title' => self::faker()->words(5, true),
'description' => self::faker()->paragraph(),
'trackCount' => self::faker()->numberBetween(5, 20),
'genre' => self::faker()->randomElement(['pop', 'rock']),
'votes' => self::faker()->numberBetween(-50, 50),
];
}
Hey Rufnex,
Hm, that's weird if even with the Faker\Factory::create('fr_FR')
you still get latin data. What method are you. calling on that custom localized faker? Could you show a part of your code where you're using it? Because as far as I understand your code it should just work this way, probably you're missing something obvious.
Cheers!
Hey Rufnex,
Yep, exactly like Ruslan mentioned below in https://symfonycasts.com/screencast/symfony-doctrine/foundry#comment-27727
I.e. you create a faker instance manually via $faker = Factory::create('fr_FR');
passing the desired locale and then use it below.
Cheers!
I followed the example .. so in my factory i have this code:
protected function getDefaults(): array
{
$faker = Factory::create('fr_FR');
return [
// TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories)
'title' => $faker->words(5, true),
'description' => $faker->text(),
'hits' => self::faker()->randomNumber(),
];
}
Hey Rufnex,
Oh, that's exactly what is in the docs actually, I'm not sure why it does not work. Well, the only explanation might be is that those methods you're using like words()
/text()
are not localized to French. I'd recommend you to change that code to something like this:
protected function getDefaults(): array
{
$faker = Factory::create('fr_FR');
// This should dump a French region and a French phone number that is started with +33
dd($faker->region(), $faker->phoneNumber());
return [
// TODO add your default values here (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories)
'title' => $faker->words(5, true),
'description' => $faker->text(),
'hits' => self::faker()->randomNumber(),
];
}
And re-load your fixtures. In the console, you should get a dumped French region and a French number that starts with +33 - it you see it - then localization actually work, but it would mean as I said before that those method you're using like text()
are not localized to French.
But if you don't see a french region and french phone number dumped - than it might be a bug in the Faker library you have installed. I'd recommend you to upgrade to the latest version and double-check if it works there. if not - probably report a bug in the Faker repository. Though, it would be a very obvious bug I suppose so other devs had to notice it already. I'm leaning towards the fact that the methods you're using are just not localizaed.
Cheers!
Hi Victor,
I think I have found the problem. The formatters text()
and word()
can't seem to be localized. To create a local text you can use realText()
$faker->realText()
$faker->realText('30')
It seems that the translation only works for the following: https://fakerphp.github.io/formatters/
If someone has the same problem, the insight might be helpful. And yes RTFM ;o)
Anyway, with the translated results, I think I'd rather stay with Latin LOL.
Thanks for your help, your service is great!
Hey Rufnex,
That's exactly what I was trying to explain you a few times :) Yeah, not everything is localized, unfortunately... or fortunately, because it might be done on purpose, that's just a strategy of that lib.
Good catch on that realText()
method, and thanks for sharing it with others!
Anyway, with the translated results, I think I'd rather stay with Latin LOL
Lol! But yeah, that's the reason why the "lorem ipsum" is so popular, it has no "semantic load", just text :)
Anyway, I'm really happy we figured this mystery out! ;)
Cheers!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"babdev/pagerfanta-bundle": "^3.7", // v3.7.0
"doctrine/doctrine-bundle": "^2.7", // 2.7.0
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.12", // 2.12.3
"knplabs/knp-time-bundle": "^1.18", // v1.19.0
"pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
"pagerfanta/twig": "^3.6", // v3.6.1
"sensio/framework-extra-bundle": "^6.2", // v6.2.6
"stof/doctrine-extensions-bundle": "^1.7", // v1.7.0
"symfony/asset": "6.1.*", // v6.1.0
"symfony/console": "6.1.*", // v6.1.2
"symfony/dotenv": "6.1.*", // v6.1.0
"symfony/flex": "^2", // v2.2.2
"symfony/framework-bundle": "6.1.*", // v6.1.2
"symfony/http-client": "6.1.*", // v6.1.2
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/proxy-manager-bridge": "6.1.*", // v6.1.0
"symfony/runtime": "6.1.*", // v6.1.1
"symfony/twig-bundle": "6.1.*", // v6.1.1
"symfony/ux-turbo": "^2.0", // v2.3.0
"symfony/webpack-encore-bundle": "^1.13", // v1.15.1
"symfony/yaml": "6.1.*", // v6.1.2
"twig/extra-bundle": "^2.12|^3.0", // v3.4.0
"twig/twig": "^2.12|^3.0" // v3.4.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"symfony/debug-bundle": "6.1.*", // v6.1.0
"symfony/maker-bundle": "^1.41", // v1.44.0
"symfony/stopwatch": "6.1.*", // v6.1.0
"symfony/web-profiler-bundle": "6.1.*", // v6.1.2
"zenstruck/foundry": "^1.21" // v1.21.0
}
}
Hey team,
I just followed the tutorial and got an Error. To blame me: I am using symfony 6.2.8.
But did I miss something? The new Method from MixController works fine and slug is created automatically, but deleting 'slug' => self::faker()->text() and executing "symfony console doctrine:fixtures:load" leads to
In ExceptionConverter.php line 47:
An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column "slug" of relation "vinyl_mix" violates not-null constraint
DETAIL: Failing row contains (9, id veniam ducimus exercitationem est, Totam dolor et tenetur fuga voluptas nisi rerum. Est animi corpo..., 6, rock, 2023-04-17 17:21:34, 20, 2023-04-17 17:21:34, null).
Any comment is appreciated.
Thank you.