I have written quite a large and complicated query that internally uses a UNION to select from multiple tables, then returns an array of mixed type entities.
I know that best practises in Symfony say to always put the queries within the repository classes, but how do I decide which to put it in? There's no parent/child relationship between them, the two entities are completely equal.
I usually put them in the repository which I consider the most dependent entity in the context.
For instance, if I had two entities: User and Group.
Many entities might have an owning relationship with group, but you can't expect the Group repository to single handedly provide the methods necessary for every specific dependent to function.
It is the responsibility of the dependent (the owning side) to make the connection and hense provide the functionality.
So a method like getUsersInGroup(Group $group) would belong in the UserRepository.
However, you said there are no direct relationships between your two entities.
In this case, my first comment applies. Use the repository whose entity is more dependent on the other within the context of the query. Whichever entity that one is, depends entirely on you.
Do not use a repository. Repository is bounded to the context of the specific entity type, and it is supposed to be used as a Collection.
Also
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
(M.Fowler)
And in your case I assume there is no Object-Relational-Mapping, given that you are using a query with a UNION of tables. I assume that your query will not return an actual Doctrine entity, right?
In that case, the query doesn't belong to the Repository pattern, and I suggest you to have a custom class where to encapsulate the big/complex query PHP+SQL code, e.g.:
namespace App\Query
class MyComplexQuery {
(optionally implementing QueryInterface)
and call it from your Controller or Service, without passing from the Repository.
In case you are defining a custom Doctrine Entity to represent the results of your UNION of entities, then use the repository of such entity.
Related
Imagine the following situation. You have books:
Book(bookId, authorId, title)
and authors:
Author(authorId, name)
and each book has (for the sake of simplicity) a single author.
By default all associations are configured in lazy mode. So, if I have the scenario, when I first load all books, iterate over the collection and fetch author of each book, I'll perform lots of queries to the database.
$books = $this->getDoctrine()
->getRepository('AppBundle:Book')
->findAll();
foreach($books as $b) {
echo $b->getAuthor()->getName();
}
Can I programmatically ask Doctrine to load authors eagerly for this specific query (not globally via configuration)?
Related: In Doctrine 2 can the Fetch Mode (Eager/Lazy etc.) be changed at runtime?
Related: http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/getting-started.html#list-of-bugs
You can simply mark the association between books and authors as EAGER (versus the implicit default of LAZY) and Doctrine will always load that particular association up front.
This can be accomplished by adding:
fetch=EAGER
to the mapping association.
One potential method to do this at runtime would be to create a Mapped Superclass. The superclass would define your relationships and other parts of your associations (not the relationship you're trying to adjust).
Then, to actually use the class at run time, you could create two other concrete implementations: LazyBook and EagerBook. Depending on your scenario at runtime, you would use one or the other of these concrete implementation entities to construct your associations.
Of course, LazyBook would define your Book -> Author association as a LAZY one (either explicitly or implicitly) and EagerBook would define it as EAGER.
This isn't truly dynamic as you've defined, but it allows you to programmatically determine which association to use at any given time while also self-documenting that it could be either.
One very important thing to understand here, is that Doctrine uses the Data Mapper pattern and not the Active Record pattern (you can find it in the Yii framework for example):
Doctrine 2 is an object-relational mapper (ORM) for PHP 5.4+ that
provides transparent persistence for PHP objects. It uses the Data
Mapper pattern at the heart, aiming for a complete separation of your
domain/business logic from the persistence in a relational database
management system.
The benefit of Doctrine for the programmer is the ability to focus on
the object-oriented business logic and worry about persistence only as
a secondary problem. This doesn’t mean persistence is downplayed by
Doctrine 2, however it is our belief that there are considerable
benefits for object-oriented programming if persistence and entities
are kept separated.
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/tutorials/getting-started.html#what-is-doctrine
That essentially means, that entity classes do not know a single thing about how they are persisted to the database. Even though they can have comment type annotations on them, those are just a form of metadata processed by ORM.
In turn that means you can do the very same thing you did with ActiveRecord, but it's now done at just another place. Let's take a look at the difference:
In ActiveRecord-based ORM (like Yii):
$books = Book::model()->with('author')->findAll();
In DataMapper-based ORM (like Symfony/Doctrine):
$books = $this->getDoctrine()->createQueryBuilder()
->select(['b', 'a'])
->from('AppBundle:Book', 'b')
->join('b.author', a')
->addSelect('a')
->getQuery()
->getResult();
Small comment on the later. The query you are building there is not an SQL query, but rather a DQL query (object-query-language employed by Doctrine).
So join/addSelect in here is much like with at the former query just telling ORM engine that you would like to load author at the same query. Particular relation metadata (e.g. column names for both underlying tables) is still defined out there at the entities metadata level.
Syntax (select, from, join) resembles the SQL on purpose, but you shouldn't be confused by it. Here, building the query, you operate ORM entities and not the database columns/tables.
For DDD in PHP, how do we exactly protect methods on an entity in an aggregate that is not the root? How do we protect them from being used outside the aggregate (since only methods on the aggregate root should be used)?
Bonus: Also, is it normal to have duplicated entities? I mean, where say an identical product entity class is found to be a child in two seperate aggregates? Both these product classes have the same behaviors/rules for working with the domain concept of a "product". But I duplicated the class because, again, entities shouldn't be accessed outside of their aggregate.
You don't expose those entities, basically your AR shouldn't be able to return an instance of it. But in practice, I didn't have this problem mainly because my aggregates are 99% made from 1 entity + bunch of value objects.
Bonus: Your aggregates define concepts. Entities and value objects are just technical terms for objects that do have business meanings, so they are implementations of concepts. The concept of a Product should be unique per bounded context.
When another concept somehow references (that doesn't mean it contains) Product, it actually deals with the Product id, which again is more of a technical implementation detail. From a business point of view, you have one unique concept (aggregate) per context.
Since an aggregate should contain only objects that define the concept, having duplicate entities used 'inside' an aggregate is 99.99% sign of improper modelling.
An aggregate is not just a group of entities, where one entity acts as a container for others. That's 100% wrong. An aggregate consists of all objects needed to properly define the business concept. An aggregate root is Never a container, it's the object in charge of maintaining the aggregate consistency (that's why you have to work only with it).
For parents holding children there is the database and the repository pattern.
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 develop an application that allows to access two different ERP systems. I want to abstract away some differences, such that I can search the same entity in both systems and represent a list that contains entities of both (e.g. a list of invoices). I'm thinking about how to architect this.
One approach is using the ActiveRecords pattern, have a Document class that extends from ObjectModel and then create two subclasses that each contain the database field to object property mapping. I feel it is cumbersome, because these object then represent a single instance of a Document, and still need to handle queries that result in a collection of documents.
I never touched doctrine2, but as I'm reading about Active Record vs Data Mapper pattern, it seems that it could bring a solution to my problem. Therefore my question:
Is it possible to have one object (here Document), and link two data mapper configurations to it, one for each data source ?
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