I am using symfony 2.8 and just did a composer udate on my local machine and started getting this kind of errors.
Full error (happens for both for mailer and user manager, that were working smoothly for a year...)
[Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
Unable to replace alias "fos_user.user_manager" with actual definition "my.custom_user_manager".
[Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException]
You have requested a non-existent service "my.custom_user_manager".
config.yml
fos_user:
service:
user_manager: my.custom_user_manager
mailer: my.custom_user_mailer # fos_user.mailer.twig
It happens for a custom user manager of have overriden as well. My FOS user bundle version is v2.0.0-alpha3 and has not changed since precedent version.
My service definitions used to / and look correct.
Any lead here ?
Service definitions:
// bunch of "use" here...
/**
* #DI\Service("my.custom_user_manager", public=true)
*/
class CoreUserManager extends BaseUserManager
{
/**
* #DI\InjectParams({
* "encoderFactory" = #DI\Inject("security.encoder_factory"),
* "usernameCanonicalizer" = #DI\Inject("fos_user.util.username_canonicalizer"),
* "emailCanonicalizer" = #DI\Inject("fos_user.util.email_canonicalizer"),
* "em" = #DI\Inject("doctrine.orm.entity_manager"),
* })
*/
public function __construct(
EncoderFactoryInterface $encoderFactory,
CanonicalizerInterface $usernameCanonicalizer,
CanonicalizerInterface $emailCanonicalizer,
EntityManager $em
)
{
$class = 'Medical\CoreBundle\Entity\User';
parent::__construct($encoderFactory, $usernameCanonicalizer, $emailCanonicalizer, $em, $class);
}
And the mailer
// bunch of proper "use"... here
/**
* Extends FOS mailer to send your own emails and add methods
* #DI\Service("my.custom_user_mailer", public=false)
*/
class MedicalUserMailer extends BaseMailer implements MailerInterface
{
/**
* #todo To move into settings and inject via DI
* $from = $this->getParameter('medical.core.emails')['from']; LIKE in AdminController
*/
private $from = array('support#360medics.fr' => '360 medics');
/**
* #DI\InjectParams({
* "mailer"= #DI\Inject("mailer"),
* "router"= #DI\Inject("router"),
* "templating"= #DI\Inject("templating"),
* "config"= #DI\Inject("%medical.core.emails%")
* })
*/
public function __construct(
\Swift_Mailer $mailer, UrlGeneratorInterface $router, EngineInterface $templating, array $config
)
{
parent::__construct($mailer, $router, $templating, array());
// $this->mailer = $mailer;
// $this->router = $router;
// $this->templating = $templating;
$this->config = $config;
}
Related
I want to recover the date of the last time a user logged in my Symfony 5 website, I created a LoginListener and did the right settings (So-think-I ?) to make it work but in the class Login Listener :
namespace App\Event;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
// Get the User entity.
$user = $event->getAuthenticationToken()->getUser();
// Update your field here.
$user->setLastLogin(new \DateTime());
// Persist the data to database.
$this->em->persist($user);
$this->em->flush();
}
}
The setLastLogin(new \DateTime()) is described as an undefined method. Yet this method is in the entity User.php and it is properly called in the loginlistener. And the "use App\Entity\User :
/**
* {#inheritdoc}
*/
public function setLastLogin(\DateTime $time = null)
{
$this->lastLogin = $time;
return $this;
}
/**
* Gets the last login time.
*
* #return \DateTime|null
*/
public function getLastLogin()
{
return $this->lastLogin;
}
/**
* #var \DateTime $lastLogin
*
* #ORM\Column(type="datetime")
*/
private $lastLogin;
And when I try to make a exit(var_dump($user), nothing appears. Here is my services.yaml :
App\EventListener\LoginListener:
- tags:
- { name: 'kernel.event_listener', event: 'security.interactive_login', entity: 'App\Entity\User' }
Can someone help me please ? Thank you.
I've just upgraded an app from Symfony 4.3.9 to 4.4.2. After that, I had the debug bar not working and showing "An error occurred while loading the web debug toolbar. "
After a long investigation, I found that it's because of an EventListener on security.authentication.failure event that was the cause.
Commenting the onAuthenticationFailure method content did nothing and after some investigation it works when removing the TokenStorageInterface from the tags and constructor... But I need it.
Any ideas?
Here's the code :
services.yaml
App\EventListener\LoginListener:
arguments: ["#doctrine", "#security.token_storage", "#router", "#event_dispatcher"]
tags:
- { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure }
LoginListener.php
<?php
namespace App\EventListener;
use App\Entity\AdminUser;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
/**
* Class LoginListener
* Listens to user log in events (failure, interactive log in) to provide additionnal security measures
*
* #package App\EventListener
*/
final class LoginListener
{
protected $doctrine;
protected $request;
protected $tokenStorage;
protected $router;
protected $dispatcher;
/**
* Login constructor.
*
* #param Registry $doctrine
* #param TokenStorageInterface $tokenStorage
* #param RouterInterface $router
* #param EventDispatcherInterface $dispatcher
*/
public function __construct(
Registry $doctrine,
TokenStorageInterface $tokenStorage,
RouterInterface $router,
EventDispatcherInterface $dispatcher
) {
$this->doctrine = $doctrine;
$this->tokenStorage = $tokenStorage;
$this->router = $router;
$this->dispatcher = $dispatcher;
}
/**
* #param AuthenticationFailureEvent $event
* #throws ORMException
* #throws OptimisticLockException
*/
public function onAuthenticationFailure(AuthenticationFailureEvent $event)
{
/** #var EntityManager $em */
$em = $this->doctrine->getManager();
$username = $event->getAuthenticationToken()->getUsername();
/** #var AdminUser $user */
$user = $em->getRepository(AdminUser::class)->findOneBy(['username' => $username]);
if ($user instanceof AdminUser) {
$user->addFailedLogin();
if ($user->getFailedLogin() == 5) {
$user->setLocked(1);
}
$em->persist($user);
$em->flush();
}
}
}
Thanks :)
---EDIT---
In fact that listener was an edit from another one. It doesn't need the TokenStorage but I'll have the problem in that one in a near future then :
<?php
namespace App\XXXBundle\EventListener;
use App\XXXBundle\Entity\AdminUser;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
/**
* Class Login
* Listens to user log in events (failure, interactive log in) to provide additionnal security measures
*
* #package App\XXXBundle\EventListener
*/
class Login
{
protected $doctrine;
protected $request;
protected $tokenStorage;
protected $router;
protected $dispatcher;
/**
* Login constructor.
*
* #param Registry $doctrine
* #param TokenStorage $tokenStorage
* #param RouterInterface $router
* #param EventDispatcherInterface $dispatcher
*/
public function __construct(
Registry $doctrine,
TokenStorage $tokenStorage,
RouterInterface $router,
EventDispatcherInterface $dispatcher
) {
$this->doctrine = $doctrine;
$this->tokenStorage = $tokenStorage;
$this->router = $router;
$this->dispatcher = $dispatcher;
}
/**
* #param AuthenticationFailureEvent $event
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
*/
public function onAuthenticationFailure(AuthenticationFailureEvent $event)
{
/** #var EntityManager $em */
$em = $this->doctrine->getManager();
$userName = $event->getAuthenticationToken()->getUsername();
/** #var AdminUser $user */
$user = $em->getRepository(AdminUser::class)->findOneByUsername($userName);
if ($user instanceof AdvancedUserInterface) {
$user->addFailedLogin();
if ($user->getFailedLogin() == 5) {
$user->setLocked(1);
}
$em->persist($user);
$em->flush();
}
}
/**
* #param InteractiveLoginEvent $event
* #throws \Exception
*/
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if ($user instanceof AdvancedUserInterface) {
$em = $this->doctrine->getManager();
if ($user->getLocked()) {
$this->tokenStorage->setToken(null);
throw new CustomUserMessageAuthenticationException('Compte verrouillé.');
}
if ($user->getExpiresAt() && $user->getExpiresAt() <= new \DateTime()) {
$user->setIsActive(0);
$em->persist($user);
$em->flush();
$this->tokenStorage->setToken(null);
throw new CustomUserMessageAuthenticationException('Compte expiré.');
}
if ($user->getCredentialsExpireAt() && $user->getCredentialsExpireAt() <= new \DateTime()) {
$this->dispatcher->addListener(KernelEvents::RESPONSE, [$this, 'redirectToCredentialsChange']);
}
$user->setLastLogin(new \DateTime());
$user->setFailedLogin(0);
$em->persist($user);
$em->flush();
}
}
public function redirectToCredentialsChange(ResponseEvent $event)
{
$event->getResponse()->headers->set('Location', $this->router->generate('admin_security_changecredentials'));
}
}
In the past two days I've been trying to make my custom LocaleSubscriber, a subscriber which is supposed to set user's locale to user's preference or to default.
<?php
namespace App\Event\Subscriber;
use App\Entity\Language;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
/**
* Class LocaleSubscriber
* #package App\Event\Subscriber
*/
class LocaleSubscriber
{
/** #var EntityManagerInterface */
private $entityManager;
/** #var SessionInterface */
private $session;
/**
* LocaleSubscriber constructor.
*
* #param EntityManagerInterface $entityManager
* #param SessionInterface $session
*/
public function __construct(EntityManagerInterface $entityManager, SessionInterface $session)
{
$this->entityManager = $entityManager;
$this->session = $session;
}
/** {#inheritdoc} */
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$request = $event->getRequest();
$user = $event->getAuthenticationToken()->getUser();
if (null !== $user->getLocale()) {
$this->session->set('_locale', $user->getLocale());
$request->setLocale($user->getLocale());
} else {
/** #var Language $language */
$language = $this->entityManager->getRepository(Language::class)->findOneBy(['defaultLang' => true]);
$language = $language->getSlug();
$this->session->set('_locale', $language);
$request->setLocale($language);
}
}
}
I'm trying to do this system because all languages are listed in my database (Language is one of my entities) and I want to make this application easy to maintain and extend for non-technical users.
The problem is that after leaving this method, request's locale is back to en and the new locale is kept only in session.
I've dumped at the end of the function dump($request, $this->session->all()) and both have the same language. Also, the priority in services.yaml is set to 15.
App\Event\Subscriber\LocaleSubscriber:
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin, priority: 15 }
Any idea why is not working? Did someone encountered the same problem? Any examples?
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.