Inject service to EventSubscriber - symfony

I have problem with my code. I have EventSubscriber:
<?php
namespace App\EventSubscriber;
use App\Service\PostService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class NewPostSubscriber implements EventSubscriberInterface
{
/**
* #var PostService $postService
*/
private $postService;
/**
* #param PostService $postService
*/
public function constructor($postService)
{
$this->postService = $postService;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::REQUEST => 'postNotification'
];
}
public function postNotification(RequestEvent $event)
{
$request = $event->getRequest();
if ('app_post_createpost' != $request->get('_route')) {
return;
}
$post = $this->postService->deletePost(3);
}
}
and i inject for him PostService, but when i send request to post endpoint then i receive exception witj code 500 and text
Call to a member function deletePost() on null
In my services.yaml i have
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
app.post_service:
class: App\Service\PostService
arguments: ['#doctrine.orm.entity_manager']
autowire: true
app.new_post_subscriber:
class: App\EventSubscriber\NewPostSubscriber
arguments: ['#app.post_service']
autowire: false
tags:
- { name: kernel.event_subscriber }
I have method deletePost in my PostService. I don't know what is wrong, i have service and deletePost method, service is inject to subscriber but still not working.

In symfony 4 you don't need declare your services in services.yaml. You can do like this
private $postService;
/**
* #param PostService $postService
*/
public function __construct(PostService $postService)
{
$this->postService = $postService;
}
And in your PostService
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
Also you have wrong name of your constructor constructor must be __construct

Related

Cannot autowire service in Symfony 3.4 and FosUserBundle

I try to override REGISTRATION_SUCCESS in FosUserBundle to redirect the admin on user's list after register a new user.
So I have created a new event subscriber :
<?php
namespace AppBundle\EventListener;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Mailer\MailerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class RedirectAfterRegistrationSubscriber implements EventSubscriberInterface
{
private $mailer;
private $tokenGenerator;
private $router;
public function __construct(MailerInterface $mailer, TokenGeneratorInterface $tokenGenerator, UrlGeneratorInterface $router)
{
$this->mailer = $mailer;
$this->tokenGenerator = $tokenGenerator;
$this->router = $router;
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$user->setEnabled(false);
if (null === $user->getConfirmationToken()) {
$user->setConfirmationToken($this->tokenGenerator->generateToken());
}
$this->mailer->sendConfirmationEmailMessage($user);
$url = $this->router->generate('user_index');
$event->setResponse(new RedirectResponse($url));
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess'
];
}
}
and the following service :
app.redirect_after_registration_subscriber:
class: AppBundle\EventListener\RedirectAfterRegistrationSubscriber
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
I don't understand why this error appears :
Cannot autowire service "AppBundle\EventListener\RedirectAfterRegistrationSubscriber":
argument "$mailer" of method "__construct()" references interface
"FOS\UserBundle\Mailer\MailerInterface" but no such service exists. You should maybe alias
this interface to one of these existing services: "fos_user.mailer.default",
"fos_user.mailer.twig_swift", "fos_user.mailer.noop".
I suppose you are using autodiscovering of services. Something like:
# services.yaml
AppBundle\:
resource: '../src/'
...
So in addition to the #app.redirect_after_registration_subscriber that you define, Symfony defines another service with id #AppBundle\EventListener\RedirectAfterRegistrationSubscriber. Both point to AppBundle\EventListener\RedirectAfterRegistrationSubscriber class. Yet you configured the mailer parameter only for the first one.
The solution:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments: ['#fos_user.mailer', '#fos_user.util.token_generator', '#router']
tags:
- { name: kernel.event_subscriber }
With autowiring and autoconfigure you can even sypmlify to:
AppBundle\EventListener\RedirectAfterRegistrationSubscriber:
arguments:
$mailer: '#fos_user.mailer'

Cannot autowire service in symfony

I'm trying to split one big service.yaml to few smaller files. In origin service.yaml I had
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App\Domain\Country\Infrastructure\Repository\CountryRepository:
public: true
class: App\Domain\Country\Infrastructure\Repository\CountryRepository
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments: [App\Domain\Country\Entity\Country]
Then I added import at the begining service.yam
imports:
- {resource: services/repositories.yaml}
repositories.yaml
services:
_defaults:
autowire: true
autoconfigure: true
public: true
App\Domain\Country\Infrastructure\Repository\CountryRepository:
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments: [App\Domain\Country\Entity\Country]
After that I started to get error
Cannot autowire service "App\Domain\Country\Infrastructure\Repository\Count
ryRepository": argument "$class" of method "Doctrine\ORM\EntityRepository::
__construct()" references class "Doctrine\ORM\Mapping\ClassMetadata" but no
such service exists.
What's wrong there?
https://pastebin.com/Uy85YJmc (service.yaml)
https://pastebin.com/ZesAw3sD (repositories.yaml)
You should not need to define the repository for the purposes of autowiring.
services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
Entity\Country:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\CountryRepository")
*/
class Country
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
}
Repository\CountryRepository:
<?php
namespace App\Repository;
use App\Entity\Country;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
/**
* #method Country|null find($id, $lockMode = null, $lockVersion = null)
* #method Country|null findOneBy(array $criteria, array $orderBy = null)
* #method Country[] findAll()
* #method Country[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CountryRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Country::class);
}
}
And finally, your service:
<?php
namespace App\Service;
use App\Repository\CountryRepository;
class ExampleService
{
/**
* #var CountryRepository
*/
private $repository;
/**
* #param CountryRepository $repository
*/
public function __construct(CountryRepository $repository)
{
$this->repository = $repository;
}
}
The autowiring will see that you've injected that CountryRepository into the ExampleService constructor and handle the rest.
Use named arguments instead :
repositories.yaml
services:
App\Domain\Country\Infrastructure\Repository\CountryRepository:
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments:
$class: '#App\Domain\Country\Entity\Country'

Symfony 3.4 autowire service

Am developing a mini app in Symfony 3.4. Am putting together an authentication process using Guard. I have created a class called LoginFormAuthenticator which extends AbstractFormLoginAuthenticator.
Receiving error:
Cannot autowire service "app.security.login_form_authenticator": argument "$em" of method
"AppBundle\Security\LoginFormAuthenticator::__construct()" references
class "Doctrine\ORM\EntityManager" but no such service exists. Try
changing the type-hint to one of its parents: interface
"Doctrine\ORM\EntityManagerInterface", or interface
"Doctrine\Common\Persistence\ObjectManager".
My code in my form authenticating class:
<?php
namespace AppBundle\Security;
use AppBundle\Form\LoginForm;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
private $formFactory;
private $em;
private $router;
public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router)
{
$this->formFactory = $formFactory;
$this->em = $em;
$this->router = $router;
}
public function getCredentials(Request $request)
{
$isLoginSubmit = $request->getPathInfo() == '/login' && $request->isMethod('POST');
if(!$isLoginSubmit){
return false;
}
$form = $this->formFactory->create(LoginForm::class);
$form->handleRequest($request);
$data = $form->getData();
return $data;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['_username'];
return $this->em->getRepository('AppBundle:User')
->findOneBy(['email' => $username]);
}
public function checkCredentials($credentials, UserInterface $user)
{
$password = $credentials['_password'];
if($password == 'iliketurtles'){
return true;
}
return false;
}
protected function getLoginUrl()
{
return $this->router->generate('security_login');
}
}
My services.yml:
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
# add more services, or override services that need manual wiring
# AppBundle\Service\ExampleService:
# arguments:
# $someArgument: 'some_value'
app.security.login_form_authenticator:
class: AppBundle\Security\LoginFormAuthenticator
autowire: true
Am a complete novice in Symfony so apologies if I'm missing something obvious.
As noted by #Cerad in the comments, you should change EntityManager to EntityManagerInterface in your constructor.
Change the line
use Doctrine\ORM\EntityManager;
to
use Doctrine\ORM\EntityManagerInterface;
And also change the line
public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router)
to
public function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $em, RouterInterface $router)
The Doctrine\Common\Persistence\ObjectManager interface is no longer aliased to the doctrine.orm.entity_manager service, use Doctrine\ORM\EntityManagerInterface instead.

Subscribtion to FOSUserEvents::REGISTRATION_COMPLETED is running twice

In symfony 3 I have subscribed to
FOSUserEvents::REGISTRATION_COMPLETED
to fill a second table with address data and writing a log.
namespace AppBundle\EventListener;
use AppBundle\Entity\Address;
use Doctrine\ORM\EntityManager;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FilterUserResponseEvent;
class RegistrationSubscriber implements EventSubscriberInterface
{
private $em;
private $container;
public function __construct(EntityManager $em,ContainerInterface $container)
{
$this->em = $em;
$this->container = $container;
}
public static function getSubscribedEvents()
{
return [
FOSUserEvents::REGISTRATION_COMPLETED => [
['onRegistrationCompleted',0]
],
];
}
public function onRegistrationCompleted(FilterUserResponseEvent $event)
{
$user = $event->getUser();
$address = new Address();
$address->addAddress($user);
$this->em->persist($address);
$this->em->flush();
$this->container->get('app.logging')->write('Anmeldung',array('customer'=>$user->getId()));
}
}
services.yml:
app.registration_listener:
class: 'AppBundle\EventListener\RegistrationSubscriber'
arguments: ['#doctrine.orm.entity_manager']
tags:
- { name: kernel.event_subscriber}
app.logging:
class: AppBundle\Util\LogHandler
arguments: ['#doctrine.orm.entity_manager']
public: true
After submitting the registration form, FOSUserEvents::REGISTRATION_COMPLETED is triggered, but it is running twice, because address data and log data are written twice.
What can be the reason ?
I found it by myself.
In services.yml of symfony 3.3 I used the option
autoconfigure: true,
so services and event subscribers are loaded automatically.
That is why the event was triggered twice.

Symfony 3.1 Listener with access to database

I have a config table with a 'web_enabled' key, when true I want to show the route requested but when false I want to show a 'Site in maintenance' page. Obviously this check has to be performed before any route action.
I have been reading about Events and Listeners but I don't see how to implement the access to the doctrine and template.
Thanks for the help.
This is the solution I implemented finally, differs from the proposed by Alsatian because I don't use a parameter in the service.yml. Is just a question of taste, nothing else.
in app/config/services.yml
services:
app.request_listener:
class: AppBundle\EventListener\RequestListener
arguments: ["#doctrine.orm.entity_manager","#templating"]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelController }
in src/AppBundle/EventListener/RequestListener.php
namespace AppBundle\EventListener;
use \Symfony\Component\HttpKernel\Event\GetResponseEvent;
use \Symfony\Component\HttpFoundation\Response;
class RequestListener
{
private $em;
private $templating;
public function __construct($em, $templating)
{
$this->em = $em;
$this->templating = $templating;
}
public function onKernelController(GetResponseEvent $event)
{
if ( !$this->configKey = $this->em->getRepository('AppBundle:Config')->getconfig('web_enabled') )
$event->setResponse($this->templating->renderResponse('default/construction.html.twig'));
}
}
and in src/AppBundle/Repository/ConfigRepository.php explaining the getconfig method:
namespace AppBundle\Repository;
class ConfigRepository extends \Doctrine\ORM\EntityRepository
{
public function getconfig( $config_name )
{
$config = $this->getEntityManager()
->createQuery('SELECT p.config_value FROM AppBundle:Config p WHERE p.config_name = :config_name')
->setParameter('config_name', $config_name)
->getResult();
if (sizeof($config)){
return $config[0]['config_value'];
}else{
return false;
}
}
}
Hope this helps.
You have just to inject both EntityManager and Templating in your listener :
Definition as service :
# src/AppBundle/Ressources/config/services.yml
services:
app.request_listener:
class: AppBundle\EventListener\RequestListener
arguments: ["%web_enabled%","#doctrine.orm.entity_manager","#templating"]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Listener :
namespace AppBundle\EventListener;
use \Symfony\Component\HttpKernel\Event\GetResponseEvent;
use \Symfony\Component\HttpFoundation\Response;
class RequestListener
{
private $configKey;
private $em;
private $templating;
public __construct($configKey, $em, $templating)
{
$this->configKey = $configKey;
$this->em = $em;
$this->templating = $templating;
}
public function onKernelRequest(GetResponseEvent $event)
{
if(!$this->configKey){
$var = $this->em->getRepository('AppBundle:MyEntity')->findOne(1);
$event->setResponse($this->templating->renderResponse('my_template.html.twig',array('var'=>$var));
}
}
}

Resources