Please help me to translate custom annotation.
I'm trying to translate #Render(title="Page"). Translate generator not found this, and title not traslate.
I try to understand how it is done in the component validation Symfony but nothing happens.
<?php
namespace Shooos\ProductBundle\Controller\Admin;
use Sensio\Bundle\FrameworkExtraBundle\Configuration as PRS;
use Shooos\CoreBundle\Controller\BaseController;
use Aft\RenderParkingBundle\Annotations as CA;
use Gedmo\Mapping\Annotation\Translatable;
/**
* #PRS\Route("/admin")
* Class CategoryController
* #package Shooos\ProductBundle\Controller\Admin
*/
class CategoryController extends BaseController
{
/**
* #CA\Render(title="Categories")
* #PRS\Route("/categories", name="admin.categories")
*/
public function indexAction()
{
}
}
<?php
namespace Aft\RenderParkingBundle\Annotations\Driver;
use Doctrine\Common\Annotations\Reader;
use Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Aft\RenderParkingBundle\Annotations;
use Symfony\Component\Translation\TranslatorInterface;
class AnnotationDriver
{
/**
* #var Reader
*/
private $reader;
/**
* #var TemplateGuesser
*/
private $guesser;
/**
* #var TranslatorInterface
*/
private $translator;
public function __construct(Reader $reader, TemplateGuesser $guesser, TranslatorInterface $translator)
{
$this->reader = $reader;
$this->guesser = $guesser;
$this->translator = $translator;
}
/**
* This event occurs when call any controller
*/
public function onKernelController(FilterControllerEvent $event)
{
/** Controller exists */
if (!is_array($controller = $event->getController())) {
return;
}
/**
* Controller
* #var \ReflectionObject $object
*/
$object = new \ReflectionObject($controller[0]);
$method = $object->getMethod($controller[1]);
foreach ($this->reader->getMethodAnnotations($method) as $configuration) {
if ($configuration instanceof Annotations\Render) {
$request = $event->getRequest();
$title = $this->translator->trans($configuration->getTitle());
$request->attributes->set('_page_title', $title);
if (null === $configuration->getTemplate()) {
$configuration->setTemplate(
$this->guesser->guessTemplateName(
$controller,
$request
));
}
$request->attributes->set('_page_template', $configuration->getTemplate());
}
}
}
}
On your annotation to object converter, where you inject the annotation reader, inject the translator service and translate the value at the transformation process, from annotation to object.
$description = $this->translator->trans($transformedAnnotationObject->getDescription());
Related
based on the example LessThan.html#propertypath I would like to write a FunctionalTest for my own validator #Assert\Amount. $amount is to be validated depending on $currency.
My question is, how can I set the value for $currency in the FunctionalTest for the Amount Validator? I have looked at the tests of the package symfony/validator but can't find a clue. I have already written FunctionalTests for validators. But I am not getting anywhere with this requirement.
Could someone give me a hint or show me an example.
Example:
class Entity
{
/**
* #var \string
* #Assert\Currency()
*/
protected $currency;
/**
* #var \float
* #Assert\Amount(propertyPath="currency")
*/
protected $amount;
}
I have found a solution. I validated the complete entity and looked at the object in the validator and found the complete entity.
When I thought about how to test it, I did some research and found other tests that did the same. I don't know if this is the best solution, but I can now write my tests.
ConstraintValidatorTestCase
<?php
namespace App\Tests\Validator\Constraint;
use App\Entity\Entity;
use App\Validator\MinimumAmount;
use App\Validator\MinimumAmountValidator;
use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\ConstraintValidatorInterface;
class MinimumAmountValidatorTest extends ConstraintValidatorTestCase
{
/**
* #var ExecutionContextInterface
*/
protected $context;
/**
* #var ConstraintValidatorInterface
*/
protected $validator;
/**
* #var Constraint
*/
protected $constraint;
protected function setUp(): void
{
$this->constraint = new Entity();
$this->context = $this->createContext();
$this->validator = $this->createValidator();
$this->validator->initialize($this->context);
}
public function createValidator(): MinimumAmountValidator
{
return new MinimumAmountValidator();
}
public function createContext(): ExecutionContext
{
$myEntity = new Entity();
$myEntity->setCurrency('EUR');
$translator = $this->createMock(TranslatorInterface::class);
$translator->expects($this->any())->method('trans')->willReturnArgument(0);
$validator = $this->createMock(ValidatorInterface::class);
$executionContext = new ExecutionContext($validator, $myEntity, $translator);
$context->setNode('InvalidValue', null, null, 'property.path');
$context->setConstraint($this->constraint);
return $executionContext;
}
public function testValidation()
{
$this->validator->validate(40.00, new MinimumAmount());
$this->assertNoViolation();
}
}
Constraint
<?php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class MinimumAmount extends Constraint
{
/**
* #var string
*/
public $message = '';
/**
* #return string
*/
public function validatedBy(): string
{
return MinimumAmountValidator::class;
}
}
Validator
<?php
namespace App\Validator;
use App\Entity\Entity;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class MinimumAmountValidator extends ConstraintValidator
{
/**
* #param float $value
* #param Constraint $constraint
*
* #return void
*/
public function validate($value, Constraint $constraint)
{
/**
* #var Entity
*/
$contextRoot = $this->context->getRoot();
$currency = $contextRoot->getCurrency(); // EUR
$amount = $value; // 40.00
// Validation implementation
}
}
I am building my own CMS system. And i did a lot of work with symfony in the past, now i wanna do all a bit more pro :) I want to render basic controller for admin dashboard witch contains a menu, systemnotifications on route "/admin" and then i want to set another controller for example "test" on route "/admin/test" and my problem is that all object notifications from controller named AdminController are not available in this second route "/admin/test", only on route "/admin"
Here my adminControler controller:
class AdminController extends Controller
{
/**
* #Route("/admin", name="adminDashboard")
*/
public function adminDashboard()
{
$loggedUser = $this->getUser()->getId();
$systemnotifications = $this->forward('App\Controller\SystemNotificationController::notif', [
'loggedUser' => $loggedUser
]);
return $this->render('admin/index.html.twig', [
'systemnotifications' => $systemnotifications
]);
}
}
Here my test controller:
class TestController extends Controller
{
/**
* #Route("/admin/test", name="test")
*/
public function test()
{
return $this->render('admin/dashboard/index.html.twig', [
]);
}
}
In twig is set, that adminController extends base.html.twig, and Test controller extends index.html.twig (this one witch is rendered from adminController.
My question is how to handle it properly with Symfony best practice. How i should set the Admin Controller for get systemnotifications object where is another Test Controller launched ?
Please help :)
There are two ways to do that the first is inyect in the twig the variable. example see this doc.
# config/packages/twig.yaml
twig:
# ...
globals:
# the value is the service's id
user_management: '#App\DataProvider\UserDataProvider'
# config/services.yaml
services:
'App\DataProvider\UserDataProvider':
arguments:
- '#session'
autoconfigure: false
The other way is more complicate, if you for example wants the responsability to render specific part of the page, like ... the barnav or the messages
Add this piece of code to the default twig:
{% block user_control %}
{{ render(controller('LayoutCoreBundle:User:index')) }}
{% endblock %}
<?php
namespace App\Controller;
use App\Event\ShowUserEvent;
use App\Event\ThemeEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use \stdClass;
class UserController extends EmitterController
{
/**
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction()
{
if (!$this->getDispatcher()->hasListeners(ThemeEvents::THEME_NAVBAR_USER)) {
return new Response();
}
/** #var ShowUserEvent $userEvent */
$userEvent = $this->triggerMethod(ThemeEvents::THEME_NAVBAR_USER, new ShowUserEvent());
$userClass = $userEvent->getUser();
$user = new stdClass();
$user->id = $userClass->getIdentifier();
$user->idEmployee = $userClass->getIdEmployee();
$user->setCompanyLogo = $userClass->getCompanyLogo();
$user->companyName = $userClass->getCompanyName();
$user->company = $userClass->getCompany();
$user->avatar = $userClass->getAvatar();
$user->fullName = $userClass->getName();
$user->menu = $userClass->getMenu();
$user->role = $userClass->getRolname();
return $this->render(
'header/index.html.twig',
[
'userJson' => $user,
]
);
}
}
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class EmitterController extends AbstractController
{
/**
* #var EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* #param EventDispatcherInterface $dispatcher
*/
public function __construct(EventDispatcherInterface $dispatcher)
{
$this->eventDispatcher = $dispatcher;
}
/**
* #return EventDispatcherInterface
*/
protected function getDispatcher()
{
return $this->eventDispatcher;
}
/**
* #param string $eventName
*
* #return bool
*/
protected function hasListener($eventName)
{
return $this->getDispatcher()->hasListeners($eventName);
}
/**
* Will look for a method of the format "on<CamelizedEventName>" and call it with the event as argument.
*
*
* Then it will dispatch the event as normal via the event dispatcher.
*
* #param $eventName
* #param Event $event
*
* #return Event
*/
protected function triggerMethod($eventName, Event $event)
{
$method = sprintf('on%s', Container::camelize(str_replace('.', '_', $eventName)));
if (is_callable([$this, $method])) {
call_user_func_array([$this, $method], [$event]);
}
if ($event->isPropagationStopped()) {
return $event;
}
$this->getDispatcher()->dispatch($eventName, $event);
return $event;
}
}
interface ThemeEvents
{
/**
* Used to receive notification data
*/
public const THEME_NOTIFICATIONS = 'theme.notifications';
/**
* Used to receive message data
*/
public const THEME_MESSAGES = 'theme.messages';
/**
* Used to receive task data
*/
public const THEME_TASKS = 'theme.tasks';
/**
* Used to receive the current user for the navbar
*/
public const THEME_NAVBAR_USER = 'theme.navbar_user';
/**
* Used to receive breadcrumb data
*/
public const THEME_BREADCRUMB = 'theme.breadcrumb';
/**
* Used to receive the current user for the sidebar
*/
public const THEME_SIDEBAR_USER = 'theme.sidebar_user';
/**
* Used to receive the sidebar menu data
*/
public const THEME_SIDEBAR_SETUP_MENU = 'theme.sidebar_setup_menu';
}
class ShowUserEvent extends ThemeEvent
{
/**
* #var UserInterface
*/
protected $user;
/**
* #var bool
*/
protected $showProfileLink = true;
/**
* #var bool
*/
protected $showLogoutLink = true;
/**
* #var NavBarUserLink[]
*/
protected $links = [];
/**
* #param UserInterface $user
* #return ShowUserEvent
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* #return UserInterface
*/
public function getUser()
{
return $this->user;
}
/**
* #return NavBarUserLink[]
*/
public function getLinks()
{
return $this->links;
}
/**
* #param NavBarUserLink $link
* #return ShowUserEvent
*/
public function addLink(NavBarUserLink $link)
{
$this->links[] = $link;
return $this;
}
/**
* #return bool
*/
public function isShowProfileLink()
{
return $this->showProfileLink;
}
/**
* #param bool $showProfileLink
* #return ShowUserEvent
*/
public function setShowProfileLink($showProfileLink)
{
$this->showProfileLink = $showProfileLink;
return $this;
}
/**
* #return bool
*/
public function isShowLogoutLink()
{
return $this->showLogoutLink;
}
/**
* #param bool $showLogoutLink
* #return ShowUserEvent
*/
public function setShowLogoutLink($showLogoutLink)
{
$this->showLogoutLink = $showLogoutLink;
return $this;
}
}
class ThemeEvent extends Event
{
}
And then only you need a evensuscriber
class NavbarUserSubscriber implements EventSubscriberInterface
{
/**
* #var Security
*/
protected $security;
/**
* #param Security $security
*/
public function __construct(Security $security)
{
$this->security = $security;
}
/**
* #return array
*/
public static function getSubscribedEvents(): array
{
return [
ThemeEvents::THEME_NAVBAR_USER => ['onShowUser', 100],
ThemeEvents::THEME_SIDEBAR_USER => ['onShowUser', 100],
];
}
/**
* #param ShowUserEvent $event
* #throws \Exception
*/
public function onShowUser(ShowUserEvent $event)
{
if (null === $this->security->getUser()) {
return;
}
/** #var User $user */
$user = $this->security->getUser();
$event->setUser($user);
}
}
#templates/header/index.html.twig
<script type="text/javascript">
var user = {{ userJson | json_encode() | raw }};
</script>
I am using symfony 3. I already created some events and they work fine. But this new event is different. I must somehow send some additional parameters to subscriber. What is the best way to do that? I created phenstalk service to perform job. The controller dispatch an event like:
$dispatcher = $this->container->get('event_dispatcher');
$dispatcher->dispatch(Events::POWERPLANT_GET_DATA, new PowerPlantEvent($this->getPowerPlantAction($id)));
Call like this will not work, cuz I am missing some parameters.
and I have action in subscriber:
public function onPowerPlantGetData(PowerPlantEvent $event, $startDate, $endDate, $measurement, $field)
How to do that?
If you event uses optional parameters, you can make them optional in the constructor.
use Symfony\Component\EventDispatcher\Event;
final class PowerPlantEvent extends Event
{
/**
* #var PowerPlantAction
*/
private $powerPlantAction;
/**
* #var \DateTime
*/
private $startDate;
/**
* #var \DateTime
*/
private $endDate;
/**
* #var int
*/
private $measurement;
/**
* #var string
*/
private $field;
public function __construct(
$powerPlantAction,
$startDate = null,
$endDate = null,
$measurement = null,
$field = null
) {
$this->powerPlantAction = $powerPlantAction;
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->measurement = $measurement;
$this->field = $field;
}
public function getPowerPlantAction()
{
return $this->powerPlantAction;
}
public function getStartDate()
{
return $this->startDate;
}
public function getEndDate()
{
return $this->endDate;
}
public function getMeasurement()
{
return $this->measurement;
}
public function getField()
{
return $this->field;
}
}
Then dispatching like this:
$this->eventDispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent($this->getPowerPlantAction($id))
);
Or:
$this->eventDispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent(
$this->getPowerPlantAction($id),
$startDate,
$endDate,
$measurement,
$field
)
);
In your subscriber just use those getters:
public function onPowerPlantGetData(PowerPlantEvent $powerPlantEvent)
{
$powerPlantEvent->getStartDate();
// ...
}
You should put all necessary data into Event itself and then you can access them in subscriber
For simplicity I made properties public and added all to __construct but it's up to you how you implement it. You might want to create new Event class for that
class PowerPlantEvent extends Event
{
/**
* #var \DateTime
*/
public $startDate;
/**
* #var \DateTime
*/
public $endDate;
/**
* #var Something
*/
public $measurement;
public $field;
/**
* PowerPlantEvent constructor.
*
* #param \DateTime $startDate
* #param \DateTime $endDate
* #param Something $measurement
* #param $field
*/
public function __construct(\DateTime $startDate, \DateTime $endDate, Something $measurement, $field)
{
$this->startDate = $startDate;
$this->endDate = $endDate;
$this->measurement = $measurement;
$this->field = $field;
}
}
and call it with
$dispatcher = $this->container->get('event_dispatcher');
$dispatcher->dispatch(
Events::POWERPLANT_GET_DATA,
new PowerPlantEvent($startDate, $endDate, $measurement, $field)
);
and access in subscriber (or event listener)
public function onPowerPlantGetData(PowerPlantEvent $event)
{
$startDate = $event->startDate; // you might want to implement getters for those
}
I'am new in testing PHP with PHPSpec. I have a class where i inject symfony current logged user (TokenStorageInterface). And make changes with that user.
<?php
namespace AppBundle\Service;
use AppBundle\Entity\Payment;
use AppBundle\Entity\User;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class TransferService
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var User
*/
private $currentUser;
/**
* #var InvoiceService
*/
private $invoiceService;
/**
* PaymentManager constructor.
* #param EntityManager $entityManager
* #param TokenStorageInterface $tokenStorage
* #param InvoiceService $invoiceService
*/
public function __construct(
EntityManager $entityManager,
TokenStorageInterface $tokenStorage,
InvoiceService $invoiceService
) {
$this->entityManager = $entityManager;
if ($tokenStorage->getToken() === null) {
throw new \Exception('User not logged in');
}
$this->currentUser = $tokenStorage->getToken()->getUser();
$this->invoiceService = $invoiceService;
}
/**
* #param Payment $payment
*/
public function transfer(Payment $payment)
{
$payer = $this->currentUser;
$amount = $payment->getAmount();
$receiver = $payment->getReceiver();
if ($payer === $receiver) {
throw new \LogicException('Cannot be same User');
}
if ($payer->getBalance() < $amount) {
throw new \LogicException('Not enough in balance');
}
$payment->setPayer($payer);
//TODO: Move to class?
$this->subtractBalance($payer, $amount);
$this->addBalance($receiver, $amount);
$this->invoiceService->createInvoice($payment);
$this->entityManager->persist($payment);
$this->entityManager->flush();
}
/**
* #param User $user
* #param $amount
*/
private function subtractBalance(User $user, $amount)
{
$user->setBalance($user->getBalance() - $amount);
}
/**
* #param User $user
* #param $amount
*/
private function addBalance(User $user, $amount)
{
$temp = $user->getBalance();
$user->setBalance($user->getBalance() + $amount);
}
}
And have wrote Spec for that class:
<?php
namespace spec\AppBundle\Service;
use AppBundle\Entity\Payment;
use AppBundle\Entity\User;
use AppBundle\Service\InvoiceService;
use Doctrine\ORM\EntityManager;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class TransferServiceSpec extends ObjectBehavior
{
function let(EntityManager $entityManager, TokenStorage $tokenStorage, InvoiceService $invoiceService)
{
$user = new User();
$user->setUsername('aaa');
$user->setBalance(100.10);
$temp = new UsernamePasswordToken($user, null, 'main', ['ROLE_USER']);
$tokenStorage->getToken()->willReturn($temp);
$this->beConstructedWith($entityManager, $tokenStorage, $invoiceService);
}
function it_is_initializable()
{
$this->shouldHaveType('AppBundle\Service\TransferService');
}
function it_should_transfer_money(
User $user,
EntityManager $entityManager,
TokenStorageInterface $tokenStorage,
InvoiceService $invoiceService,
Payment $payment
) {
$user->getBalance()->willReturn(0);
$user->setBalance(99.9)->shouldBeCalled();
$payment->getReceiver()->willReturn($user);
//TODO how to check injected current user?
//$payment->getPayer()->willReturn($tokenStorage->getToken());
$payment->getAmount()->willReturn(99.9);
$invoiceService->createInvoice($payment)->shouldBeCalled();
$entityManager->persist($payment)->shouldBeCalled();
$entityManager->flush()->shouldBeCalled();
$this->transfer($payment);
}
}
The problem is, how to check that changes were made (to test that balance was edited) in current user (injected token storage getUser()) because following method dont work:
$payment->getPayer()->willReturn($tokenStorage->getToken()->getUser());
Call to undefined method Prophecy\Prophecy\MethodProphecy::getUser()
You should not call methods on prophecy, but mock everything instead, see:
function it_should_transfer_money(
User $user,
EntityManager $entityManager,
TokenStorageInterface $tokenStorage,
TokenInterface $token,
UserInterface $user,
InvoiceService $invoiceService,
Payment $payment
) {
$user->getBalance()->willReturn(0);
$user->setBalance(99.9)->shouldBeCalled();
$payment->getReceiver()->willReturn($user);
$tokenStorage->getToken()->willReturn($token);
$token->getUser()->willReturn($user);
$payment->getPayer()->willReturn($user);
$payment->getAmount()->willReturn(99.9);
$invoiceService->createInvoice($payment)->shouldBeCalled();
$entityManager->persist($payment)->shouldBeCalled();
$entityManager->flush()->shouldBeCalled();
$this->transfer($payment);
}
Attempting to build a user data fixture results in
Fatal error: Call to a member function get() on a non-object in
...\Tests\Repository\DataFixtures\LoadAdminUserData.php on line 35
line 35:
$discriminator = $this->container->get('pugx_user.manager.user_discriminator');
Complete fixture (w/ namespace edit)
<?php
//src\Vol\VolBundle\\DataFixtures\ORM\LoadAdminUserData
namespace Vol\VolBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Description of LoadAdminUserData
*
*/
class LoadAdminUserData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
/**
* #var ContainerInterface
*/
private $container;
/**
* {#inheritDoc}
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function load(ObjectManager $manager)
{
$discriminator = $this->container->get('pugx_user.manager.user_discriminator');
$discriminator->setClass('Vol\VolBundle\Entity\Admin');
$userManager = $this->container->get('pugx_user_manager');
$admin = $userManager->createUser();
$admin->setUsername('bborko');
$admin->setEmail('bborko#bogus.info');
$admin->setPlainPassword('123456');
$admin->setEnabled(true);
$userManager->updateUser($admin, true);
$this->addReference('test-user', $admin);
}
public function getOrder()
{
return 3;
}
}
Test
<?php
//src\Vol\VolBundle\Tests\Repository
namespace Vol\VolBundle\Tests\Repository;
use Vol\VolBundle\DataFixtures\ORM\DoctrineTestCase;
/**
* Description of AdminUserRepositoryTest
*
* #author George
*/
class AdminUserRepositoryTest extends DoctrineTestCase
{
/**
* Set up repository test
*/
public function setUp()
{
$this->loadFixturesFromDirectory($this->dir);
}
/**
* Test finding all countries ordered
*/
public function testFindAll()
{
$focuses = $this->getRepository()->findAll();
$this->assertCount(1, $focuses, 'Should return 1 admin user');
}
/**
* Returns repository
*
* #return \Vol\VolBundle\Entity\Admin
*/
protected function getRepository()
{
return $this->em->getRepository('\Vol\VolBundle\Entity\Admin');
}
}
class DoctrineTestCase
<?php
//src\Vol\VolBundle\Tests\Repository
namespace Vol\VolBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* Class DoctrineTestCase
*
* This is the base class to load doctrine fixtures using the symfony configuration
*/
class DoctrineTestCase extends WebTestCase
{
/**
* #var \Symfony\Component\DependencyInjection\Container
*/
protected $container;
/**
* #var \Doctrine\ORM\EntityManager
*/
protected $em;
/**
* #var string
*/
protected $environment = 'test';
/**
* #var bool
*/
protected $debug = true;
/**
* #var string
*/
protected $entityManagerServiceId = 'doctrine.orm.entity_manager';
protected $dir;
/**
* Constructor
*
* #param string|null $name Test name
* #param array $data Test data
* #param string $dataName Data name
*/
public function __construct($name = null, array $data = array(), $dataName = '')
{
parent::__construct($name, $data, $dataName);
if (!static::$kernel) {
static::$kernel = self::createKernel(array(
'environment' => $this->environment,
'debug' => $this->debug
));
static::$kernel->boot();
}
$this->container = static::$kernel->getContainer();
$this->em = $this->getEntityManager();
$this->dir = __DIR__;
}
/**
* Executes fixtures
*
* #param \Doctrine\Common\DataFixtures\Loader $loader
*/
protected function executeFixtures(Loader $loader)
{
$purger = new ORMPurger();
$executor = new ORMExecutor($this->em, $purger);
$executor->execute($loader->getFixtures());
}
/**
* Load and execute fixtures from a directory
*
* #param string $directory
*/
protected function loadFixturesFromDirectory($directory)
{
$loader = new Loader();
$loader->loadFromDirectory($directory);
$this->executeFixtures($loader);
}
/**
* Returns the doctrine orm entity manager
*
* #return object
*/
protected function getEntityManager()
{
return $this->container->get($this->entityManagerServiceId);
}
}
Solution:
You'll have to pass the container to your fixture-loader inside the TestCase yourself.
Just use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader ...
... which extends Doctrine\Common\DataFixtures\Loader and expects the container ...
... as constructor argument.
use Symfony\Bridge\Doctrine\DataFixtures\ContainerAwareLoader;
protected function loadFixturesFromDirectory($directory)
{
$loader = new ContainerAwareLoader($this->container);
$loader->loadFromDirectory($directory);
$this->executeFixtures($loader);
}
When does symfony2 inject the container automatically?
The FrameworkBundle injects the container into instances of ContainerAwareInterface being controllers or commands only.
The corresponding code can be found here and here.
DataFixtures can be container-aware, too.
DoctrineFixtureBundle's Command searches for (container-aware) fixtures and injects the container.
Only fixtures that conventially live in a bundle's DataFixtures/ORM folder are processed.
A quick look at the code reveals it:
foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
$paths[] = $bundle->getPath().'/DataFixtures/ORM';
}
The --fixtures flag:
Appending the --fixtures flag - which can be a string or an array - to the doctrine:fixtures:load allows to add additional fixture-paths for processing.