In my Doctrine model I want to have a BasePerson class which has all the database persisted fields.
* #ORM\Entity()
class BasePerson
{
$street;
$number;
...
}
I also want to have a Person class extending from the base person class which has additionally a entitylistener attached. The listener triggers an API call and filling my "Person" Entity with the location data.
* #ORM\Entity()
* #ORM\EntityListeners({"..."})
class Person extends BasePerson
{
$location;
}
For all base operations without needing API calls I want to use the BasePerson class, and for all API related operation I will use the Person class; but in every case I have at least the information coming from the database.
The problem now is, whenever I add the #ORM\Entity() annotation to both entities, Doctrine thinks this should be two tables. I don't want two tables. If I left the entity annotation on one of both entities, doctrine gives me an error, that this entity is unmanaged ...
How can I have it all in one table? I read about single table inheritance but for this, doctrine will manage this with an discriminator column ... but that's not the use case for my intend ...
To have a clean architecture, you should use the same entity and two DTOs. One for the API and add it the attribute "location" and the other for other views of project.
Doctrine (version >= 2.4) can directly return a DTO from a query using "NEW" operator, an example:
<?php
$this
->get('doctrine.orm.entity_manager')
->createQueryBuilder()
->select('NEW Acme\DTO\CategoryListView(category.id, category.title)')
->from('AcmeDemoBundle:Category', 'category');
You can just have some method related to API in your Person class and use them only when needed. I don't see why this would be such an issue.
I don't think you should be using the BasePerson class directly.
I assume Doctrine would not allow the behaviour you're trying to have.
Related
I would like logic like the following:
-> controller- > call to Model class (that extend model with the entity,manager)- > the function in model will call to getRepository() and use funcion that dealing with the DB.
How can I do that? When I call to Repository I must have an entity, and if I have an empty entity the error is that I must have primary column with id.
As the first comment says, you should not call the repository directly from the entity class. You have several options here (descending by the code quality)
Refine the logic to invert the control flow
Define a service, which accepts the entity, the repository and does the stuff
$service = new EntityStuffService($repository);
$service->doTheStuff($entity);
Declare a relation
Just declare a realtion within your class and filter it manually. Doctrine PersistentCollection implements Selectable, so you can just filter it using the ->matching($criteria) call.
http://doctrine-orm.readthedocs.io/en/latest/reference/working-with-associations.html#filtering-collections
The ugliest way
Doctrine has the ObjectManagerAware interface, which allows you to inject the object manager into the entity on hydation completion. This will allow you to implement and kind of AR patters and other spaghetti-code and I strongly recommend you not to do this.
https://github.com/doctrine/common/blob/master/lib/Doctrine/Common/Persistence/ObjectManagerAware.php
You can implement this interface and store the ObjectManager inside the entity to do any kind of operations with it.
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.
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.
I have these entities on my code.
class Review extends BaseEntity {
/** #ORM\OneToOne(targetEntity="Action", mappedBy="review") */
protected $action;
}
class Action extends BaseEntity {
/** #ORM\OneToOne(targetEntity="Review", inversedBy="action") */
protected $review;
}
As you can see it's a OneToOne relationship between Action and Review. My problem is I use soft-delete for my entities as well, so when I delete an entity is not actually removed only a deletion date is set. Later in audit trail I need to show deleted reviews and I also need information from the parent action of course. My question is, do I need to make this relationship OneToMany? or is there a better approach?
To be honest i'm not very found of the soft-delete behaviour. What you need to be aware is that soft-deleting an entity is a strong compromise in a relational database.
You may want to consider in this particular instance an event sourcing approach. I would recommend to store the information about the deletion and the (soft)deleted entity in a document based DB.
Any other approach (like OneToMany) is still fine but keep in mind that the risk here is degrading your relation by introducing a incoherent relationship.
That being said I'm fully aware that real life it's quite different than theory :) good luck.
Regards.
Now I have an model User which represents an user in the application. And I use an UserRepository with methods like getById($id) and getAll().
An user can post reviews. If I want to show the 5 or 10 or maybe 20 last reviews of an user it's most logical to ask the user I think. So I would have a method $user->getLastReviews(5).
But what's the best way to implement this? Give each user an instance of the ReviewRepository (with a method $reviewRepository->getByUser(User $user) for example)? Or are there better ways?
I think it's fine to have models contain and use instances of other models, so your way is fine, to have User model contain an instance of the Review model, and let that Review model handle the logic of getting said reviews.
Also, you could add a method to the UserRepository to handle it like so:
class UserRepository extends Model{
$review = new reviewRepository();
function getReviews($numOfReviews){
return $this->review->getReviews($user_id);
}
Another option would be to create a repository method where you passed in both variables. Something like $reviewRepository->getLastReviewsByUser(5, $user).
Usually this is a job for the ORM. Almost every framework uses one implementation (ie. Doctrine for PHP/Symfony or Hibernate for Java) but naturally you can implement your own ORM (ORM are often implemented using introspection).
Once you have an ORM library you define relations between Models in a "setup phase" (in your case you'll have "user has many reviews"). Then you'll use the ORM methods which knows how to deal with those ones (often relations are mutual ie. "review belongs to user"). The concept is that this setup phase will discharge you from dealing with issues like the one you pointed.
My suggestion is to use one of the already existing ORM implementations which already supplies facilities for getter and setter methods of related Models. In the other case, you have to write specialized getters and setters by yourself for every Model.