Injecting Securitycontext into a Listener Prepersist or Preupdate in Symfony2 to Get User in a Createdby or Updatedby Causes Circular Reference Error

Injecting SecurityContext into a Listener prePersist or preUpdate in Symfony2 to get User in a createdBy or updatedBy Causes Circular Reference Error

I had similar problems and the only workaround was to pass the whole container in the constructor (arguments: ['@service_container']).

use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;

class MyListener
{
protected $container;

public function __construct(ContainerInterface $container)
{
$this->container = $container;
}

// ...

public function prePersist(LifeCycleEventArgs $args)
{
$securityContext = $this->container->get('security.context');

// ...
}
}

Symfony2: Injecting @security.context to get the current user. How to avoid a ServiceCircularReferenceException? Inject the whole container?

Injecting the whole container directly into the lister may be a working solution ... but we can do better :)

Inject a UserCallable that returns the current user instead.

This way you express the real purpose of the depedency more clearly without introducing a hard dependency between your listener and the container(-interface). An example would be ...

Knp\DoctrineBehaviors\ORM\Blameable\UserCallable

This particular example can be improved further by creating an interface and using that for type-hinting in your listener instead. That allows easier exchangeability if you plan to re-use the listener.

The interfaces:

namespace Acme\Common;

interface UserCallableInterface
{
/**
* @return \Symfony\Component\Security\Core\User\UserInterface
*/
public function getCurrentUser();
}

namespace Acme\Common;

use Symfony\Component\Security\Core\User\UserInterface;

interface TrackableInterface
{
/**
* @param UserInterface $user
*/
public function setUser(UserInterface $user);
}

The UserCallable:

namespace Acme\Util;

use Acme\Common\UserCallableInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class UserCallable implements UserCallableInterface
{
/** @var ContainerInterface **/
protected $container;

/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}

/**
* @{inheritdoc}
*/
public function getCurrentUser()
{
return $this->container->get('security.context')->getToken()->getUser() ?: false;
}

The listener:

use Acme\Common\UserCallableInterface;
use Acme\Common\TrackableInterface;
use Doctrine\Common\EventArgs;

class Listener
{
/** @var UserCallableInterface **/
protected $userCallable;

/**
* @param UserCallableInterface $user_callable
**/
public function __construct(UserCallableInterface $user_callable)
{
$this->userCallable = $user_callable;
}

/**
* @param EventArgs $args
**/
public function onPrePersist(EventArgs $args)
{
$entity = $args->getEntity();

if ( !($entity instanceof TrackableInterface) ) {
return;
}

if ( !($user = $this->userCallable->getCurrentUser())) {
return;
}

$entity->setUser($user);
}
}

Circular Reference when injecting Security Context into (Entity Listener) Class

This happens because your security context depends on this listener, probably via the entity manager being injected into a user provider. The best solution is to inject the container into the listener and access the security context lazily.

I typically don't like injecting the entire container into a service, but make an exception with Doctrine listeners because they are eagerly loaded and should therefore be as lazy as possible.

Symfony2 - Inject the currently logged in user into a listener

for symfony 2.4 you should be able to inject expression,
so instead of @service_container
use
@=service('security.context').getToken().getUser() to get user directly

Symfony 2: Get Security Context Outside of Controller

The best way to do this is using (as phpisuber said) dependency injection through the Service Container. But, instead of injecting the entire container (which is considered bad practice as it makes your entire class less testable and breaks loose coupling) you should inject the security.context service like so:

acme_foo.bar_service:
class: %acme_foo.bar_service.class%
arguments:
- @security.context

Your service can be something like this:

<?php
namespace Acme\FooBundle\Service;

use Symfony\Component\Security\Core\SecurityContext;

class BarService
{
/**
* @var SecurityContext
*/
protected $context;

/**
* @param SecurityContext $context
*/
public function __construct($context)
{
$this->context = $context;
}

public function doSomething()
{
return $this->context->isGranted('ROLE_USER');
}
}

Injecting EntityManager-dependend service into Listener

First of all I switched from an EventListener to an EventSubscriber. From the docs:

Doctrine defines two types of objects that can listen to Doctrine events: listeners and subscribers. Both are very similar, but listeners are a bit more straightforward.

It turns out one can access the ObjectManager via the passed $args-parameter like so:

/** @var Doctrine\Common\Persistence\ObjectManager $manager */
$manager = $args->getObjectManager();

So either use it directly in the callback:

public function postUpdate(LifecycleEventArgs $args)
{
$manager = $args->getObjectManager();
// ...

...or set it to an object field:

/** @var ObjectManager $manager */
private $manager;

public function postUpdate(LifecycleEventArgs $args)
{
$this->manager = $args->getObjectManager();
// ...

Sonata User - Injecting securityContext into Admin class

After weeks of search with no response at all on none of my questions,

I finally found a SonataAdmin service definition example.

Here is what looks like mine:

services:
sonata.admin.user:
class: Application\Sonata\UserBundle\Admin\Entity\UserAdmin
tags:
- {name: sonata.admin, manager_type: orm, group: user, label: users}
arguments:
- null
- Application\Sonata\UserBundle\Entity\User
- 'SonataAdminBundle:CRUD'
calls:
- [setTranslationDomain, [MyProjectBundle]]
- [setUserManager, ['@fos_user.user_manager']]
- [setSecurityContext, ['@security.context']]

Obviously setSecurityContext() have to be implemented into the admin class.

Symfony2 get security context in custom validator

You either need to extend ContainerAware with your filter class then call setContainer on the class when you instantiate it or pass through the container to the class on construct. This is because filters are not container aware by default like the controller is.

To do it by constructor you can add the __construct method like below to your filter class:

protected $container;

public function __construct($container){
$this->container = $container;
}

Either way you will need to use container in your call:

$this->container->get('security.context')->getToken()->getUser()

Check this link https://groups.google.com/forum/?fromgroups#!topic/symfony2/WWIc8N7KZrg for extending container aware idea for doctrine entities, might be of some help.

Edit

Looking at the docs for validator you can add a service if it has any dependencies:

services:
validator.unique.your_validator_name:
class: Fully\Qualified\Validator\Class\Name
tags:
- { name: validator.constraint_validator, alias: alias_name }

why not add an argument to that for the container:

arguments:  [@security.context] 

and then use the construct method?

protected $security_context;

public function __construct($security_context){
$this->security_context = $security_context;
}

Symfony2 access user from twig extension service

I solved it by adding an if to my SecurityContext calls.
Checking if the GetToken() returned an object (authenticated) or string (anonymous)

if (is_object($this->context->getToken()))
{
.... // getUser() etc.
}


Related Topics



Leave a reply



Submit