Symfony2 - abstract entity only for inheritance, without DB table? - php

In Symfony 2.8 I have some entities which have the same fields, but for the purpose of data organisation I need them to be separate entities. Is it possible to create one abstract base class with all the fields, getters and setters and then just create new entities extending base abstract class, repeating as few as possible?
Example:
Let's say I have Movie entity, which has some people connected. People have different roles (actor, director), but let's assume they have only FirstName and LastName. I don't want to keep them in one table (with roles) for data separation purposes.
So I'd like to create one abstract Person entity, which will have FirstName, LastName, getters and setters. And then I'd like to create empty (as empty as possible) classes Actor and Director, extending Person and let Doctrine take care of the rest, so in DB I should have actor table with all the Person fields.
Is it possible to achieve something close to what I want?

It just seems like basic inheritance. Since you do not want to persist the Person entity but those which inherit from it you can define your base Entity defining Doctrine statements at everything you want to persist but the Entity itself.
Then on the child Entity you define the rest of the fields you may want to persist, configure them to work with Doctrine and on the Child Entity annotations you define the table that is going to persist that particular entity + telling the Entity to inherit its parent Doctrine configuration.
Then you will have both the parent and the child Doctrine configuration and only the table configuration on the child so every child will be persisted on its own table.

Related

Role in Cycle ORM

In cycle orm docs some mappings have such thing as role. Text states that is it entity role (well, not informative at all). What does that mean and why do we need this roles stuff? Doctrine ORM for instance doesn't have such thing.
You can treat role as alias to class name in Doctine. Unlike Doctrine Cycle ORM can map any type of objects, including StdClass or custom models. In order to identify mapping between same class instances it uses roles.

Inserting data with Doctrine - Repository Vs Entity

I've realized there is a difference between a Doctrine repository and Doctrine entity.
I'm trying to implement simple CRUD actions on a table and was injecting a default Doctrine repository into my controller (without injecting an entity).
For the "Update" action I would first ->find($id) for the record to update and it would return an instance of the entity for me to bind to my form object.
For the "Create" action I realized I can't ->find($id) a record to insert (since it doesn't exist) in order to retrieve an instance of the entity for me to bind to my form object.
Is there is an alternate way to insert data using Doctrine without an instance of an entity? Or is there a way to retrieve an instance of the entity from the repository so I can ->bind() it to the form? If the answer to both are no, then I imagine my only options are to inject an instance of the entity to my controller, or to use a custom repository which contains a method which would return an entity to use in the ->bind() for insertion.
My guess would be to define a custom repository which has a method which retrieves an empty entity instance for use in insertion. Is this assumption correct?
As pointed by #Crisp in comments, Entities are no more than PHP classes, same for Repositories.
The two are differentiated by their respective role.
You'll never implicitly create a new instance of a Repository because doctrine do it for you across DependencyInjection principles (Service, Factory, ...).
To create a new database entry, you must create a new instance of the corresponding entity, then store it using EntityManager::persist and EntityManager::flush methods.
Reuse the same instance of an entity would not give you any benefit, nor make any difference in your project's maintainability.
The entity class itself will never be broken/changed, only instances of them are created, renamed, moved, deleted.
These instances represents your database entries, this is the primary interest of use an ORM.

Symfony MappedSuperclass store Entity reference

I'm currently writing a MappedSuperclass to store entity reference on an entity object.
For example to store favorites for a user, it can be any kind of model (product, category of product or whatever).
The class contains deux fields :
entityType : the class of the entity reference
entityId : the id of the entity reference
Favorite entity class will extends this Superclass, and that's work.
I'm would like to know, if there is another (cleaner?) way to do this ?
Thanks for your inputs.
As we work a lot currently with Doctrine mapping, this all depends of your buisness logic and what you want your database looks like.
If using a mappedSuperclass, basically, this entity will not appear in your database. Only entities that inherits from that mappedSuperClass will appear with its own properties and the superClass properties.
Another approach could be to use inheritance joined type that will generates this time the class in your database, event it is abstract.
Usability of your schema, even in mappedSuperclass or JOINED type, do not changes a lot of things, it's all about your preferences and your logic.
I personaly prefer the second method, as it permit to structure more your data.
I hope this helps.
Best Regards.

Doctrine ORM and factories with abstract classes strategy

So I've stumbled upon this hurdle where I have to create an abstract class and a factory to create objects of more specific classes that extend the abstract class and implement more specific object methods.
Simply said, I got a SocialMediaAbstract class. Extending classes are Facebook, Instagram, and they implement a SocialMediaInterface. Facebook, Instagram etc are all saved in the db, with an id, a name and several more properties that are all used among the extending classes, hence an abstract class.
Because I want to be able to query several things from the SocialMedia Objects, and every social media platform have their own APIs for it, I made the interface and created the different classes so they can all have their own implementations of those methods.
Now, the problem is of course with my abstract class and Doctrine. Doctrine says this on their website regarding inheritance:
A mapped superclass cannot be an entity, it is not query-able [...]
Now if I had a SocialMediaFactory and threw in an ID, I would like to get the respective Object of, for example, class Facebook or Instagram back. I don't want to know exactly which SocialMedia it is when I collect them. Now that is a problem with doctrine, at least that's what I think it is.
Am I overlooking something, is the factory pattern still possible? Or should I really just remove the abstract class, and create a factory that searches in every table of a SocialMediaInterface implementing class, which seems highly inefficient and unmaintable when an application gets bigger.
Any insight or pointers would be appreciated, since I'm sure this problem must've come up more often. I tried googling and searching on Stackoverflow itself, but I couldn't get any relevant questions or answers.
Thank you very much in advance.
EDIT:
I came across this interesting possibility: Class Table Inheritance. This would mean adding:
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"facebook" = "Facebook", "instagram" = "Instagram"})
to my code. I had high hopes, but sadly enough the validator gave me this error:
[Doctrine\ORM\Mapping\MappingException]
It is not supported to define inheritance information on a mapped superclas
s 'Portal\SocialMedia\Entity\SocialMediaAbstract'.
A shame mapper superclasses are not supported.
EDIT 2/CONCLUSION:
I've decided to go with Class Table Inheritance (just like the answer below suggested). Removing the abstract from the class made it possible to still use my factory.
I am using a concrete class as an abstract class now however, which feels wrong. I've documented in docblock that no objects should be instantiated from this class.
One little sidenote: Doctrine's Entity Manager more or less already provides the Factory:
$socialMedia = $entityManager->find('Portal\SocialMedia\Entity\SocialMedia', 2);
This returns an Instagram object. I still suggest you build your own factory above it for maintainability later as the SocialMedia entity might change later on.
Some time has passed now since I worked with doctrine, but if I remember correctly, doctrine's mapped super classes are an implementation of the concrete table inheritance pattern by Martin Fowler.
In the example mentioned there, the Player is the mapped super class, whose attributes are distributed to all inheriting entities / models. The point here is that a player can't be instantiated and thus has no own id. Instead, every inheriting model got it's own id, which are all independent of each other.
I think the pattern you are looking for is either single table inheritance or class table inheritance (have a look at doctrine's inheritance types).
Single table inheritance is implemented in doctrine's inheritance type "SINGLE_TABLE", where you have one table for all entities. They are sharing the exact same attributes and same id pool, meaning you can "throw in" an id, get the object and check the type (Facebook, Instagram etc..).
The downside is that if you got in any of the entites an attribute that may be NULL, you could run into problems if the other entites don't have this attribute or don't need it. This would mean you have to set the given attribute to a dummy value in the other entities to save them into the database table.
Class table inheritance overcomes this issue by saving every entity in its own table, while still being able to share the id pool, because doctrine takes care that the common attributes are saved in the base class table, while all the attributes specific to an entity are saved in the entity's table. The tables are then joined by the id, hence the inheritance type "JOINED" in doctrine.
Conclusion:
Use single table inheritance if the classes are very similar and only differ in function definition or implementation, but have the same attributes.
Use class table inheritance if the classes have distinct attributes that would be problematic to store in a single table.
Use concrete table inheritance if the classes are not really related to each other, but only share a small amount of common attributes. But this could also be implemented through PHP's traits, which in my opinion is easier and more flexibly to use than doctrine's mapped super class. In a PHP trait you can also use doctrine's annotations, because the PHP interpreter will properly assign the annotations to the classes you use the traits in.
You should still be able to use your SocialMediaFactory with either single table or class table inheritance pattern.

Pros and cons of a global base object for all objects in Doctrine2

What are the pros and cons of using a global base entity for all entities in my Doctrine2 model?
I am considering having all of my domain entities extend a single generic base entity, so that I can specify the target entity of an association to be the base entity class, thereby allowing that association to hold any entity in my domain.
My associations will still be as specific as possible: an Order entity will contain Product objects, not Base objects. But more 'meta'-like functionality like status log items, authorization specs, etc., can be specified very generically by defining LogItems and AuthorizationRules having an association to BaseEntity, instead of with specific items.
I am not seeing this approach anywhere, but there seems to be some use or power to having all classes extend off a base Object entity ('Java-style'). But perhaps I am needlessly overcomplicating things.
Is a base entity in Doctrine2 a good idea?
You have it all described here:
http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html
You can do inheritance from the OOP point of view(php) and map it to database(you can have persons and employees that are persons as well(or people)) but you cant have the database understand the inheritance(it's done in the db abstraction layer(doctrine)).
So each class you might want to have in relation, has to be and entity, meaning that both a person and and employee have to be entities. If you do it as single table inheritance, you could relate and entity to "Person" and then it would include employees also.
http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance
http://martinfowler.com/eaaCatalog/singleTableInheritance.html

Categories