Skip to content

Instantly share code, notes, and snippets.

@ElectricMaxxx
Last active August 29, 2015 13:57
Show Gist options
  • Save ElectricMaxxx/9659316 to your computer and use it in GitHub Desktop.
Save ElectricMaxxx/9659316 to your computer and use it in GitHub Desktop.
Combine ODM and ORM

Hi,

it try to combine some orm and odm stuff. All examples i made are done with the phpcr-odm. I try to understand the lazy-loading of collections, cause i need to do same when loading documents that are referenced by an entity.

But from beginning: When i understand it right, doctrine helps while loading collections. It doesn't query for every item in a collection if it isn't realy needed, means if it isn't asked for. Atm i create a project where i need to load some documents that are referenced in an entity on my own. All references in one world (ORM or ODM) will end up with a list of proxies. But as i am loading the document on my own i would like to have an equal solution for that.

But how do i load the documents? I have stored an uuid of the documents in every entity, so i have got a (almost) unique reference. I created a DocumentAwareEntityInterface that looks like that:

/**
 * This interface helps to combine the ORM world with the ODM world.
 *
 * Entities which implement this interface are able to reference a document by its uuid.
 * This uuid will be persisted into the relational entity. On the other hand the document
 * is fetched from the document repository on an preLoad event and back on an
 * preUpdate/perPersist event.
 *
 * To make reusable a EntityAwareDocumentInterface will be created too. By doing this
 * we are able to handle it in both world.
 *
 * @author Maximilian Berghoff <[email protected]>
 */
interface DocumentAwareEntityInterface
{
    /**
     * Simple setter for the referenced document.
     *
     * @param EntityAwareDocumentInterface
     */
    public function setDocumentNode(EntityAwareDocumentInterface $document);

    /**
     * Simple getter for the referenced document
     *
     * @return EntityAwareDocumentInterface
     */
    public function getDocumentNode();

    /**
     * Getter for the uuid of the referenced document.
     *
     * @return $uuid
     */
    public function getUuid();

    /**
     * Setter for the uuid of the referenced document.
     *
     * @param $uuid
     */
    public function setUuid($uuid);
}

As you can see, i have got an interface for the "other" side too. But that doesn't matter atm. I created an eventlistener on postLoad:

    /**
     * When an entity was loaded that implements the DocumentAwareEntityInterface
     * we will fetch the content by it uuid from the document repository an store it instead of the
     * uuid to the entity
     */
    public function postLoad(LifecycleEventArgs $eventArgs)
    {
        $entity = $eventArgs->getObject();
        if ($entity instanceof DocumentAwareEntityInterface) {
            $uuid = $entity->getUuid();
            if (!UUIDHelper::isUUID($uuid)) {
                return;
            }
            $document = $this->getDocumentManager()->find(null, $uuid);
            if (null === $document) {
                return;
            }

            $entity->setDocumentNode($document);
            $entity->setTitle($document->getTitle());
        }
    }

As you can see my entity has a field for title too. So i do not need to query the document when just displaying the entity in a list or selection. Quite dirty but it's ok.

For addition only i got an listener on postUpdate too:

    /**
     * When an entity is persisted or updated we need to take care of the entire document.
     * This one needs to be persisted/updated too.
     *
     * @param \Doctrine\ORM\Event\LifecycleEventArgs $eventArgs
     */
    public function preUpdate(LifecycleEventArgs $eventArgs)
    {
        $entity = $eventArgs->getObject();
        if ($entity instanceof DocumentAwareEntityInterface) {
            $document = $entity->getDocumentNode();
            if ($document instanceof EntityAwareDocumentInterface) {
                $this->getDocumentManager()->persist($document);
                $this->getDocumentManager()->flush();
            }
        }
    }

The one for "create new" isn't realy ready, cause i have issues with the parent/position setting for a new document.

That works fine for me, but to fine. Cause: A collection of those entites will query the odm part of the database for every entity. Means in my case: i got a select-field (entiy-form-type) with 2k Entites -> 2k queries to get the document, what is quite uncool.

So,... What i would need now is to learn how the lazy loading works and how it is implemented. I want to query when $entity->getDocument() is called only.

This example could end up in a proposal to combine orm and odm world. I do not know if the other odm implementation has got an equal way for referencing, but the phpcr-odm has it.

You would ask: Why the hell is he doing that? Cause i got a structure where have tons of relations (-1 for odm) but would need versioning and translations for some properties (+1 for odm). So i wanne combine that stuff.

What do you think?

@ElectricMaxxx
Copy link
Author

I think i will need some kind of class metadata mapping for that to know the documents/enties. Btw. this would solve my position/parent problem on create too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment