Somehow I always have problems installing and configuring Alice bundle. Symfony and Alice are beeing developed and new versions are pushed regularly – there is lots of outdated and confusing information. Even official documentation is not always correct and up to date. So since I was unable to make the two work out of the box, and since I didn’t find all the info in the one place – I decided to write this tutorial.
In this tutorial you will learn how to install, configure and create fixtures, write custom fixture Buffy quote generator (formatter). 😀
Please note, this is not detailed tutorial about Alice, we will just make it work. Also, don’t forget, PHP 7.1+ is required by Symfony 4.
Code from this article was tested on Fedora 27, Symfony 4.0.7, Alice bundle 1.4, nelmio/alice 3.2.2 and PHP 7.1.15.
Code
Complete code is available from this article or from github: github.com/dribtech/symfony4-alice3-tutorial .
Installing
Follow this steps:
1 2 3 4 5 6 7 8 9 10 11 |
$ composer create-project symfony/skeleton sf4alice $ cd sf4alice/ $ composer require annotations $ composer require doctrine/orm $ composer require doctrine/doctrine-bundle $ composer require --dev doctrine/doctrine-fixtures-bundle |
Fix #1
Please note: recently I noticed that this bug is now fixed. But the rest of tutorial still apply.
And finally install Alice bundle:
$ composer require --dev hautelook/alice-bundle
And here it is, the first problem: Cannot register “Hautelook\AliceBundle\HautelookAliceBundle” without “Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle”.
To fix it, add this lines to the config/bundle.php
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => [‘dev’ => true, ‘test’ => true],
Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => [‘dev’ => true, ‘test’ => true],
Now config/bundles.php should look like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], Hautelook\AliceBundle\HautelookAliceBundle::class => ['dev' => true, 'test' => true], Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true], Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true], ]; |
Credit: github.com/hautelook/AliceBundle/issues/380.
Fix #2
Please note: recently I noticed that this bug is now fixed. But the rest of tutorial still apply.
To see did we fix the problem run:
$ bin/console c:c
Another error: Unrecognized option “db_drivers” under “hautelook_alice”
Change config/packages/dev/hautelook_alice.yaml to:
1 2 3 4 5 |
hautelook_alice: fixtures_path: 'Resources/fixtures' # Path to which to look for fixtures relative to the project directory or the bundle path. root_dirs: - '%kernel.root_dir%' - '%kernel.project_dir%' |
For now, just ignore fixtures_path since it is not used in this tutorial.
Run
$ bin/console c:c
Finally no errors! 😀
Lets load some fixtures!
Configure database
Edit .env to set DATABASE_URL
1 2 3 4 5 6 |
###> doctrine/doctrine-bundle ### # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" # Configure your db driver and server_version in config/packages/doctrine.yaml DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name ###< doctrine/doctrine-bundle ### |
Just fill in all the required data according to the DATABASE_URL template from the existing .env file.
Then run
$ bin/console doctrine:database:create
You should get message like this: “Created database db_name
for connection named default”
Create Entity
In src/Entity create Buffy.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="buffy") */ class Buffy { /** * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") * @ORM\Column(type="integer") */ private $id; /** * @ORM\Column(type="string") */ private $name; /** * @ORM\Column(type="text") */ private $quote; /** * @return mixed */ public function getName() { return $this->name; } /** * @param mixed $name */ public function setName($name): void { $this->name = $name; } /** * @return mixed */ public function getQuote() { return $this->quote; } /** * @param mixed $quote */ public function setQuote($quote): void { $this->quote = $quote; } /** * @return mixed */ public function getId() { return $this->id; } } |
Run
$ bin/console doctrine:schema:update --force
To create buffy table. Output:
Updating database schema…
1 query was executed
[OK] Database schema updated successfully!
Create and load fixture #1
In src/DataFixtures/ORM create fixtures.yaml :
1 2 3 4 |
App\Entity\Buffy: buffy_{1..20}: name: <name()> quote: <text()> |
In src/DataFixtures/ORM create LoadFixtures.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace App\DataFixtures\ORM; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\Persistence\ObjectManager; use Nelmio\Alice\Loader\NativeLoader; class LoadFixtures extends Fixture { public function load(ObjectManager $manager) { $loader = new NativeLoader(); $objectSet = $loader->loadFile(__DIR__ . '/fixtures.yaml')->getObjects(); foreach($objectSet as $object) { $manager->persist($object); } $manager->flush(); } } |
Run
$ bin/console doctrine:fixtures:load
Output:
Careful, database will be purged. Do you want to continue y/N ?y
> purging database
> loading App\DataFixtures\ORM\LoadFixtures
Lets check it:
$ bin/console doctrine:query:dql "select b from App\Entity\Buffy b"
And here we go, 20 random names and quote lines.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
array (size=20) 0 => object(stdClass)[605] public '__CLASS__' => string 'App\Entity\Buffy' (length=16) public 'id:App\Entity\Buffy:private' => int 1 public 'name:App\Entity\Buffy:private' => string 'Adah Reichel' (length=12) public 'quote:App\Entity\Buffy:private' => string 'Occaecati debitis et saepe eum sint dolorem. Enim ipsum inventore sed libero et velit qui suscipit. Deserunt laudantium quibusdam enim nostrum soluta qui ipsam non.' (length=164) 1 => object(stdClass)[607] public '__CLASS__' => string 'App\Entity\Buffy' (length=16) public 'id:App\Entity\Buffy:private' => int 2 public 'name:App\Entity\Buffy:private' => string 'Agustina Boehm' (length=14) public 'quote:App\Entity\Buffy:private' => string 'Non voluptates ut optio quos qui illo error nihil. Vero a officia id corporis incidunt saepe. Esse hic eligendi quos culpa ut ab.' (length=129) |
Create custom generator (formatter)
Lets create custom formatter which will output some real people quotes!
In src/DataFixtures/ORM create BuffyProvider.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace App\DataFixtures\ORM; use Faker\Provider\Base as BaseProvider; use Faker\Generator; class BuffyProvider extends BaseProvider { public function __construct(Generator $generator) { parent::__construct($generator); } public function buffyQuote() { $key = array_rand($this->quotes); return $this->quotes[$key]; } // src: https://thoughtcatalog.com/jayson-flores/2014/06/the-25-best-buffy-the-vampire-slayer-quotes/ private $quotes = [ "The hardest thing in this world is to live in it. Be brave. Live.", "Out. For. A. Walk. Bitch.", "I owe you pain.", "[On life] Yes, it’s terribly simple. The good guys are always stalwart and true, the bad guys are easily distinguished by their pointy horns or black hats, and, uh, we always defeat them and save the day. No one ever dies, and everybody lives happily ever after.", "I may be dead, but I’m still pretty.", "Passion...it lies in all of us. Sleeping, waiting, and though unwanted, unbidden, it will stir, open its jaws, and howl. It speaks to us, guides us... passion rules us all. And we obey. What other choice do we have? Passion is the source of our finest moments; the joy of love, the clarity of hatred, and the ecstasy of grief. It hurts sometimes more than we can bear. If we could live without passion, maybe we’d truly know some kind of peace. But we would be hollow. Empty rooms, shuttered and dank... without passion, we’d truly be dead.", "No, she was never gone. She was with me. We should have been forever, and I – I let her be dead. She’s really dead. And I killed her.", "So, either you hit her, or you did your wacky mime routine for her.", "You’re not friends. You’ll never be friends. Love isn’t brains, children, it’s blood. Blood screaming inside you to work its will. I may be love’s bitch, but at least I’m man enough to admit it.", "Seize the moment, ‘cause tomorrow you might be dead.", "I’ve seen honest faces before. They’re usually attached to liars.", "Strong is fighting. It’s hard and it’s painful and it’s every day. It’s what we have to do, and we can do it together, but if you’re too much of a coward for that, then burn.", "You still my girl?", "Don’t be ridiculous. Martha Stewart isn’t a demon. She’s a witch. Nobody could do that much decoupage without calling on the powers of darkness.", "[To Xander] It must be really hard when all your friends have, like superpowers; Slayer, werewolf, witch, vampires; and you’re, like, this little nothing.", "Funny. ‘Cause I look around at this world you’re so eager to be a part of, and all I see is six billion lunatics looking for the fastest ride out. Who’s not crazy? Look around. Everyone’s drinking, smoking, shooting up, shooting each other, or just plain screwing their brains out ‘cause they don’t want ‘em anymore. I’m crazy? Honey, I’m the original one-eyed chicklet in the kingdom of the blind. ‘Cause at least I admit the world makes me nuts.", "If the apocalypse comes, beep me.", "[On humans] I guess I just realized how amazingly screwed up they all are. I mean, really, really screwed up, in a monumental fashion. And they have no purpose that unites them so they just drift around, blundering through life until they die... which they... they know is coming, yet every single one of them is surprised when it happens to them. They’re incapable of thinking about what they want beyond the moment. They kill each other, which is clearly insane. And yet, here’s the thing – when it’s something that really matters, they fight. I mean, they’re lame morons for fighting, but they do. They never... never quit. So I guess I will keep fighting too.", "I want to take comfort in you, and I know it will cost me my soul, and a part of me doesn’t care.", "When are you going to this, B? Life for a Slayer is very simple — want...take...have.", "The Earth is doomed.", "When I say ‘I love you,’ it’s not because I want you or because I can’t have you. It has nothing to do with me. I love what you are, what you do, how you try. I’ve seen your kindness and your strength. I’ve seen the best and worst of you. And I understand with perfect clarity exactly what you are. You’re a hell of a woman. You’re the one, Buffy.", "[To Angel] My diary? You read my diary? That is not okay! A diary is like a person’s most private place! You don’t even know what I was writing about! ‘Hunk’ can mean a lot of things, bad things. And, when it says that your eyes are ‘penetrating’, I meant to write ‘bulging’. And ‘A’ doesn’t even stand for Angel for that matter, it stands for Achmed, a charming foreign exchange student, so that whole fantasy part has nothing to even do with you at all...", "[To Angel] We’re not friends. We never were. And I can fool Giles, and I can fool my friends, but I can’t fool myself. Or Spike, for some reason. What I want from you I can never have.", "I’m the thing that monsters have nightmares about. And right now, you and me are gonna show ‘em why.", ]; } |
Register the Buffy provider
According to the instructions, no extra action is necessary. But it didn’t work for me. So here is the another way (also described in the documentation):
In src/DataFixtures/ORM create AppNativeLoader.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App\DataFixtures\ORM; use Nelmio\Alice\Loader\NativeLoader; use Faker\Generator as FakerGenerator; class AppNativeLoader extends NativeLoader { protected function createFakerGenerator(): FakerGenerator { $generator = parent::createFakerGenerator(); $generator->addProvider(new BuffyProvider($generator)); return $generator; } } |
Change src/DataFixtures/ORM/LoadFixtures.php to use AppNativeLoader instead of NativeLoader. Now, LoadFixtures looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php namespace App\DataFixtures\ORM; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\Persistence\ObjectManager; class LoadFixtures extends Fixture { public function load(ObjectManager $manager) { $loader = new AppNativeLoader(); $objectSet = $loader->loadFile(__DIR__ . '/fixtures.yaml')->getObjects(); foreach($objectSet as $object) { $manager->persist($object); } $manager->flush(); } } |
And the last step, edit src/DataFixtures/ORM/fixtures.yaml to use our buffyQuote() instead of text():
1 2 3 4 |
App\Entity\Buffy: buffy_{1..20}: name: <name()> quote: <buffyQuote()> |
Run
$ bin/console doctrine:fixtures:load
And to check it out:
$ bin/console doctrine:query:dql "select b from App\Entity\Buffy b"
1 2 3 4 5 6 7 8 9 10 11 12 13 |
array (size=20) 0 => object(stdClass)[605] public '__CLASS__' => string 'App\Entity\Buffy' (length=16) public 'id:App\Entity\Buffy:private' => int 21 public 'name:App\Entity\Buffy:private' => string 'Adah Reichel' (length=12) public 'quote:App\Entity\Buffy:private' => string 'I want to take comfort in you, and I know it will cost me my soul, and a part of me doesn’t care.' (length=99) 1 => object(stdClass)[607] public '__CLASS__' => string 'App\Entity\Buffy' (length=16) public 'id:App\Entity\Buffy:private' => int 22 public 'name:App\Entity\Buffy:private' => string 'Prof. Maude Zulauf PhD' (length=22) public 'quote:App\Entity\Buffy:private' => string 'I’m the thing that monsters have nightmares about. And right now, you and me are gonna show ‘em why.' (length=104) |
Thanks for this, fixes #1 and #2 we’re wahat I needed 🙂
Great! 🙂 Thx for the feedback!
Thanks 🙂 This was useful.
Great, thank you! 🙂
Thanks!!
thx for the tutorial
i just discovered an other method for those who want to add custom providers. Here is the link:
https://github.com/hautelook/AliceBundle/blob/master/doc/faker-providers.md
very straightforward and it works !
Hi Jérémy, I was aware of this method – but I couldn’t make it work for me! 🙁