symfony 5 - event not being dispatched - php

I use an event subscriber to handle some actions when my order form is submitted.
Problem my event is not being dispached but symfony is able to find him because he tells me that my OrderEvent::ORDER_CREATE is orphan.
I excpected that execution was stopped with die('Hello you from subscriber'); but it's not.
Controller
public function commanderPanierAction(Request $request, SelectionWeb $selectionWeb, TableLumineuse $tableLumineuse, EventDispatcherInterface $eventDispatcher)
{
// DO PREVIOUS STUFF
$Order = new Order();
$OrderForm = $this->createForm(OrderForm::class, $Order);
if ($request->isMethod('POST')) {
$OrderForm->handleRequest($request);
if ($OrderForm->isSubmitted() && $OrderForm->isValid()) {
// OrderForm is valid proceed
$eventDispatcher->dispatch(
new OrderEvent($Order),
OrderEvent::ORDER_CREATE
);
}
}
OrderEvent
<?php
namespace App\Event;
use App\Entity\Order;
use Symfony\Contracts\EventDispatcher\Event;
class OrderEvent extends Event
{
public const ORDER_CREATE = 'order.created';
protected $order;
public function __construct(Order $order)
{
$this->order= $order;
}
public function getOrder(): Order
{
return $this->order;
}
}
OrderSubscriber
<?php
namespace App\EventSubscriber;
use App\Event\CommandeWebEvent;
use App\Service\SelectionWeb\SelectionWeb;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OrderSubscriber implements EventSubscriberInterface
{
private $entityManager;
private $selectionWeb;
public function __construct(EntityManagerInterface $entityManager, SelectionWeb $selectionWeb)
{
$this->entityManager = $entityManager;
$this->selectionWeb = $selectionWeb;
}
public static function getSubscribedEvents()
{
return [
OrderEvent::ORDER_CREATE => [
// The higher the number, the earlier the method is called.
['processOrder', 10],
['notifyOrder', -10]
]
];
}
public function processOrder(OrderEvent $event)
{
// TODO
die('Hello you from subscriber');
}
public function notifyOrder(OrderEvent $event)
{
// TODO
}
}
EDIT
The only workaround found (thx to #nikserg) is to inject subscriber into controller action (subscriber has dependencies) then register my subscriber as service in services.yaml finaly use $eventDispatcher->addSubscriber($subscriber); before $eventDispatcher->dispatch(new OrderEvent($Order),OrderEvent::ORDER_CREATE);
It seems all that stuff is really complex for a task as simple as that
EDIT2
I found an another way I'm able to execute my subscriber without usage of $eventDispatcher->addSubscriber($subscriber); and only with $eventDispatcher->dispatch(new OrderEvent($Order)); only if I configure my subscriber as service in services.yaml but why symfony does need this information in services.yaml ? Thought that everything in src/ is avaible to be used as service..
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# If I add those It works
App\EventSubscriber\OrderSubscriber:
autowire: true
EDIT3
My OrderSubscriber is loaded into container so why I should set it explicitly to being execute ? I can't figure out what's going on
php bin/console debug:container
---------------------------------------- ---------------------------------------
Service ID Class name
---------------------------------------- ----------------------------------------
App\EventSubscriber\OrderSuscriber App\EventSubscriber\OrderSuscriber
EDIT 4
If I set my OrderSubscriber explicitly there is two instances of it into container.
Why symfony execute one set explicitly and not the one set with resource: '../src/*'

Symfony will autowire your subscriber as service, if you will require it as argument in action:
public function commanderPanierAction(Request $request, SelectionWeb $selectionWeb, TableLumineuse $tableLumineuse, OrderSubscriber $orderSubscriber)
Of course, if your subscriber is registered properly.
But let me advice you not to create subscribers as objects manually. The main good thing about subscribers is that you know nothing about them, when you fire event. There could be dozens of subscribers to this event, and all of them will proceed your event. That will keep your code nice and lower cohesion.
It's in docs: https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber

First I want to thank you all for your time and let me apologize my problem was due to a typo I wrote OrderSuscriber instead of OrderSubscriber that's why there was 2 services into my container and why defined service explicitly was working.

Related

How should one implement a custom Doctrine purger when using Symfony 6?

The Symfony docs shows a solution, but it doesn't appear to work (i.e. Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory needs to be replaced with Doctrine\Bundle\FixturesBundle\Purger\ORMPurgerFactory, and other changes). I modified the code as shown below, but am pretty certain I am not doing it correctly.
<?php
declare(strict_types=1);
namespace App\DataFixtures\Purger;
use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Bundle\FixturesBundle\Purger\ORMPurgerFactory;
class CustomPurgerFactory implements PurgerFactory
{
public function __construct(private ORMPurgerFactory $purgeFactory)
{
}
public function createForEntityManager(?string $emName, EntityManagerInterface $em, array $excluded = [], bool $purgeWithTruncate = false) : PurgerInterface
{
// Change $excluded, $purgeWithTruncate as desired.
return new CustomPurger($emName, $em, $excluded, $purgeWithTruncate, $this->purgeFactory);
}
}
<?php
declare(strict_types=1);
namespace App\DataFixtures\Purger;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Bundle\FixturesBundle\Purger\ORMPurgerFactory;
class CustomPurger implements ORMPurgerInterface
{
public function __construct(private ?string $emName, private EntityManagerInterface $entityManager, private array $excluded, private bool $purgeWithTruncate, private ORMPurgerFactory $purgeFactory)
{
}
public function setEntityManager(EntityManagerInterface $entityManager):void
{
// Seems rather redundent doing this even though I earlier inject $entityManager.
$this->entityManager = $entityManager;
}
public function purge() : void
{
// Delete any tables which must be deleted first to prevent FK constraint errors.
// This doesn't seem write.
$purger = $this->purgeFactory->createForEntityManager($this->emName, $this->entityManager, $this->excluded, $this->purgeWithTruncate);
$purger->purge();
}
}
services:
App\DataFixtures\Purger\DoctrinePurgerFactory:
tags:
- { name: 'doctrine.fixtures.purger_factory', alias: 'my_purger' }
arguments:
- '#doctrine.fixtures.purger.orm_purger_factory'
Or should it be done by decorating the default purger as suggested by this post?
Okay. So you do have a few things wrong and the docs are somewhat out of date. From a big picture point of view you want something like:
bin/console doctrine:fixtures:load --purger=my_purger
to use your custom purger factory (aliased as my_purger) to instantiate and execute your custom purger's purge method. The job of the factory is to just create the purger not to execute it.
I followed the docs and implemented PurgerInterface but the purge command complained about it not implementing ORMPurgerInterface which, as you noted, adds a seemingly superfluous method. I think it is still a work in progress. The default ORMPurger has a couple of additional public methods not defined in any interface which is also strange. The fact that Doctrine is inconsistent with it's usage of the Interface suffix does not help. But it is what it is.
This works under 6.1:
# CustomPurger.php
use Doctrine\Common\DataFixtures\Purger\ORMPurgerInterface;
use Doctrine\ORM\EntityManagerInterface;
class CustomPurger implements ORMPurgerInterface
{
private EntityManagerInterface $em;
public function setEntityManager(EntityManagerInterface $em) : void
{
$this->em = $em;
}
public function purge() : void
{
dd(' my purger');
}
}
# CustomPurgerFactory.php
use Doctrine\Bundle\FixturesBundle\Purger\PurgerFactory;
use Doctrine\Common\DataFixtures\Purger\PurgerInterface;
use Doctrine\ORM\EntityManagerInterface;
class CustomPurgerFactory implements PurgerFactory
{
public function createForEntityManager(?string $emName, EntityManagerInterface $em, array $excluded = [], bool $purgeWithTruncate = false) : PurgerInterface
{
return new CustomPurger($em);
}
}
# services.yaml
App\Purger\CustomPurgerFactory:
tags:
- { name: 'doctrine.fixtures.purger_factory', alias: 'my_purger' }
bin/console doctrine:fixtures:load --purger=my_purger
> purging database
^ " my purger"
As far as decorating goes, you decorate a service when you want to modify some methods without extending the original class. There is only one method here and it's quite a doozy so I don't think decorating will help.
If you wanted to always use your purger without the --purger option then you could probably point the default purger factory service id to your factory. I'll leave that as an exercise for the reader.
One final note: I took a look at your decorating link. Don't know what they were trying to do but I do know it has nothing to do with decorating.

Dependency injection from service to another service not working symfony 5

I am trying to configure dependency injection for a "Newuser" service. In order not to depend on mysql in the future, what is done is to create a "mysqlService" service that implements an interface with the "persist" method.
From the controller I instantiate the use case "NewUser" that in its constructor by injecting the interface of "DatabaseServiceInterface" and another service "UserPasswordEncoderInterface".
It doesn't work properly since symfony complains because "NewUser doesn't receive anything as parameter" (When the service should be automatically injected).
Files are:
DatabaseServiceInterface:
<?php
namespace App\Application\Infraestructure\DatabaseService;
Interface DatabaseServiceInterface
{
public function persist(Object $ormObject):void;
}
MysqlService:
<?php
namespace App\Application\Infraestructure\DatabaseService;
use Doctrine\ORM\EntityManagerInterface;
class MysqlService implements DatabaseServiceInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function persist(Object $ormObject):void{
$this->entityManager->persist($ormObject);
$this->entityManager->flush();
}
}
RegistrationController:
<?php
namespace App\Controller;
use App\Application\AppUseCases\User\NewUser\NewUserRequest;
use App\Application\Domain\User\User;
use App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface;
use App\Application\Infraestructure\DatabaseService\MysqlService;
use App\Form\RegistrationFormType;
use App\Application\Infraestructure\User\UserAuthenticator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\
UserPasswordEncoderInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use App\Application\AppUseCases\User\NewUser\NewUser;
class RegistrationController extends AbstractController
{
/**
* #Route("/register", name="app_register")
*/
public function register(Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler, UserAuthenticator
$authenticator): Response
{
$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$newUserRequest = new NewUserRequest();
$newUserRequest->email = $form->get('email')->getData();
$newUserRequest->user = $user;
$newUserRequest->password = $form->get('plainPassword')-
>getData();
$newUser = new NewUser();
$newUser->execute($newUserRequest);
// do anything else you need here, like send an email
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
return $this->render('registration/register.html.twig', [
'registrationForm' => $form->createView(),
]);
}
}
Usecas NewUser
<?php
namespace App\Application\AppUseCases\User\NewUser;
use App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface;
use Symfony\Component\Security\Core\Encoder\
UserPasswordEncoderInterface;
class NewUser {
private $databaseService;
private $passwordEncoder;
public function __construct(
DatabaseServiceInterface $databaseService,
UserPasswordEncoderInterface $passwordEncoder
) {
$this->databaseService = $databaseService;
$this->passwordEncoder = $passwordEncoder;
}
public function execute(NewUserRequest $userRegisterRequest) {
//Encode the plain password
$userRegisterRequest->user->setPassword(
$this->passwordEncoder->encodePassword(
$userRegisterRequest->user,
$userRegisterRequest->password
)
);
$userRegisterRequest->user->setEmail($userRegisterRequest->email);
$userRegisterRequest->user->setRoles(array_unique(['ROLE_USER']));
//crear servicio para mysql
$this->databaseService->persist($userRegisterRequest->user);
}
}
Services.yaml
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where
the app is deployed
#https://symfony.com/doc/current/best_practices/
configuration.html#application-related-configuration
parameters:
locale: en
availableLocales:
- es
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your
services.
autoconfigure: true # Automatically registers your services as
commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified
class name
App\:
resource: '../src/*'
exclude:
'../src/{DependencyInjection,Entity,
Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller
#class
App\Application\Infraestructure\DatabaseService\
DatabaseServiceInterface:
App\Application\Infraestructure\DatabaseService
Although symfony does not throw any errors because it seems that the configuration is fine it still does not work. The error it throws when executing the use case is the following:
Too few arguments to function App\Application\AppUseCases\User\NewUser\NewUser::__construct(), 0 passed in /var/www/symfony/src/Controller/RegistrationController.php on line 33 and exactly 2 expected
You are not retrieving the NewUser class from the container, but instancing it manually, so Dependency Resolution is not happening and the service is not reciving any of its dependencies. You should inject the service into the controller for dependency resolution to occur, or pass the arguments explicitly when instancing it.
public function register(Request $request,
UserPasswordEncoderInterface $passwordEncoder,
GuardAuthenticatorHandler $guardHandler,
UserAuthenticator $authenticator,
NewUser $newUser): Response
{
//...
$newUserRequest = new NewUserRequest();
//...
// $newUser = new NewUser(); // Not passing The Database or PasswordEncoder dep
$newUser->execute($newUserRequest);
//...
}

Symfony 2.8 Services issue

Since the last 4 hours I'm trying to understand the logic of Symfony 2 services and how they integrate in the application...
Basically I'm trying to set my EntityManager via a service and use it in a controller
I have the following structure
Bundle1/Controller/Bundle1Controller.php
Bundle1/Services/EntityService.php
Bundle2/Controller/Bundle2Controller.php
Bundle3/Controller/Bundle3Controller.php
....
I'm trying to make a REST API with different entry points, that's why I use multiple bundles bundle2,bundle3....
The logic is the following:
A POST is fired to Bundle2/Controller/Bundle2Controller.php
Bundle2Controller.php instances a new() Bundle1Controller.php
Inside Bundle1Controller I want to access a service entity_service in order to get my EntityManager
I have 2 cases in which I manage to land...
In Bundle1/Controller/Bundle1Controller if I try $this->container or $this->get('entity_service') I get a null everytime
If I set the container in Bundle2/Controller/Bundle2Controller and try $this->get('entity_service') I get You have requested a non-existent service "entity_service"
I will place all the code below
Bundle1/Controller/Bundle1Controller
<?php
namespace Bundle1\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use EntityBundle\Entity\TestEntity;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
class Bundle1Controller extends Controller
{
/**
* #param $response
* #return array
*/
public function verifyWebHookRespone($response){
$em = $this->get('entity_service')->getEm();
$array = json_decode($response);
$mapping = $em->getRepository('EntityBundle:TestEntity')
->findBy(["phone" => $array['usernumber']]);
return $mapping;
}
}
Bundle2/Controller/Bundle2Controller.php
<?php
namespace Bundle2\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Bundle1\Controller\Bundle1Controller;
class Bundle2Controller extends Controller
{
public function webhookAction(Request $request)
{
$data = $request->request->get('messages');
$model = new Bundle1Controller();
$responseMessage = $model->verifyWebHookRespone($data);
return new Response($responseMessage, Response::HTTP_CREATED, ['X-My-Header' => 'My Value']);
}
}
Bundle1/Services/EntityService.php
<?php
namespace EntityBundle\Services;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\Container;
class EntityService
{
protected $em;
private $container;
public function __construct(EntityManager $entityManager, Container $container)
{
$this->em = $entityManager;
$this->container = $container;
}
/**
* #return EntityManager
*/
public function getEm()
{
return $this->em;
}
}
services.yml
services:
entity_service:
class: Bundle1\Services\EntityService
arguments: [ "#doctrine.orm.entity_manager" , "#service_container" ]
Can anyone please help me with something regarding this issue?
How can I register a service and call it from anywhere no matter the bundle or another service?
You should check where your services.yml is located and whether it is imported in the config.yml
You can't just instantiate a controller and expect it to work, you need to set the container.
But you can call EntityManager without needing any other service by using;
$this->get('doctrine.orm.entity_manager');
I can't understand your structure or what you are trying to achieve, but those are the options to go about if you want to keep this structure.

Symfony 3 - Outsourcing Controller Code into Service Layer

I am very new in Symfony 3 and I want to avoid
the business logic in my controllers.
What I have done so far is this:
<?php
namespace RestBundle\Controller;
use RestBundle\Entity\Attribute;
use RestBundle\Entity\DistributorProduct;
use RestBundle\Entity\AttributeValue;
use RestBundle\Entity\ProductToImage;
use Symfony\Component\HttpFoundation\Request;
use RestBundle\Entity\Product;
use FOS\RestBundle\Controller\FOSRestController;
/**
* Product controller.
*
*/
class ProductController extends FOSRestController
{
/**
* Creates a new Product entity.
*
*/
public function createProductAction(Request $request)
{
// Doctrine Manager
$em = $this->getDoctrine()->getManager();
// todo: get the logged in distributor object
$distributor = $em->getRepository('RestBundle:Distributor')->find(1);
// Main Product
$product = new Product();
$product->setEan($request->get('ean'));
$product->setAsin($request->get('asin'));
$em->persist($product);
// New Distributor Product
$distributorProduct = new DistributorProduct();
$distributorProduct->setDTitle($request->get('title'));
$distributorProduct->setDDescription($request->get('description'));
$distributorProduct->setDPrice($request->get('price'));
$distributorProduct->setDProductId($request->get('product_id'));
$distributorProduct->setDStock($request->get('stock'));
// Relate this distributorProduct to the distributor
$distributorProduct->setDistributor($distributor);
// Relate this distributorProduct to the product
$distributorProduct->setProduct($product);
$em->persist($distributorProduct);
// Save it
$em->flush();
$response = $em->getRepository('RestBundle:Product')->find($product->getUuid());
return array('product' => $response);
}
}
}
I know that this is not good code because all the business logic is in the controller.
But how and where can I put this code (set requests into model, persist and flush with doctrine, etc) into a service or use dependency injection for it? Or is service for this purpose not the right way?
I know this page and tutorial http://symfony.com/doc/current/best_practices/business-logic.html
but it is not clearify for me where to put CRUD Actions.
Do ONE service for save a whole project with all the related entities? And use the Symfony\Component\HttpFoundation\Request; in a service? So put the whole controller code where I get the request and assign to the models into a service?
Thanks
UPDATE 2: I've extended this answer in a post. Be sure to check it!
UPDATE: use Symfony 3.3 (May 2017) with PSR-4 service autodiscovery and PHP 7.1 types.
I will show you how I lecture controller repository decoupling in companies.
There are 2 simple rules:
there are no signs about Doctrine in controller
there is no new in the controller (static, non-DI approach) (there is now also Sniff for that)
Let's apply this to your controller
Note: this is pseudo code, I haven't tried that, but the logic should be easy to understand. If this is too many change, just check the steps 3 and 4.
We decouple create and save process. For both entities.
This will lead us to 4 services:
# app/config/services.yml
services:
_defaults:
autowire: true
App\Domain\:
resource: ../../App/Domain
App\Repository\:
resource: ../../App/Repository
1. Product Factory to decouple create process
// ProductFactory.php
namespace App\Domain\Product;
final class ProductFactory
{
public function createFromRequest(Request $request): Product
{
$product = new Product();
$product->setEan($request->get('ean'));
$product->setAsin($request->get('asin'));
return $product;
}
}
2. Distributor Product Factory to decouple create process
// DistributorProductFactory.php
namespace App\Domain\Product;
final class DistributorProductFactory
{
public function createFromRequestProductAndDistributor(
Request $request,
Product $product,
Distributor $distributor
): DistributorProduct {
$distributorProduct = new DistributorProduct();
$distributorProduct->setDTitle($request->get('title'));
$distributorProduct->setDDescription($request->get('description'));
$distributorProduct->setDPrice($request->get('price'));
$distributorProduct->setDProductId($request->get('product_id'));
$distributorProduct->setDStock($request->get('stock'));
// Relate this distributorProduct to the product
$distributorProduct->setProduct($product);
// Relate this distributorProduct to the product
$distributorProduct->setDistributor($distributor);
return $distributorProduct;
}
}
3. Create own ProductRepository service
// ProductRepository.php
namespace App\Repository;
use RestBundle\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
final class ProductRepository
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
public funtion __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function save(Product $product): void
{
$this->entityManager->persist($product);
$this->entityManager->flush();
}
}
4. Create own DistributorProductRepository service
// DistributorProductRepository.php
namespace App\Repository;
use RestBundle\Entity\DistributorProduct;
use Doctrine\ORM\EntityManagerInterface;
final class DistributorProductRepository
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
public funtion __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public function save(DistributorProduct $distributorProduct): void
{
$this->entityManager->persist($distributorProduct);
$this->entityManager->flush();
}
}
5. And we finish with nice and thin controller!
namespace RestBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\FOSRestController;
final class ProductController extends FOSRestController
{
// get here dependencies via constructor
public function createProductAction(Request $request): array
{
// todo: get the logged in distributor object
$distributor = $em->getRepository('RestBundle:Distributor')->find(1);
$product = $this->productFactory->createFromRequest($request);
$distributorProduct = $this->distributorProductFactory->createFromRequestProductAndDistributor(
$request,
$product,
$distributor
);
$this->productRepository->save($product);
$this->distributorProductRepository->save($product);
return [
'product' => $product
];
}
}
That's all!

How to add an entity-specific listener in Symfony2 that has access to container

In a Symfony2 application, I have an entity that needs to be populated on pre-persist with various context properties (like user id, what page it was called from, etc.)
I figured that to do this, I need to add a doctrine event listener that has access to "service_container", and the best way to give such access is to pass "service_container" as an argument to this listener.
I have a specific entity that I want to listen to, and I do not want to trigger the listener to events with any other entity.
We can add an entity-specific listener, documentation is found here:
http://docs.doctrine-project.org/en/latest/reference/events.html#entity-listeners
- but this does not provide example of how to pass an argument (I use PHP annotations to declare the listener).
I also tried to use JMSDiExtraBundle annotations, like in the example below:
http://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations#doctrinelistener-or-doctrinemongodblistener
- but this way requires to declare the listener as non-entity-specific
Is there any way to make a listener for one entity only, and have it have access to container?
One of the ways similar to doctrine docs through dependency injection:
<?php
namespace AppBundle\EntityListener;
use AppBundle\Entity\User;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Psr\Log\LoggerInterface;
use Symfony\Component\Routing\RouterInterface;
class UserListener {
/**
* #var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function postPersist(User $user, LifecycleEventArgs $args)
{
$logger = $this->logger;
$logger->info('Event triggered');
//Do something
}
}
services:
user.listener:
class: AppBundle\EntityListener\UserListener
arguments: [#logger]
tags:
- { name: doctrine.orm.entity_listener }
And dont forget add listener to entity mapping:
AppBundle\Entity\User:
type: entity
table: null
repositoryClass: AppBundle\Entity\UserRepository
entityListeners:
AppBundle\EntityListener\UserListener: ~
I would simply check entity type from the event. If you check type inside or outside the subscriber, it has the same performance cost. And simple type condition is fast enough.
namespace App\Modules\CoreModule\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
class SetCountryToTaxSubscriber implements EventSubscriber
{
/**
* {#inheritdoc}
*/
public function getSubscribedEvents()
{
return [Events::prePersist];
}
public function prePersist(LifecycleEventArgs $lifecycleEventArgs)
{
$entity = $lifecycleEventArgs->getEntity();
if ( ! $entity instanceof Tax) {
return;
}
$entity->setCountry('myCountry');
}
}

Categories