Multiple files for Doctrine Fixtures for Symfony 3 - symfony

I'm using the bundle Doctrine:Fixtures to load an example of bbdd throw the Entitys and since I work alone in the project it's ok.
But now, I got a colleague in my project and we were wondering if it's possible to set up a different files for the loading.
I got my own file of fixtures associated in git and I don't want him modifying this file. I would like to have an special file just for him that will allows him to modify whenever he wants this file of fixtures. So, anyone can have his owns records in the init of the bbdd.
If it's not possible with multiple files, could be possible in another way?
http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html

You can load specific fixture files using the --fixture flag:
php app/console doctrine:fixtures:load --fixture=/src/BundleName/DataFixtures/ORM/Fixture.php
Or, you could add a Fixtures.php.dist file with some working examples, then use .gitignore to ignore Fixtures.php.
Then add a command into your build (or in composer scripts), and/or your documentation to copy this .dist file to Fixtures.php when checking out the project.
Another method if you're doing BDD is to create a Helper class that can be used in your Context to create and persist entities as you need them in tests. This would allow you to create only specifics needed for the test. All it really needs is the EntityManager so it may be simpler than pre-defining all the fixtures up front.
You can use Faker to generate realistic entities.
class AbstractFixtureHelper implements ContainerAwareInterface
{
/**
* #var Generator
*/
protected $faker;
/**
* #var ContainerInterface
*/
protected $container;
public function __construct()
{
$this->faker = Factory::create();
}
/**
* #param ContainerInterface|null $container
* #return void
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* #return EntityManager
*/
protected function getEntityManager()
{
return $this->container->get('doctrine.orm.entity_manager');
}
}
Then for different entities - in this example, a user:
class UserFixtureHelper extends AbstractFixtureHelper
{
public function createUser()
{
$user = new User();
$user->setEmail($this->faker->email);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
return $user;
}
}
Then in your Context, inject the UserFixtureHelper and create directly in the scenario steps.
/**
* #Given there is a User who XXX
*/
public function thereIsAUser()
{
$user = $this->userFixtureHelper->createUser();
}

Related

Inject service based on dynamic value in Symfony

I have 2 services, BlueWorkerService and YellowWorkerService, both implementing the same interface, WorkerServiceInterface. Each of these services use the same entities but with different required logic.
I need to inject one of, but not both, of these classes and use them in ProcessorService so that the interface methods are called using on correct Worker. Which worker service to use is dependent on which Worker is currently being processed. I'll break it down:
Class WorkerProcessor {
private $workerService;
public function __construct(WorkerServiceInterface $workerServiceInterface)
{
$this->workerService = $workerServiceInterface;
}
public function getMixedColourWithRed() {
return $this->workerService->mixWithRed();
}
}
The worker service that is being used would be based on whether the worker being processed has the colour property of Blue or Yellow.
I know I can probably use a Factory to achieve this as described here but my problem is how to tell the factory which Worker colour I am processing?
Running on Symfony 3.4
If you need more info, just ask and I will update the question.
NOTE: I'm using Symfony 4.3.1. I'll post it like that, then I'll help you to move all code from this architecture to Symfony 3.4.
I'm using a similar concept to load different classes in my project. Let me explain first, then I'll add code under this text.
Firstly, I'm loading a custom compiler pass under src/Kernel.php (your file is app/AppKernel.php):
/**
* {#inheritDoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new BannerManagerPass());
}
BannerManagerPass its created under src/DependencyInjection/Compiler (in your case should be src/BUNDLE/DependencyInjection/Compiler`).
class BannerManagerPass implements CompilerPassInterface
{
/**
* {#inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(BannerManager::class)) {
return;
}
$definition = $container->findDefinition(BannerManager::class);
$taggedServices = $container->findTaggedServiceIds('banner.process_banners');
foreach (array_keys($taggedServices) as $id) {
$definition->addMethodCall('addBannerType', [new Reference($id)]);
}
}
}
As you see, this class should implement CompilerPassInterface. You can observe that I'm looking for specific services tagged as banner.process_banners. I'll show how I tagged services a little bit later. Then, I'm calling addBannerType method from BannerManager.
App\Service\BannerManager.php: (in your case src/BUNDLE/Service/BannerManager.php)
class BannerManager
{
/**
* #var array
*/
private $bannerTypes = [];
/**
* #param BannerInterface $banner
*/
public function addBannerType(BannerInterface $banner)
{
$this->bannerTypes[$banner->getType()] = $banner;
}
/**
* #param string $type
*
* #return BannerInterface|null
*/
public function getBannerType(string $type)
{
if (!array_key_exists($type, $this->bannerTypes)) {
return null;
}
return $this->bannerTypes[$type];
}
/**
* Process request and return banner.
*
* #param string $type
* #param Server $server
* #param Request $request
*
* #return Response
*/
public function process(string $type, Server $server, Request $request)
{
return $this->getBannerType($type)->process($request, $server);
}
}
This class has a custom method (created by me) called process(). You can name it whatever you want it, but I think that's pretty verbose. All parameters are sent by me, so don't mind. You can send whatever you want.
Now we have our Manager and compiler pass is set. It's time to set our banner types (based on my example) and tag them!
My banner types are under src/Service/Banner/Types (in your case should be src/BUNDLE/Service/WhateverYouWant/Type. This does not matter! You can change it later from services.yaml).
These types are implementing my BannerInterface. It does not matter the code under the class in this instance. One more thing that I should warn you! You should see that under BannerManager, inside the addBannerType() I'm calling $banner->getType(). This is one method inherited from BannerInterface in my case and it has a unique string (in my example I have three banner types: small, normal, large). This method can have any name, but don't forget to update it as well in your manager.
We are almost ready! We should tag them, then we are ready to try them!
Go to your services.yaml and add these lines:
App\Service\Banner\Types\:
resource: '../src/Service/Banner/Types/'
tags: [banner.process_banners]
Please see the tag!
Whatever I want to show a custom banner, I'm using a simple URL with $_GET where I keep my banner type, then I load it like this:
public function view(?Server $server, Request $request, BannerManager $bannerManager)
{
...
return $bannerManager->getBannerType($request->query->get('slug'))->process($request, $server);
}

Independent functional tests with LiipFunctionalTestBundle and fixtures?

I'm adding tests to a Symfony2 project. Previously I used the same database for dev and test environments, it used a MySQL database already populated with the same data than on the production server.
The tests were working dependently because some tests depended on previous tests. For example if I had a shop website, I added a product in the cart then removed the product from the cart. So I needed to insert data by using a form, before being able to remove it.
Now I want to work with independent functional tests, because that's the recommended way (by one of Symfony2's developers).
I've configured LiipFunctionalTestBundle correctly to use a SQLite database in the test environment and I've started to add fixtures with DoctrineFixturesBundle.
But I don't know how much data I have to load for each functional test. What fixture should I load at the beginning of a test? How to deal with CRUD operations when the entity depends on other entities because of relationships between tables?
Let's say I'm developing a shop, I want a few tests:
The user add some products in its cart
The user remove one product from its cart
The user order the remaining products
Should I create a different fixture for every step? It means that my fixtures will need to exist in many different states: empty cart, cart with one product ordered, etc. It seems correct to me but very time consuming, so I'm wondering if my idea is valid.
For each test case is better to load less fixture as possible both for isolation and for performance (the test suite can go very slowly).
When fixture depends each other, you simply manage them with the doctrine reference and link each other, take care of the order also.
As Example, suppose the simply user and role relations.
A generic class for manage role fixture:
abstract class BaseLoadRoleData extends AbstractFixture implements OrderedFixtureInterface
{
public function getOrder()
{
return 1;
}
protected function createRole(ObjectManager $manager, $rolename)
{
$role= new Role();
$role->setName($rolename);
$manager->persist($role);
$manager->flush();
$this->setReference('role-' . $rolename, $role);
}
}
A Dedicated class for the Simple Role
class LoadSimpleRoleData extends BaseLoadRoleData
{
public function load(ObjectManager $manager)
{
$this->createRole($manager, Role::SIMPLE);
}
}
A Dedicated class for the Admin Role
class LoadAdminRoleData extends BaseLoadRoleData
{
public function load(ObjectManager $manager)
{
$this->createRole($manager, Role::ADMIN);
}
}
And the user:
A generic class for manage user fixture:
abstract class BaseLoadUserData extends AbstractFixture implements OrderedFixtureInterface
{
/**
* #var ContainerInterface
*/
private $container;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function getOrder()
{
return 2;
}
protected function buildUser($username, $firstName = "",$lastName ="")
{
$user= new User();
$user->setUsername($username);
$user->setFirstName($firstName);
$user->setLastName($lastName);
return $user;
}
}
A Dedicated class for the Simple User
class LoadSimpleUserData extends BaseLoadUserData {
/**
* Load data fixtures with the passed EntityManager
*
* #param Doctrine\Common\Persistence\ObjectManager $manager
*/
function load(ObjectManager $manager)
{
$user = $this->buildUser($manager, "simple#example.com");
$user->addRole($this->getReference('role-'.Role::SIMPLE));
$manager->persist($user);
$manager->flush();
$this->setReference('user-' . "admin#example.com", $user);
}
}
A Dedicated class for the Admin User
class LoadAdminUserData extends BaseLoadUserData {
/**
* Load data fixtures with the passed EntityManager
*
* #param Doctrine\Common\Persistence\ObjectManager $manager
*/
function load(ObjectManager $manager)
{
$user = $this->buildUser($manager, "admin#example.com");
$user->addRole($this->getReference('role-'.Role::ADMIN));
$manager->persist($user);
$manager->flush();
$this->setReference('user-' . "admin#example.com", $user);
}
Now you can use it separately, as example, based on the Liip Functional Test Bundle:
class LoginControllerTest {
public function testAdminUserLogin()
{
$this->loadFixtures(array(
'Acme\DemoBundle\DataFixtures\ORM\LoadAdminRoleData',
'Acme\DemoBundle\DataFixtures\ORM\LoadAdminUserData'
));
// you can now run your functional tests with a populated database
$client = static::createClient();
// ...
// test the login with admin credential
}
public function testSimpleUserLogin()
{
// add all your fixtures classes that implement
// Doctrine\Common\DataFixtures\FixtureInterface
$this->loadFixtures(array(
'Acme\DemoBundle\DataFixtures\ORM\LoadSimpleRoleData',
'Acme\DemoBundle\DataFixtures\ORM\LoadSimpleUserData'
));
// you can now run your functional tests with a populated database
$client = static::createClient();
// ...
// test the login with simple user credential
}
}
Hope this help.

symfony2 entity oneToMany and methods

Hi i had fully successfully setted my entity onetoMany and ManyToOne i generated setters and getters and in user entity it created this method:
user entity:
/**
* #ORM\OneToMany(targetEntity="TB\RequestsBundle\Entity\Requests", mappedBy="followeeuser")
*/
protected $followees;
requests entity:
/**
* #ORM\ManyToOne(targetEntity="TB\UserBundle\Entity\User", inversedBy="followees")
* #ORM\JoinColumn(name="followee_id", referencedColumnName="id", nullable=false)
*/
protected $followeeuser;
And when i using my own custom queries it works good... but i cant figure out how to use this generated function from symfony:
public function addFollowee(\TB\UserBundle\Entity\User $followee)
{
$this->followees[] = $followee;
}
I dont know what to pass there... i tried first get user object based on id of user from twig... worked good but the error occur:
$user->addFollowee($userRepository->find($target_user_id));
Found entity of type TB\UserBundle\Entity\User on association TB\UserBundle\Entity\User#followees, but expecting TB\RequestsBundle\Entity\Requests
Maybe you should think about what you're trying to before coding it. Grab a pen and a sheet of paper. :)
Tell me if I'm wrong, but here is what I think you're trying to do :
One user can have many "followee".
One "followee" can have one user.
So, a OneToMany relation is ok.
Here is how to write it, from the doc :
Requests.php (btw, you should use Request.php)
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="requests")
**/
private $user;
User.php
/**
* #ORM\OneToMany(targetEntity="Requests", mappedBy="user", cascade={"all"})
**/
private $requests;
public function __construct()
{
$this->requests = new \ArrayCollection();
}
Now you can check if you your relation is ok, and update your schema :
php app/console doctrine:schema:validate
php app/console doctrine:schema:update --force
About getters/setters :
Requests.php
public function getUser()
{
return $this->user;
}
public function setUser(User $user) // Please add a Use statement on top of your document
{
$this->user = $user;
return $this;
}
User.php
public function addRequest(Requests $request)
{
$this->requests->add($request);
return $this;
}
public function removeRequest(Requests $request)
{
$this->requests->removeElement($request);
return $this;
}
// Get requests and set requests (you know how to write those ones)
Now, to set a user to a Request, use
$request->setUser($user);
And to add a Request to a user, use
$user->addRequest($request);

Best way to create a test database and load fixtures on Symfony 2 WebTestCase?

I have a WebTestCase that executes some basic routes in my application.
I want to, on the setUp method of PHPUnit, create a test database identical to my main database, and load fixtures into it.
I'm currently doing some workaround and executing some console commands, something like this:
class FixturesWebTestCase extends WebTestCase
{
protected static $application;
protected function setUp()
{
self::runCommand('doctrine:database:create');
self::runCommand('doctrine:schema:update --force');
self::runCommand('doctrine:fixtures:load --purge-with-truncate');
}
protected static function runCommand($command)
{
$command = sprintf('%s --quiet', $command);
return self::getApplication()->run(new StringInput($command));
}
protected static function getApplication()
{
if (null === self::$application) {
$client = static::createClient();
self::$application = new Application($client->getKernel());
self::$application->setAutoExit(false);
}
return self::$application;
}
}
But I'm quite sure this is not the best approach, especially because the doctrine:fixtures:load expects the user to hit a Y char to confirm the action.
How can I solve that?
If you want to use doctrine:fixtures:load, you can use the --append option to avoid the user confirmation. Since you are recreating the database every time, purging is unnecessary. I used to use doctrine fixtures alone for testing, but have since switched to using fixtures & LiipFunctionalTestBundle to avoid DRY. This bundle makes fixtures easier to manage.
EDIT: David Jacquel's answer is the correct one for loading Doctrine Fixtures:
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
In order to bypass user confirmation you can use
doctrine:fixtures:load --no-interaction
or
doctrine:fixtures:load -n
UPDATED ANSWER
You can create a base class for your test cases which makes fixture loading easy by leveraging some classes from the Doctrine Data Fixtures library. This class would look pretty much like this:
<?php
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
abstract class FixtureAwareTestCase extends KernelTestCase
{
/**
* #var ORMExecutor
*/
private $fixtureExecutor;
/**
* #var ContainerAwareLoader
*/
private $fixtureLoader;
public function setUp()
{
self::bootKernel();
}
/**
* Adds a new fixture to be loaded.
*
* #param FixtureInterface $fixture
*/
protected function addFixture(FixtureInterface $fixture)
{
$this->getFixtureLoader()->addFixture($fixture);
}
/**
* Executes all the fixtures that have been loaded so far.
*/
protected function executeFixtures()
{
$this->getFixtureExecutor()->execute($this->getFixtureLoader()->getFixtures());
}
/**
* #return ORMExecutor
*/
private function getFixtureExecutor()
{
if (!$this->fixtureExecutor) {
/** #var \Doctrine\ORM\EntityManager $entityManager */
$entityManager = self::$kernel->getContainer()->get('doctrine')->getManager();
$this->fixtureExecutor = new ORMExecutor($entityManager, new ORMPurger($entityManager));
}
return $this->fixtureExecutor;
}
/**
* #return ContainerAwareLoader
*/
private function getFixtureLoader()
{
if (!$this->fixtureLoader) {
$this->fixtureLoader = new ContainerAwareLoader(self::$kernel->getContainer());
}
return $this->fixtureLoader;
}
}
Then, in your test case, simply extend the above class and before your test add all the needed fixtures and execute them. This will automatically purge your database before loading fixtures. Example follows:
class MyTestCase extends FixtureAwareTestCase
{
public function setUp()
{
parent::setUp();
// Base fixture for all tests
$this->addFixture(new FirstFixture());
$this->addFixture(new SecondFixture());
$this->executeFixtures();
// Fixtures are now loaded in a clean DB. Yay!
}
}
OLD ANSWER
(I decided to "deprecate" this answer because it only explains how to clean up the database without telling how to load fixtures after).
There's an even cleaner way of accomplishing this without having to run commands. It basically consists in using a combination of the SchemaTool and the ORMPurger. You can create an abstract base class which performs this kind of operations to avoid repeating them for each specialized test case. Here's a code example of a test case class which sets up database for a generic test case:
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\Tools\SchemaTool;
abstract class DatabaseAwareWebTestCase extends WebTestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
$kernel = static::createKernel();
$kernel->boot();
$em = $kernel->getContainer()->get('doctrine')->getManager();
$schemaTool = new SchemaTool($em);
$metadata = $em->getMetadataFactory()->getAllMetadata();
// Drop and recreate tables for all entities
$schemaTool->dropSchema($metadata);
$schemaTool->createSchema($metadata);
}
protected function tearDown() {
parent::tearDown();
$purger = new ORMPurger($this->getContainer()->get('doctrine')->getManager());
$purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
$purger->purge();
}
}
This way, before running each test case which inherits from the above class, the database schema will be rebuilt from scratch, then cleaned up after every test run.
Hope this helps.
I've stumbled upon a really neat bundle named Doctrine-Test-Bundle
Instead of creating and dropping schema on every test it simply rollback.
My Tests went from 1m40s to.. 2s. And it's isolated.
All you need is a clear test database and it'll do the trick.
I used this command:
yes | php app/console doctrine:fixtures:load --purge-with-truncate
But of course LiipFunctionalTestBundle looks promising.
I wanted to load all your fixtures like the doctrine:fixtures:load command does. I didn't want to run exec from inside the test case because it seemed like a messy way to do things. I looked at how the doctrine command does this itself and just copied over the relevant lines.
I extended from the Symfony WebTestCase and after the Kernel was created I just called my method which works exactly like the Doctrine load-fixtures command.
/**
* Load fixtures for all bundles
*
* #param Kernel $kernel
*/
private static function loadFixtures(Kernel $kernel)
{
$loader = new DataFixturesLoader($kernel->getContainer());
$em = $kernel->getContainer()->get('doctrine')->getManager();
foreach ($kernel->getBundles() as $bundle) {
$path = $bundle->getPath().'/DataFixtures/ORM';
if (is_dir($path)) {
$loader->loadFromDirectory($path);
}
}
$fixtures = $loader->getFixtures();
if (!$fixtures) {
throw new InvalidArgumentException('Could not find any fixtures to load in');
}
$purger = new ORMPurger($em);
$executor = new ORMExecutor($em, $purger);
$executor->execute($fixtures, true);
}
Just recently the bundle hautelook/AliceBundle expose two traits to help you solve the use case of loading fixtures in functional tests: RefreshDatabaseTrait and ReloadDatabaseTrait.
From the doc:
namespace App\Tests;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class NewsTest extends WebTestCase
{
use RefreshDatabaseTrait;
public function postCommentTest()
{
$client = static::createClient(); // The transaction starts just after the boot of the Symfony kernel
$crawler = $client->request('GET', '/my-news');
$form = $crawler->filter('#post-comment')->form(['new-comment' => 'Symfony is so cool!']);
$client->submit($form);
// At the end of this test, the transaction will be rolled back (even if the test fails)
}
}
And you are good !

Environment specific data fixtures with Symfony+Doctrine

With Smyfony2 and Doctrin2, data fixtures can be created using the following example: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
What I would like is to be able to use this concept for testing so that setup/teardown can create a pure test data environment for functional testing. How might I go about having a specific set of test-only fixtures run during functional tests and how do I separate these fixtures from my standard fixtures so that the console command ignores them?
It seems that the way to do it would be to replicate the functionality of the doctrine:fixtures console command and store the test fixtures elsewhere. Does anyone have a better solution?
An alternative to breaking out fixtures by directory is to use a custom fixture class. Your fixture classes would then extend this class and specify the environments it will actually be loaded in.
<?php
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\KernelInterface;
/**
* Provides support for environment specific fixtures.
*
* This container aware, abstract data fixture is used to only allow loading in
* specific environments. The environments the data fixture will be loaded in is
* determined by the list of environment names returned by `getEnvironments()`.
*
* > The fixture will still be shown as having been loaded by the Doctrine
* > command, `doctrine:fixtures:load`, despite not having been actually
* > loaded.
*
* #author Kevin Herrera <kevin#herrera.io>
*/
abstract class AbstractDataFixture implements ContainerAwareInterface, FixtureInterface
{
/**
* The dependency injection container.
*
* #var ContainerInterface
*/
protected $container;
/**
* {#inheritDoc}
*/
public function load(ObjectManager $manager)
{
/** #var KernelInterface $kernel */
$kernel = $this->container->get('kernel');
if (in_array($kernel->getEnvironment(), $this->getEnvironments())) {
$this->doLoad($manager);
}
}
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* Performs the actual fixtures loading.
*
* #see \Doctrine\Common\DataFixtures\FixtureInterface::load()
*
* #param ObjectManager $manager The object manager.
*/
abstract protected function doLoad(ObjectManager $manager);
/**
* Returns the environments the fixtures may be loaded in.
*
* #return array The name of the environments.
*/
abstract protected function getEnvironments();
}
Your fixtures would end up looking like this:
<?php
namespace Vendor\Bundle\ExampleBundle\DataFixtures\ORM;
use AbstractDataFixture;
use Doctrine\Common\Persistence\ObjectManager;
/**
* Loads data only on "prod".
*/
class ExampleData extends AbstractDataFixture
{
/**
* #override
*/
protected function doLoad(ObjectManager $manager)
{
// ... snip ...
}
/**
* #override
*/
protected function getEnvironments()
{
return array('prod');
}
}
I believe that this should work with both ORM an ODM data fixtures.
The easiest way is to put your fixtures into different folders and then load them with the php app/console doctrine:fixtures:load --fixtures=../src/Acme/TestBundle/DataFixtures/ORM/test command. The fixtures option must point to the relative path from where you app folder!
You can then split up your data into initial, test and so on or create dev, test, staging, prod fixtures, just as you like.
If you want to mix them up, I don't know any better solution than what I did: I createt a "templates" folder where all fixtures reside in. In my dev folder, I create one class which extends the proper fixture class from template and adjusts what is needed to adjust (like overriding the getOrder method). It's not perfect and I guess one could think about extending the fixtures:load command to take multiple paths, but it works for me.

Resources