stof / StofDoctrineExtensionsBundle

Integration bundle for DoctrineExtensions by l3pp4rd in Symfony
https://symfony.com/bundles/StofDoctrineExtensionsBundle/current/index.html
MIT License
1.89k stars 380 forks source link

Blameable and decoupled user entity and userinterface'd class #408

Open danaki opened 4 years ago

danaki commented 4 years ago

Hi, I'm using decoupled Entity and security user like it's described here https://stovepipe.systems/post/decoupling-your-security-user

The reason for that is that I want to keep domain separated following DDD principles.

Now my entity looks like:

class Batch {
// ....
    /**
     * @var Employee
     *
     * @Gedmo\Blameable(on="create")
     * @ORM\ManyToOne(targetEntity="Employee")
     */
    private $createdBy;
//...

while class 'App\Security\User\SecurityUser' implements actual UserInterface.

When I try to save the entity, it produces an error:

The class 'App\\Security\\User\\SecurityUser' was not found in the chain configured namespaces App\\Entity

Which is because of this line of code:

        if (null !== $token && $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
            $this->blameableListener->setUserValue($token->getUser());
        }

$token->getUser() returns an object of class App\\Security\\User\\SecurityUser not the Entity. Is it possible to override this behavior introducing a configuration option?

mleczakm commented 4 years ago

It is not possible without overriding BlameableListener, but additionaly you need compiler pass if you need to inject custom dependencies, for example using process function from your app kernel. You will need to inject them using setters instead of constructors because of endless loop:

    /**
     * You can modify the container here before it is dumped to PHP code.
     */
    public function process(ContainerBuilder $container): void
    {
        $container
            ->getDefinition('stof_doctrine_extensions.listener.blameable')
            ->addMethodCall('setEntityManager', [$container->getDefinition(EntityManagerInterface::class)])
        ;
    }

Custom blameable listener need to extend default class, example of it:

class BlameableListenerDecorator extends BlameableListener
{
    /**
     * @var EntityManagerInterface
     */
    private $em;

    public function setEntityManager(EntityManagerInterface $em): void
    {
        $this->em = $em;
    }

    public function setUserValue($user): void
    {
        if ($user instanceof CustomUser) {
            $user = $this->em->find(UserEntity::class, $user->getId());
        }

        parent::setUserValue($user);
    }
}

BTW Consider moving to supported fork https://github.com/antishov/StofDoctrineExtensionsBundle

danaki commented 4 years ago

Didn't know about the fork. Ok, I'll reopen this issue there.