cross-solution / YAWIK

YAWIK is a web application. It can be used as an ATS applicant tracking system or as a jobboard.
https://yawik.org
MIT License
125 stars 67 forks source link

Allow entities to embed documets from other modules #299

Closed TiSiE closed 7 years ago

TiSiE commented 8 years ago

We need the ability to have entites from one module be embedded in entites from other modules. e.g. The Applications module embeds a document to an organization entitiy.

This mechanism must be independent from Doctrine, because disabling one module should not break the other - we must come up with an own strategy. But maybe we could use Doctrines "Custom Collection Classes" in combination with postLoad Events....


Concept sketch in this gist

TiSiE commented 8 years ago

A concrete example would be custom and editable job categories which would be embedded in an organization entity to be available to all jobs created for this organization. Managable by the organization owner (or any employment with the correspondent right). Speaking of that, customizable employee rights are just another example of such embedded entities.

TiSiE commented 8 years ago

@fedys I tried to sketch a concept in a gist what do you think?

fedys commented 8 years ago

I can take a look at it on Monday. I will let you know my thoughts.

fedys commented 8 years ago

I went over the description above and the concept in the gist and I did not grasp it. Please provide an example of a client code. I think this could help me to understand the concept easier.

TiSiE commented 8 years ago

We want to create a mechanism to let modules embed their own documents into an entity of another module. This is to allow to access this document through the other modules' entity.

If a module is disabled however, its embedded documents in the entities of the other module MUST NOT affect the functionality of YAWIK. Therefor we CAN NOT use Doctrine, as its implementation relies on the classes of embedded documents to be available (and they are not, if a module is not loaded.)


Example client code:

$moduleA_entity->embed($moduleB_entity);
$moduleB_entity = $moduleA_entity->getEmbedded('moduleB\Entity\moduleB_entity');

/* or define own key */
$moduleA_entity->embed($moduleB_entity, 'moduleB.custom');
$moduleB_entity = $moduleA_entity->getEmbedded('moduleB.custom');

/* If not embedded or module is disabled getEmbedded returns null */
$moduleB_entity = $moduleA_entity->getEmbbeded('nonExistentKey');
var_dump($moduleB_entity); // => null
var_dump($moduleA_entity->getEmbedded('moduleB.custom')); // null, if ModuleB is disabled
fedys commented 8 years ago

I assume the $moduleB_entity will not be managed by an ODM and embeds inside the $moduleA_entity will be stored in @ODM\Hash (we will be responsible for correct hydrating/extracting). Am I right?

TiSiE commented 8 years ago

Yes, and that's is exactly the point where it gets dirty. I'm not so sure anymore that embedding is the way to go.

What if we just store references and require a dedicated collection for moduleB_entities. That way we just have to implement the logic to fetch/store the referenced entity from the ODM.

{
    "moduleB.custom": {
         "repository": "ModuleB/EmbeddedEntities",
         "entitiy": "<objectId>"
    }
}

in a custom "Referenced Entity Collection":

function loadEmbedded($key)
{
    $ref = $this->getEmbeddedRef($key);
    return $this
        ->repositories
        ->get($ref->getRepository()) 
        ->find($ref->getEntityId())
    ;
}
fedys commented 8 years ago

I thought of it the same way! I think it would be more sensible solution ;)

fedys commented 8 years ago

Do you want me to sketch a use case with a custom "Referenced Entity Collection"?

TiSiE commented 8 years ago

I created a new gist reflecting the reference idea..

The only thing I didn't figure out is how to inject the dependencies in the custom collection (which will further on be wrapped in a PersistentCollection once loaded from the ODM). Although I did read the documentation.

fedys commented 8 years ago

Dependencies can be injected via AbstractPersistentCollectionFactory described in http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/custom-collections.html#taking-control-of-the-collection-s-constructor

fedys commented 8 years ago

I went over the new gist and it looks good.

fedys commented 8 years ago

I noticed the new gist does not support storing/retrieving arrays of entities (e.g. mentioned categories).

TiSiE commented 8 years ago

You would store the reference (=>attach) [to] an entity which holds the categories in an array....

TiSiE commented 8 years ago

Dependencies can be injected via AbstractPersistentCollectionFactory

I've read this, but as I understand this factory is then used to create ALL persistent collections, so we would need a control statement (as in the example outlined). Also the factory is only used when entities are fetched from the database and not, if an entity is created.

fedys commented 8 years ago

When entity is created AttachedEntitiesCollection could be passed via entity constructor.

TiSiE commented 8 years ago

I remembered, we once created a RepositoryEventSubscriber which injects the entity prototype into a repository. That's where the dependencies can be injected. I updated the gist accordingly.

fedys commented 8 years ago

Nice one

TiSiE commented 8 years ago

@fedys If you don't mind, go ahead and code it! :smile:

fedys commented 8 years ago

@TiSiE I am working on it.