How can i get entityManager inside Subscriber in Symfony - php

I use Api Platform. I have subscriber
namespace App\EventSubscriber\Api;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\Product;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;
final class ProductCreateSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['createHost', EventPriorities::POST_WRITE],
];
}
public function createHost(GetResponseForControllerResultEvent $event)
{
$product = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$product instanceof Product || Request::METHOD_POST !== $method) {
return;
}
I NEED ENTITY MANAGER HERE
}
}
Is it possible to get a entity manager here?
I need create another entity, after creating Product

Symfony allow (and recommend) to inject dependencies in services.
We add a constructor to the subscriber in order to inject Doctrine and make it accessible though $this->entityManager:
use Doctrine\ORM\EntityManagerInterface;
final class ProductCreateSubscriber implements EventSubscriberInterface
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
public function __construct(
EntityManagerInterface $entityManager
) {
$this->entityManager = $entityManager;
}
public function createHost(GetResponseForControllerResultEvent $event)
{
$product = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$product instanceof Product || Request::METHOD_POST !== $method) {
return;
}
// You can access to the entity manager
$this->entityManager->persist($myObject);
$this->entityManager->flush();
}
If autowiring is enabled, you'll have nothing else to do, the service will be instantiated automatically.
If not, you'll have to declare the service:
App\EventSubscriber\Api\ProductCreateSubscriber:
arguments:
- '#doctrine.orm.entity_manager'

Related

Doctrine inserting on POST_WRITE event with Symfony 5 and APIplatform

I want to add a new UserTeam(which get a team, a user and a role) each time a User create a Team. I created an event subscriber TeamFirstUserAdminSubscriber.php but it doesn't work and I have no error message.
here is my database model:
and here is the file TeamFirstUserAdminSubscriber.php
<?php
namespace App\Events;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\Team;
use App\Entity\UserTeam;
use App\Repository\RoleUserTeamRepository;
use App\Repository\TeamRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Security;
class TeamFirstUserAdminSubscriber implements EventSubscriberInterface
{
private $security;
private $repository;
private $repositoryT;
private $manager;
public function __construct(Security $security, RoleUserTeamRepository $repository, TeamRepository $repositoryT, EntityManagerInterface $manager)
{
$this->security = $security;
$this->repository = $repository;
$this->repositoryT = $repositoryT;
$this->manager = $manager;
}
public static function getSubscribedEvents()
{
return[
KernelEvents::VIEW => ['setInstanceUserTeam', EventPriorities::POST_WRITE],
];
}
public function setInstanceUserTeam(ViewEvent $event)
{
$team = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if($team instanceof Team && $method === 'POST')
{
//get the user connected
$user = $this->security->getUser();
//get the last team created
$lastTeam = $this->repositoryT->findLastTeamCreated();
//get the admin role (NOT the symfony one)
$admin = $this->repository->findOneByRoleAdmin('Admin');
//should create a new UserTeam instance with the User, the Team and the RoleUserTeam wanted
$userTeam = new UserTeam();
$userTeam->setUser($user);
$userTeam->setTeam($lastTeam);
$userTeam->setRoleUserTeam($admin);
$manager = $this->manager;
$manager->persist($userTeam);
$manager->flush();
}
}
The new UserTeam is not created in the databse when I try it out with postman, but the Team is well created.
I think I am missing something but I don't know what.
Could anyone help me ?
I can't see your full code(controller, forms etc), but you can achieve this without an event listener. As you already know, what team to be assigned for a user, why don't you assign that on the entity when creating the user object. For example if you using form in the controller
public function addAdminUser(Request $request, RoleUserTeamRepository $repository, TeamRepository $repositoryT, EntityManagerInterface $manager)
{
$user = new User();
$form = $this->createForm(UserForm::class, $user);
$form->handleRequest($form);
 
if ($form->isSubmitted && $form->isValid()) {
//get the last team created
$lastTeam = $this->repositoryT->findLastTeamCreated();
//get the admin role (NOT the symfony one)
$admin = $this->repository->findOneByRoleAdmin('Admin');

//should create a new UserTeam instance with the User, the Team and the
RoleUserTeam wanted
$userTeam = new UserTeam();
$userTeam->setUser($user);
$userTeam->setTeam($lastTeam);
$userTeam->setRoleUserTeam($admin);
$user->setUserTeam($userTeam);

//persist the user
$manager->persist($user);
$manager->flush();
}
}
If you want to use events I would suggest use Doctrine events. You can use prePersist event on user entity to achieve same result. Doctrine Events
I solved my problem, I needed a cascade persist in my entities. Here, if it can hepl some people :
I had this in the Team entity
public function __construct()
{
$this->id = Team::class;
$this->userTeams = new ArrayCollection();
$this->categories = new ArrayCollection();
}
public function userTeam()
{
$newUserTeam = new UserTeam();
$newUserTeam->setTeam($this);
$this->userTeams->add($newUserTeam);
}
UserTeam entity, I had to add cascade={"persist"} in the relation ManyToOne:
class UserTeam
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity=Team::class, inversedBy="userTeams", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
* #Groups({"users_read", "userTeam_read"})
*/
private $team;
And the TeamByUserSubscriber that I modified :
<?php
namespace App\Events;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\Team;
use App\Entity\UserTeam;
use App\Repository\RoleUserTeamRepository;
use App\Repository\TeamRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Security;
class TeamByUserSubscriber implements EventSubscriberInterface
{
private $security;
private $repository;
private $entityManager;
public function __construct(Security $security, RoleUserTeamRepository $repository, EntityManagerInterface $entityManager)
{
$this->security = $security;
$this->repository = $repository;
$this->entityManager = $entityManager;
}
public static function getSubscribedEvents()
{
return[
KernelEvents::VIEW => ['setUserForTeam', EventPriorities::PRE_VALIDATE],
];
}
public function setUserForTeam(ViewEvent $event)
{
$team = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if($team instanceof Team && $method === 'POST')
{
//get the connected User
$user = $this->security->getUser();
//put the current User to the team we are creating (as the creator of the team)
$team->setUser($user);
//get the Admin role from the RoleUserTeam entity
$admin = $this->repository->findOneByRoleAdmin('Admin');
//create a new instance of UserTeam with the connected User, the Team we are creating and the role
$userTeam = new UserTeam();
$userTeam->setUser($user);
$userTeam->setTeam($team);
$userTeam->setRoleUserTeam($admin);
$manager = $this->entityManager;
$manager->persist($userTeam);
$manager->flush();
}
}

Symfony 4 PHP Exception Doctrine\ORM\ORMException: "Unknown Entity namespace ali as 'User'."

Here is my subscriber class. I want to get the user email to him an email.
I use here the EntityManagerInterface
use Doctrine\ORM\EntityManagerInterface;
final class RegisterMailSubscriber implements EventSubscriberInterface
{
private $mailer;
public function __construct(\Swift_Mailer $mailer, EntityManagerInterface $entityManager)
{
$this->mailer = $mailer;
$this->repository= $entityManager->getRepository('AppEntity:User');
}
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE],
];
}
public function sendMail(ViewEvent $event): void
{
$user = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$user instanceof User || Request::METHOD_POST !== $method) {
return;
}
$userInfo = $this->repository->find($user->getId());
}
}
You need to import all the dependencies used as User, Request, ViewEvent, KernelEvent, etc.
By the way, is a good practice to import the repository (UserRepository) and not the entityManager, but you dont need it because you have the $user already. You dont need to find it again.
I think this should be enough if you have the user classes in those namespaces (locations):
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use ApiPlatform\Core\EventListener\EventPriorities;
use App\Entity\User;
final class RegisterMailSubscriber implements EventSubscriberInterface
{
private $mailer;
public function __construct(\Swift_Mailer $mailer)
{
$this->mailer = $mailer;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['sendMail', EventPriorities::POST_WRITE],
];
}
public function sendMail(ViewEvent $event): void
{
$user = $event->getControllerResult();
$method = $event->getRequest()->getMethod();
if (!$user instanceof User || Request::METHOD_POST !== $method) {
return;
}
$userEmail = $user->getEmail(); //for example. You got the user 5 lines before.
}
}

Symfony - using twig in onKernelRequest kills session

I have a strange issue in symfony 4.3 (also tested it in 4.2 - same behaviour) - I am using an EventListener to process a request - heres the code:
<?php
namespace App\EventListener;
use App\Entity\Company;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;
class ShopListener implements EventSubscriberInterface
{
/** #var EntityManagerInterface */
protected $em;
/** #var Environment */
protected $twig;
public function __construct(EntityManagerInterface $entityManager, Environment $twig)
{
$this->em=$entityManager;
$this->twig=$twig;
}
public function onKernelRequest(RequestEvent $event)
{
if($event->isMasterRequest()===false) {
return;
}
/** #var Request $request */
$request=$event->getRequest();
$subDomain=$request->attributes->get('domain');
if($subDomain===null) {
return;
}
$company=$this->em->getRepository(Company::class)->findOneBy([
'subDomain' => $subDomain,
]);
if($company instanceof Company && $company->shopIsOnline()) {
$request->attributes->set('company',$company);
return;
}
$event->setResponse(
new Response($this->twig->render('page/shop_not_found.html.twig'),404)
);
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest',0],
];
}
}
After registering that listener, $request->getSession() is always null in my controller (toolbar also notices, that there is no session registered). When deregistering it, the session is there, but the logic in the listener is skipped. I have tried to play around with the priority to ensure, there's no other listener which interferes.
It seems, that already registering that event kills the session (even if onKernelRequest is empty), which is hard to believe.
What am I missing?
Session is created by Symfony\Component\FrameworkBundle\EventListener\SessionListener listener, on kernel.request event too (priority of 128).
This event has a specific behavior: if a listener sets a Response, "the process skips directly to the kernel.response event" to quote the documentation. I would suspect it could causes issues.
Try setting your listener a priority < 0 (I'm getting you tried many), and please checks the order the profiler "Events" section (/_profiler/latest?panel=events).
Found the solution - problem is the injection of the twig-environment in the constructor - without twig everything works as expected. I guess, loading the twig-environment at this stage does something to the session (like loading it too early).
I moved the listener to onKernelController and modified it:
<?php
namespace App\EventListener;
use App\Entity\Company;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;
class ShopListener implements EventSubscriberInterface
{
/** #var EntityManagerInterface */
protected $em;
/** #var Environment */
protected $twig;
public function __construct(EntityManagerInterface $entityManager, Environment $twig)
{
$this->em=$entityManager;
$this->twig=$twig;
}
public function onKernelController(ControllerEvent $controllerEvent)
{
if($controllerEvent->isMasterRequest()===false) {
return;
}
/** #var Request $request */
$request=$controllerEvent->getRequest();
$subDomain = $request->attributes->get('domain');
if($subDomain===null) {
return;
}
$company=$this->em->getRepository(Company::class)->findOneBy([
'subDomain' => $subDomain,
]);
if($company instanceof Company && $company->shopIsOnline()) {
$request->attributes->set('company',$company);
return;
}
$controllerEvent->setController(
function() {
return new Response($this->twig->render('page/shop_not_found.html.twig'),404);
}
);
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::CONTROLLER => ['onKernelController',128],
];
}
}

Symfony EventSubscribe on Entity

trying to make an subscriber for Entity actions (CRUD) and cannot figure it out.
I know there is a way, where I can make listener and send him 3 different events, but that's not what I want to reach, I dont even think is good solution.
Event Subscriber
<?php
namespace App\EventListener;
use App\Entity\Log;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
/**
* Part of program created by David Jungman
* #author David Jungman <davidjungman.web#gmail.com>
*/
class EntitySubscriber implements EventSubscriberInterface
{
/**
* #var EntityManagerInterface
*/
private $em;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage, EntityManagerInterface $em)
{
$this->em = $em;
$this->tokenStorage = $tokenStorage;
}
public static function getSubscribedEvents()
{
return array(
Events::postPersist,
Events::postUpdate,
Events::postRemove,
);
}
public function postUpdate(LifecycleEventArgs $args)
{
$this->logEvent($args, "remove");
}
public function postRemove(LifecycleEventArgs $args)
{
$this->logEvent($args, "remove");
}
public function postPersist(LifecycleEventArgs $args)
{
$this->logEvent($args, "create");
}
private function logEvent(LifecycleEventArgs $args, string $method)
{
$entity = $args->getEntity();
if($entity->getShortName() != "Log")
{
$user = $this->tokenStorage->getToken()->getUser();
$log = new Log();
$log
->setUser($user)
->setAffectedTable($entity->getShortName())
->setAffectedItem($entity->getId())
->setAction($method)
->setCreatedAt();
$this->em->persist($log);
$this->em->flush();
}
}
}
and my Service.yaml part
App\EventListener\EntitySubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }
I have tried:
I've looked into these 2 official tutorials:
-https://symfony.com/doc/current/event_dispatcher.html
-https://symfony.com/doc/current/doctrine/event_listeners_subscribers.html
but neither helped.. when I use shown part of config, my computer freeze.
When I try to debug it, I can see these methods active
( php bin/console debug:event-dispatcher )
but they are listening on "event" event
Doctrine has it's own events handler/subscriber system. However, with the class Symfony\Component\EventDispatcher\EventSubscriberInterface; that you are implementing, that is from the Symfony event system.
<?php
use Doctrine\ORM\Events;
use Doctrine\Common\EventSubscriber; // **the Doctrine Event subscriber interface**
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
class MyEventSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(
Events::postUpdate,
);
}
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();
// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product) {
// do something with the Product
}
}
}

Symfony 3 unit test form with constructor

I am trying to unit test a form which has 2 dependencies (ObjectManager and EventDispatcher)
I had tried to follow official doc but without success.
My testing file:
<?php
namespace Lch\MediaBundle\Tests\Form;
use Lch\MediaBundle\Form\AddImageType;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class AddImageTypeTest extends TypeTestCase
{
private $entityManager;
private $eventDispatcher;
protected function setUp()
{
$this->entityManager = $this->createMock(ObjectManager::class);
$this->eventDispatcher = $this->createMock(EventDispatcher::class);
parent::setUp();
}
protected function getExtensions()
{
$type = new AddImageType($this->entityManager, $this->eventDispatcher);
return array(
new PreloadedExtension(array($type), array()),
);
}
public function testSubmitValidData()
{
$form = $this->factory->create(AddImageType::class);
}
}
I got this error when I execute my test suite:
TypeError: Argument 1 passed to
LCH\MediaBundle\Form\AddImageType::__construct() must implement
interface Doctrine\Common\Persistence\ObjectManager, none given,
called in
/home/matthieu/www/lch/media/src/Lch/MediaBundle/vendor/symfony/symfony/src/Symfony/Component/Form/FormRegistry.php
on line 85
It seems that the job I do in the getExtensions method is not working, but cannot figure it out.
Does anyone have a clue?
ObjectManager is an interface, meaning you can't instantiate or pass it directly to other constructors.
If you are using Doctrine, replace it with Doctrine\ORM\EntityManager which implements ObjectManager interface and can be instantiated, otherwise replace it with your own implementation.
<?php
namespace Lch\MediaBundle\Tests\Form;
use Lch\MediaBundle\Form\AddImageType;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;
class AddImageTypeTest extends TypeTestCase
{
private $entityManager;
private $eventDispatcher;
protected function setUp()
{
$this->entityManager = $this->createMock(EntityManager::class);
$this->eventDispatcher = $this->createMock(EventDispatcher::class);
parent::setUp();
}
protected function getExtensions()
{
$type = new AddImageType($this->entityManager, $this->eventDispatcher);
return array(
new PreloadedExtension(array($type), array()),
);
}
public function testSubmitValidData()
{
$form = $this->factory->create(AddImageType::class);
}
}

Categories