1

I have an entity User which has a OneToMany relation with an entity named Session. So far so good, I can do $myUser->getSessions() and get an array with all the sessions related to the $myUser.

Session also had a OneToMany relation with another entity Moment.

Now, this Moment Entity has moved into a Document because that part of the app is now on a MongoDB database mainly for performance issues.

To keep the application running without rewriting lot of code I've created a Doctrine Listener like this :

class DoctrineListenerPostLoad
{
    private $container;
    private $dm;

    public function __construct(Container $container, DocumentManager $dm)
    {
        $this->dm           = $dm;
        $this->container    = $container;
    }

    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();

        if($entity instanceof Session) 
        {
            $entity->setMoments(array());
            $entity     = $args->getEntity();
            $moments    = $this->dm->getRepository('AppBundle\Document\Moment')->findBy(array('idSession' => $entity->getId()));
            $entity->setMoments($moments);  
        }   
    }
}

So when an instance of Session is loaded the listener get the moments related to the session in the mongodb database. But by doing so I lose doctrine's lazy load. And when I need to get all the Sessions of an user (but not the Moments) I get a OutOfMemoryException due to the high amount of data because it does load the moments.

I know I can just "unbind" Session and Moment and do a DocRepo->findBy(array('idSession' => $entity->getId()) when I need it, but I will have to rewrite a lot of nicely working code on the app. Is there another way ? Like maybe loading the DocumentManager in the entity (Yeks!) or check if a getter has been called in the PostLoad ?

Thank you !

AdrienXL
  • 3,008
  • 3
  • 23
  • 39

1 Answers1

1

1: Use Ocramius/ProxyManager to wrap database call and defer until somebody tries to use the proxied collection (should be quite straightforward)

2: Manually craft ODM's PersistentCollection that is uninitialized. If you pretend to use @ReferenceMany(repositoryMethod="getBySession", targetDocument=Moment::class) for the moments, all should work fine as collection will be lazy-loaded again using a repository method you write (the method will be given Session object as a first argument). Roughly your postLoad will look like:

$mapping = [
    'association' => \Doctrine\ODM\MongoDB\Mapping\ClassMetadata::REFERENCE_MANY,
    'repositoryMethod' => 'getBySession',
    'strategy' => 'setArray',
    'targetDocument' => Moment::class,
];
$coll = $this->dm->getConfiguration()->getPersistentCollectionFactory()->create($this->dm, $mapping);
$coll->setOwner($entity, $mapping);
$coll->setInitialized(false);
$entity->setMoments($coll);

Please note that I might have missed some required $mapping attribute and this approach may not work forever (it shouldn't break in 1.x though). If you don't want to "cheat" with reference mapping you might get away with mapping Session as a Document and mapping moments directly with @ReferenceMany(repositoryMethod="getBySession", targetDocument=Moment::class), but I'm not sure about side-effects.

malarzm
  • 2,831
  • 2
  • 15
  • 25