Creating Doctrine (pseudo) Entities on the fly? - php

I've been thrown a 'can it be done in Doctrine' type question by my supervisor, and while I've been able to do some of it with external 'Mapper' objects, I'm wondering if there might be any way to fake-out the ORM to do it within Doctrine itself.
Essentially, the thing we are interested in doing is cutting down database clutter. We have a number of tables containing distinct properties or item sets in various categories and then a whole bunch of link-tables tying them to other properties or item sets. For example
We have a couple of tables such as 'Materials' and 'PaperTypes' which describe various Material and Paper options for our products. We then group these into 'MaterialCollections' and 'PaperFamilies' respectively. Then there has to be a one-to-many link table between MaterialCollection/Materials and PaperFamilies/PaperTypes respectively. Repeat these types of relationships a couple dozen more times and you see where our DB is starting to clutter up.
The link tables themselves are nothing more than a number of entries including multiple records with the PrimaryId from the parent table (collections/families) and unique-per-parent PrimaryId's from the sub-table (materials/papertypes). Link tables could be named something like MaterialCollectionsMaterials and PaperFamilyPaperTypes for example.
The idea was to get rid of this slew of link tables by using an abstract 3-table structure as follows:
Lists (consisting of a unique ListId, a ListName and a TypeId)
Types (consisting of a unique TypeId, a TypeName and an EntityName)
ListXref (consisting of ListIds and memberId which points to the primaryId from the Entity designated in the list type)
Multiple lists can exist for a given type, but the Xref pairs are unique. Different Types can also point to the same Entity. (e.g. there may be more than one type of Material list defined)
The 'ListName' would be the equivalent of the parent TableNames above and would allow for eliminating two of the tables in those relationships. So the records in 'MaterialCollections' would now instead be records in the List table of TypeName "MaterialCollection". The records that would have been in a link table (such as MaterialCollectionsMaterials) would now instead be pointed to from ListXRef.
As stated, I've gotten a basic mapper to make this work for rather basic list creation. But what I'm wondering is if there is any way to create Entities or things that behave like Doctrine Entities to establish the abstract relationships between a given list/listtype and the table referenced by EntityName and the corresponding memberIds?
In other words, it would be really nice if I had some means by which I could produce something that had the bulk of functionality of a Doctrine Entity which could be retrieved from the Service Manager or something like it that would behave (have similar properties/functions) like a Doctrine Entity.
I tried adding a wrapper object in the Entity tree that my mappers could try to retrieve which was basically an inherited version of the Xref entity with a few alias functions, but it can't be retrieved with the Entity manager.
Any help is appreciated.
P.S. While it is not a priority at the moment, longer term I also want to really throw a wrench into the works by trying to have some lists be capable of pointing back to the records produced by other lists. So, for example, a List "ProductXMaterials" of type "ProductMaterials" might point to some but not all results of "MaterialCollections". But I'll worry about this one later.

Well, Doctrine can't generate "pseudo entity classes" but you can.
First you need to hook into the Doctrine "loadClassMetadata" event.
Inside, you generate your entity classes and configuration from code on the fly and dump them into a specified directory (in the cache for example) maintaining the doctrine convention naming but with a custom namespace. For example : "AutoGeneratedNamespace\Entities"
So let's say you need to have a dynamic entity generated for an entity with name: "TmpUser", and you're using "yml" instead of annotations
<?php
namespace Example;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class DoctrineEventListener
{
/**
* Invoked on Doctrine loadClassMetadata event
*
* #var Doctrine\ORM\Event\LoadClassMetadataEventArgs
**/
public function loadClassMetadata(LoadClassMetadataEventArgs $args)
{
$em = $args->getEntityManager();
$metadata = $args->getClassMetadata();
$factory = $em->getMetadataFactory();
$name = 'AutoGeneratedNamespace\Entities\TmpUser';
$tmpDirectory = '/path/to/cache/generated-classes';
// current class metadata is the same as the one we need. this means we already generated it
if($metadata->getName() == $name || $factory->hasClassMetadata($name)) {
return;
}
/**
Generate your class here and dump it into the cache directory with the name: TmpUser.php
(If you're using "yml" instead of annotations also create the TmpUser.orm.yml configuration and dump it in the same directory)
**/
// create a metadata for the newly created class
$metadata = $factory->newClassMetadataInstance($name);
// Register metadata so doctrine knows about it
$factory->setMetadataFor($name, $metadata);
}
}
Finally, you tell Doctrine to look for entities inside the cache directory as well using the configuration: (This is taken from the Symfony documentation so if you're not using Symfony it may vary a bit but you can find the precise configuration in the Doctrine docs)
doctrine:
orm:
mappings:
MyGeneratedEntitiesUniqueMappingsKey:
type: yml
dir: '/path/to/cache/generated-classes'
is_bundle: false
prefix: "AutoGeneratedNamespace\Entities"
alias: GeneratedEntities

Related

Symfony2 - Sharing users between multiple applications

I am currently searching for a way to share users between multiple Symfony2 applications for one of our customers. In the past 3 years, we made a few applications (4) for them that have different purposes, but always use the "same" user model and data.
Currently, we have 4 separate databases, where the "users" table is kinda the same on all the applications, except for the many-to-many relationships. At first, I was thinking about adding a second entity manager (and connection), and putting the users in a separate database. All the applications would be able to use this and all the users would have the same credentials. But how do I handle the many-to-many relationships?
To give an example, on application A you have a many-to-many relation from "Users" to "Clients", but the "Clients" table doesn't exist in application B/C/D. On application B, you have a many-to-many relation from "Users" to "Suppliers", but the "Suppliers" table doesn't exist in application A/C/D and so on. Moving the "Clients" or "Suppliers" table to the shared database isn't really an option either, because other entities (which are not shared) are also having relations to those tables.
I basically need to find a way to map many-to-many relationships on the "shared user" model/database which are unique for each application. Is there a way to achieve this with multiple databases? Should I go for some other approach?
All info is welcome. Thanks in advance.
Option 1
Using different connections, this doesn't seem to be possible with Doctrine out of the box .
Similar questions have been answered already:
Using Relationships with Multiple Entity Managers
Entities associations across different managers
As stated in the first answer, you could do the following:
keep the object graphs disconnected by saving the identifiers of the related objects (old style) instead of a reference to them, then manually get the objects through services.
But if your want Doctrine to actually be aware of the associations, you need to persist the associated entities in the same database, or your mapping will just generate errors. That means you would need to duplicate the User entity.
Option 2
In the very specific case where you can use the same connection (that is, with multiple databases of the same DBMS on the same host and with the same user), there seems to be a way, but I haven't tested it:
https://techpunch.co.uk/development/using-multiple-databases-with-symfony2-and-doctrine2
The idea is to prefix each table with the database name, as if it were a schema name, like this:
This entity is mapped to the «User» table in the database «users»:
<?php
namespace Demo\UserBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* #ORMTable(name="users.User")
*/
class User
{
/* ... */
}
This one is mapped to the «Post» table in the database «posts»:
<?php
namespace Demo\PostBundle\Entity;
use DoctrineORMMapping as ORM;
/**
* #ORMTable(name="posts.Post")
*/
class Post
{
/* ... */
}
Then you can make associations as usual:
class Post
{
/**
* #ORM\ManyToOne(targetEntity="\Demo\UserBundle\Entity\User")
**/
private $user;
/* ... */
}
The author also links to an example project on github:
https://github.com/lobsterdore/symfony2-multiple-db-example

One entity to multiple tables with symfony/doctrine

Introduction
I'm working on a symfony3 project and I have a lot of different "types" for certain entities. What I mean by this is that, I have a TypeUser, TypeEvent, TypeMeeting tables.
These tables will only have an id and a label.
Problem:
When using doctrine, I can link 1 entity to 1 table using the anotations, like so:
/**
* #ORM\Entity
* #ORM\Table(name="TypeUser")
*/
However, I would like this part to be completely generic. How can I specify the Table name depending on the type I need?
Is there another alternative when using Doctrine other than the annotations to make this possible?
I would really like to avoid making n entities for n tables, when they are very similar in name and in structure.
Question:
Is it possible to make one generic entity to match a specific TypeXXXX table, to reduce redundancy? If so how would I go about doing it?
Similar Doctrine 2.1 - Map entity to multiple tables
Symfony book on doctrine

ZF2 + Doctrine - Entities - how to access related data from an Entity and do something to it?

I'm working on an application with ZF2 and Doctrine that has 2 MySQL tables (with associated Doctrine Entities):
Readings (contains a list of values received from Sensors)
Sensors (contains the definition of the Sensors, how they are configured etc)
In my application, I have a view which lists the values from the Readings entity in date order. One of the columns, "r_value", needs to be converted using a formula that is stored in the Sensors table. If I can retrieve the appropriate value from the Sensors table, I can make a quick function (getConvertedValue()) to return the appropriately formatted data.
What I'd like to do
In my view, I'd like to call $row->getConvertedValue() in the same way I can call $row->getRValue()
My questions are
What is the correct way to define the getConvertedValue() function?
Where should it reside (in my Readings Entity, in a Repository which extends the Entity etc)?
Sorry if it's a basic question, but I can't seem to find a clear example to work from.
You should place the function inside the Readings entity:
<?php
class Readings
{
public function getConvertedValue()
{
$formula = $this->getSensor()->getFormula();
$value = $this->getRValue();
// Calculate $result using the formula
return $result;
}
}
I'm assuming a *-to-one relationship from the Readings entity to the Sonsors entity.

Doctrine ORM on EAV Tables

I was planning on creating my application and use an ORM for the models, but the thing is, there's a part of the database which uses Entity-Attribute-Value Tables.
I pretty liked Doctrine ORM but I don't know if it is possible to maybe create classes that would look like any ordinary doctrine entity, when the table actually hooked up to is of EAV style.
Would it be possible to use Doctrine on this, and if so, how?
definitely possible:
Have relationships like this:
Object (one to many) -> AttributeValue -> Many to One -> AttributeType
In view of EAV it seems to be obvious how to build a relation between entity and attribute using doctrine. In the most complicated case we deal with a Many to Many relation.
So lets say we want to map an attribute Name to an entity User. Assuming a user has exactly one name and each name belongs to exactly one user this link can be archived using One to One relation
But how to model the relation between attribute and value? The problem is that values can be of different types or even need different numbers of fields in order to save their information.
Consider the attributes name and phone_number. While a name might be represented by a string, an integer could be needed for the phone number. Or it is even necessary to not only the number but also the area code in a separate filed.
Because EAV requires very flexible value representation, it is not possible to store all of them within the same field in a database table (disregard blobs, data serialization an the like). Therefore most EAV implementations using different tables representing different value types.
In order to reach such flexibility, doctrine features Inheritance Mapping. It basically allows you to extend doctrine entities. Doing so you specify a discriminator for each sub-type of your entity:
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="value_type", type="string")
* #DiscriminatorMap({"name" = "Name", "phone" = "PhoneNumber"})
*/
class Value
{
// ...
}
/** #Entity */
class Name extends Value
{
// ...
}
/** #Entity */
class PhoneNumber extends Value
{
// ...
}
The Value class provides common implementation for all values, i.e. an id. Each subclass (i.e. Name and PhoneNumber) extend those common values by their specific ones, for example additional fields.
The #DiscriminatorColumn defines a column in the parent relation which stores the type of the value.
The #DiscriminatorMap is used by doctrine to map the type from the #DiscriminatorColumn to one of those classes.
The relation between attribute and value can be specified to the parent class. Calling values from the attribute then will fetch all types of values which can be filtered (and dealt with) during runtime using for example instanceof.

Doctrine2 - using a foreign key column as a normal field

Is there a way to have something like this in doctrine:
class Entity {
/**
* #Column(name="related_entity_id")
*/
private $relatedEntityId;
/**
* #ManyToOne(targetEntity="RelatedEntitiy")
* #JoinColumn(name="related_entity_id", referencedColumnName="id")
*/
private $relatedEntity;
}
What I want to do I do something like this:
call Entity::setRelatedEntityId($someId), and persist the entity,
and have the entity return the related entity by calling Entity::getRelatedEntity().
The related entity is selected from a table which will be strictly limited and it will never dynamically grow at runtime, so there is a finite number of related entity ids.
At the time of creating a new Entity, I'd like to set the related entity id, but without having to fetch the whole related entity from the database.
As far as I could test this, it does not work, because if I set the relatedEntityId but not the relatedEntity, Doctrine automatically sets the related_entity_id column to null, since basically no relationship has been established.
I've tried to do something like this also:
remove the relatedEntityId property, and use
Entity::setRelatedEntity(new RelatedEntity($relEntId))
the constructor of the RelatedEntity will set the id, but not other values.
I do not want to persist the RelatedEntity (it's values are already set in the DB for the given $relEntId), but this time Doctrine signals an error at flush, because it has an unpersisted entity.
Basically, what I want to do is create a relationship without knowing anyhing but the Id of the related entity. If there is some other way this can be done, please share.
Thanks in advance
EDIT:
I've found a workaround. Since the RelatedEntities will be a limited set of immutable objects, I've done the following:
use the entityManager to find all RelatedEntities;
inject the list to the object that will be creating new Entities
when creating a new Entity, select one of the RelatedEntities from the list as its RelatedEntity
I'll leave the question open for a day or two, just in case somebody comes up with something better.
Use the entity proxy:
Entity::setRelatedEntity($entityManager->getReference('RelatedEntity', $relEntId))
I don't think this is supposed to work like how you described :)
The entity you add must be a Doctrine managed object, so that means you have to load it yourself first using the find() family of methods.
Based on my experience with Doctrine 2 further elaborated here http://ssmusoke.wordpress.com/2012/03/25/doctrine2-day-3-proxies-associations-relationships/
My approach is as follows:
a) Have only the $relatedEntity property
b) Add a getRelatedEntityId() function which returns the id value from $relatedEntity
c) Add a setRelatedEntityId() which sets the $relatedEntity object - you may need to load it from the database, saves you from polluting other layers when u only have the id of the related entity
d) Add getRelatedEntity() and setRelatedEntity() functions
BOTTOM LINE: You cannot have a property for the foreign key column and the mapped property as Doctrine gets confused

Categories