Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending Doctrine EntityManager with EntityManagerDecorator leaves wrong reference in UnitOfWork

I am trying to extend the EntityManager in Doctrine using the EntityManagerDecorator and have run into a problem with a reference to the base EntityManager in the UnitOfWork being passed to the prePersist event via the $eventArgs.

It looks like the EntityManager is passed to the UnitOfWork in the EntityManager::__construct when the UnitOfWork is created.

I thought a solution might be that I can override the default UnitOfWork in MyEntityManagerDecorator::getUnitOfWork() like:

public function getUnitOfWork()
{
    if ($this->unitOfWork === null) {
        $this->unitOfWork = new UnitOfWork($this);
    }

    return $this->unitOfWork;
}

However I noticed that the UnitOfWork::__construct() requires an EntityManager not an EntityManagerInterface so this would not work.

I am looking for how I can get MyEntityManagerDecorator from the prePersist $eventArgs->getEntityManager() instead of the base EntityManager. I would like to do this without directly inheriting the EntityManager. The docs also say "You should never attempt to inherit from the EntityManager: Inheritance is not a valid extension point for the EntityManager.".

I am not sure what you require for code samples, if any, so please let me know if you require more information. I have access to MyEntityManagerDecorator as expected everywhere else in the project (that I have tried so far). I setup the decorator in yml like so:

my_multi_tenant_entity_manager:
    public: false
    class: My\MultiTenantBundle\ORM\MyEntityManagerDecorator
    decorates: doctrine.orm.default_entity_manager
    arguments: [ "@my_multi_tenant_entity_manager.inner" ]

Here is a list of packages and version numbers I am using dumped from composer:

installed:
doctrine/annotations                 v1.2.3             Docblock Annotations Parser
doctrine/cache                       v1.4.0             Caching library offering...
doctrine/collections                 v1.2               Collections Abstraction ...
doctrine/common                      v2.4.2             Common Library for Doctr...
doctrine/dbal                        v2.5.1             Database Abstraction Layer
doctrine/doctrine-bundle             v1.3.0             Symfony DoctrineBundle
doctrine/doctrine-cache-bundle       v1.0.1             Symfony2 Bundle for Doct...
doctrine/doctrine-migrations-bundle  dev-master 6a1bd73 Symfony DoctrineMigratio...
doctrine/inflector                   v1.0.1             Common String Manipulati...
doctrine/instantiator                1.0.4              A small, lightweight uti...
doctrine/lexer                       v1.0.1             Base library for a lexer...
doctrine/migrations                  dev-master 058a463 Database Schema migratio...
doctrine/orm                         v2.4.7             Object-Relational-Mapper...
incenteev/composer-parameter-handler v2.1.0             Composer script handling...
jdorn/sql-formatter                  v1.2.17            a PHP SQL highlighting l...
kriswallsmith/assetic                v1.2.1             Asset Management for PHP
monolog/monolog                      1.12.0             Sends your logs to files...
phpdocumentor/reflection-docblock    2.0.4              
phpspec/prophecy                     v1.3.1             Highly opinionated mocki...
phpunit/php-code-coverage            2.0.15             Library that provides co...
phpunit/php-file-iterator            1.3.4              FilterIterator implement...
phpunit/php-text-template            1.2.0              Simple template engine.
phpunit/php-timer                    1.0.5              Utility class for timing
phpunit/php-token-stream             1.4.0              Wrapper around PHP's tok...
phpunit/phpunit                      4.5.0              The PHP Unit Testing fra...
phpunit/phpunit-mock-objects         2.3.0              Mock Object library for ...
psr/log                              1.0.0              Common interface for log...
raven/raven                          dev-master 407d770 A PHP client for Sentry ...
sebastian/comparator                 1.1.1              Provides the functionali...
sebastian/diff                       1.2.0              Diff implementation
sebastian/environment                1.2.1              Provides functionality t...
sebastian/exporter                   1.2.0              Provides the functionali...
sebastian/global-state               1.0.0              Snapshotting of global s...
sebastian/recursion-context          1.0.0              Provides functionality t...
sebastian/version                    1.0.4              Library that helps with ...
sensio/distribution-bundle           v3.0.16            Base bundle for Symfony ...
sensio/framework-extra-bundle        v3.0.4             This bundle provides a w...
sensio/generator-bundle              v2.5.2             This bundle generates co...
sensiolabs/security-checker          v2.0.1             A security checker for y...
swiftmailer/swiftmailer              v5.3.1             Swiftmailer, free featur...
symfony/assetic-bundle               v2.6.1             Integrates Assetic into ...
symfony/monolog-bundle               v2.7.1             Symfony MonologBundle
symfony/swiftmailer-bundle           v2.3.8             Symfony SwiftmailerBundle
symfony/symfony                      v2.6.4             The Symfony PHP framework
twig/extensions                      v1.2.0             Common additional featur...
twig/twig                            v1.18.0            Twig, the flexible, fast...
like image 423
ryakad Avatar asked Feb 27 '15 18:02

ryakad


1 Answers

I would suggest to decorate (and not directly extend) the EntityManager, as it looses the coupling between your implementation and the inherited component.

In order to be able to distinguish entities that do have a relationship to a tenant implement/extend those classes from an interface or a mapped superclass.

The securityContext (for demonstration purpose) is there to get the reference to the tenant.

/**
 * `EntityManagerDecorator` exists since v2.4
 */
class MultiTenantEntityManager extends EntityManagerDecorator {

    private $securityContext;

    public function __construct(EntityManagerInterface $entityManager, $securityContext) {
        parent::__construct($entityManager);
        $this->securityContext = $securityContext;
    }

    public function persist($entity) {
        // set the tenant before persisting an entity
        if ($entity instanceof MultiTenantEntity) {
            $userId = $this->securityContext->getUserId();
            $tenant = $this->wrapped->find($userId,...);
            $entity->setTenant($tenant);
        }
        return $this->wrapped->persist($entity);
    }
}
like image 80
fateddy Avatar answered Sep 28 '22 14:09

fateddy