Symfony 3.4 autowire service - symfony

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.

Related

Inject service to EventSubscriber

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

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'

Symfony3 Use Entity Manager in Custom Container

I want to create HelperController for my project. I generate a controller with doctrine:generate:controller and I need to use entity manager in it.
I enjected to services.yml but it is giving an error like this:
Argument 1 passed to CampingBundle\Controller\HelperController::__construct() must be an instance of Doctrine\ORM\EntityManager, none given ...
My Controller Code :
namespace CampingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Doctrine\ORM\EntityManager;
class HelperController extends Controller
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
My Services.yml :
services:
camping.helper_controller:
class: CampingBundle\Controller\HelperController
arguments: ["#doctrine.orm.entity_manager"]
Why it doesn't work ? Shoudl I clear cache or something else or is there anything wrong in definition ?
Thanks
Try to use EntityManagerInterface and remove extends Controller.
Check this link if you need CAS (Controllers as Services).
Change protected $manager; to private $manager;
namespace CampingBundle\Controller;
use Doctrine\ORM\EntityManagerInterface;
class HelperController
{
/**
* #var EntityManagerInterface $entityManager
*/
private $entityManager;
/**
* #param $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
}
I'll leave my two cents here as I had same issue and fixed it by adding tags to service.
something.validate.some_service:
class: Path\To\Some\Validator
arguments:
- '#doctrine.orm.entity_manager'
tags:
- { name: validator.constraint_validator, alias: some_validator_alias }
How to Work with Service Tags by Symfony

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.

Fosuserbundle override event listener

I'm trying to override the LastLoginListener to add functionality to it.
I;m trying to do it as described here
It seems
In AppBundle\DependencyInjection\OverrideServiceCompilerPass.php
<?php
namespace AppBundle\DependencyInjection\Compiler;
use AppBundle\EventListener\LastLoginListener;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class OverrideServiceCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('"fos_user.security.interactive_login_listener');
$definition->setClass(LastLoginListener::class);
}
services.yml
services:
app.login_listener:
class: AppBundle\EventListener\LastLoginListener
arguments: []
tags:
- { name: kernel.event_subscriber }
The listener itself is copied from the bundle.
The autoloader expected class "AppBundle\DependencyInjection\OverrideServiceCompilerPass" to be defined in file "/vendor/composer/../../src/AppBundle/DependencyInjection/OverrideServiceCompilerPass.php". The file was found but the class was not in it, the class name or namespace probably has a typo.
in DebugClassLoader.php (line 261)
My goal is to add the ip address of the last login with the listener, but I'll need to create another to add a role and a registration date
I'm trying to do it "the right way" instead of doing something hackish
Its much better to use success_handler and failure_handler services.
# app/config/security.yml
firewalls:
main:
...
form_login:
...
success_handler: authentication_success_handler
failure_handler: authentication_failure_handler
Next you need to register your services and add arguments that fit your needs (probably #router and #doctrine.orm.entity_manager)
# app/config/services.yml
authentication_success_handler:
class: AppBundle\Handler\AuthenticationSuccessHandler
arguments: ['#router', '#doctrine.orm.entity_manager']
authentication_failure_handler:
class: AppBundle\Handler\AuthenticationFailureHandler
arguments: ['#router', '#doctrine.orm.entity_manager']
Then you need to create your services
// src/AppBundle/Handler/AuthenticationSuccessHandler.php
<?php
namespace AppBundle\Handler;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Router;
use Doctrine\Common\Persistence\ObjectManager;
class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface {
protected $router;
private $em;
public function __construct(Router $router, ObjectManager $em) {
$this->router = $router;
$this->em = $om;
}
public function onAuthenticationSuccess(Request $request, AuthenticationException $exception) {
// your code here - creating new object. redirects etc.
}
}
and
// src/AppBundle/Handler/AuthenticationFailureHandler.php
<?php
namespace AppBundle\Handler;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Router;
use Doctrine\Common\Persistence\ObjectManager;
class AuthenticationFailureHandler implements AuthenticationFailureHandlerInterface {
protected $router;
private $em;
public function __construct(Router $router, ObjectManager $em) {
$this->router = $router;
$this->em = $om;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
// your code here - creating new object. redirects etc.
}
}
If you want to hook into another FOSUserBundle Controller use this

Resources