Multi level getters in symfony2/doctrine2 - php

Im wondering if theres a cleaner way to approach the system i have below, using symfony2/doctrine2.
I currently have three entities
Entity A - many to one relationship with class B, one to many with class C
Entity B - one to many relationship with class A, one to many with class C.
Entity C - Many to one relationship with class A and B.
If i do $entityA->getEntityB()->getEntityC() that will return me all the C entities assigned to entity B, but what i actually want is all the entity C entities that are assigned to both entity B and entity A. Essentially i want to recognise the getter chain, if that makes sense.
At the moment i have to pass entity A into the getEntityC method and filter out the values i dont want which is starting to get a little messy when dealing with more objects and other parts of code.
Is there a way to set this up whereby the last getter will force the relationship from both parents instead of just the immediate one?
Any help would be much appreciated.

I would much recommend not to try to get value by getters, but instead create a query by DoctrineQueryBuilder and use multiple left joins. For example something like this:
$repository = $this->getDoctrine()->getRepository('GreatBundle:Entity');
$q = $repository->createQueryBuilder('e1', 'e2', 'e3')->leftJoin('e1.e2', 'e2')->leftJoin('e1.e3', 'e3');
$columns = array(
'e1.id',
'e2.id as e2_id',
'c3.id as e3_id',
);
$result = $q->select($columns)->getQuery()->getArrayResult();
Sorry that I didn't fully get your usage case, but you will have to recreate query just to fit it.

Related

Different implementation of Eloquent instance for the same table

I'm currently working a tournament organization project, I would like to what's the best technique to have different implementation for the same model, for example I have model called matches, than retrieves data related to matches, but these matches may have different types depending on match_type field specified in matches model, what I did is creating parent class /Match , and having different types of matches extend this parent class. Ex:
Class SinglElimination/Match extends /Match{}
Class GroupStage/Match extends /Match{}
so with this design I have to get the parent match first to get the match_type then re-run the query to get a match with the needed child model
$match=Match::find($id);
$type = $match->match_type;
$childMatch = new $match_type.'/Match'();
$match = $match->where('_id', $this->_id)->first();
which I know is nowhere near clean, so how would you implement this ?
If I were you I would separate those 3 classes and exclude any extending. Take a look at Polymorphic relationships in Laravel, here is the quick link. It will be a cleaner approach and in my opinion it would be the best approach here, all you'll have to do is design the tables properly and do relationships properly too.

How to populate additive field via JOIN with Doctrine

Suppose i have an entity with a ManyToOne relation (with EXTRA_LAZY) and obviously a join column.
eg. Article (main entity) -> Author (external entity)
suppose i add a field author_name to the Article that NOT mapped to the ORM and that in certain contexts this field should contain the article->author->name value (usually it can stay null).
When i query a list of articles, i would like to populate automatically that article-author_name without implementing a getter that perform a select on each element of the query result. I would like Doctrine to fetch and hydrate that value in with a simple JOIN in the original query...
I don't want to set fetch mode to EAGER for performance as in my project Author is a really complex entity.
Is this possible?
Thanks
Probably i've found a solution. Don't know if it is the best way to do it but it works.
I've added to the main class a field getter
public getAuthorName() {
return $this->author->name
}
in my context this getter will only be called by a serializer in certain conditions.
In my repository code i have a special method (that i refactored so that any method could implicitly call it) to impose the population of the Article->author field when queried. The method simply use the querybuilder to add LEFT JOIN to the Author class and temporarily set FetchMode to EAGER on Article->author.
At the end a simple repository method could be this
public findAllWithCustomHydration() {
$qb = $this->createQueryBuilder('obj');
$qb->leftJoin("obj.author", "a")
-> addSelect("a"); //add a left join and a select so the query automatically retrive all needed values to populate Article and Author entities
//you can chain left join for nested entities like the following
//->leftJoin("a.address", "a_address")
//-> addSelect("a_address");
$q = $qb->getQuery()
->setFetchMode(Article::class, "author", ClassMetadata::FETCH_EAGER);
//setFetchMode + EAGER tells Doctrine to prepopulate the entites NOW and not when the getter of $article->author is called.
//I don't think that line is strictly required because Doctrine could populate it at later time from the same query result or maybe doctrine automatically set the field as EAGER fetch when a LEFT JOIN is included in the query, but i've not yet tested my code without this line.
return $q->getResult();
}
The con is that you have to customize each query or better use a DQL / SQL / QueryBuilder for each method of the repo but with a good refactoring, for simple inclusion cases, you can write a generic method that inject that join on a field_name array basis.
Hope this help and add your answer if you find a better way.
PS. i've wrote the above code on the fly because now i'm not on my notebook, hope it works at first execution.

Php: use data from a join table with mvc

Heya I am novice web dev or actually I am still in education.
I got this situation Where I have 3 tables lets say : Students, Groups and a join table Student_group.
I put my data from Students in the student model and from groups I put its data in the Group Model so I can use it my application. But I store a date in the Student_group table because I need to know when a student changed from a group.
So my question is in which model do I put this date? Do i need to make a new model for the combined tables or do I need to add another attribute to the student model?
Thanks in advance ;D
That depends. Will the student be in many groups, or one?
If one (one to one relationship), you can decide where to put it. The column could be in either the Student table, or the Student_group. In this case, though, it may be advisable to flatten the data and simply add group columns in your Student table. You decide that as well - if it seems unnecessary to have a join for a one to one relationship (usually it is, not always), then flatten it. In either case, the data should stay in its respective model. That said, you should use the Student model if you handle it in the Student table.
If many (one to many relationship), I'd advise putting it in the Student_group table and leaving it in that model as well.
All in all, the model should be a direct reflection of the data it's representing. You could make some methods inside your Student model to make it easier to get the date, for example. However, I'd personally handle that date inside of the proper model, Student_group. As mentioned, the model should be a direct representation of the data. Again, though, there's nothing wrong with making things a bit easier by creating some methods that help out the developer.

Doctrine Inheritance and MySQL Join Table Limit

I have a problem with 61 join table limit of mysql. I have 57+ different classes extending Base Class which contain association to comments, likes, tags. And MySQL is crashing when i get most commented. Doctrine has to join whole discriminator maps and comments itself and order by COUNT(comments).
Is there way to fix that ?
And is there another way to achieve comments for different types of entities without inheritance and copying same association all over again?
Here is sample schema of Entities. When I want to add new Entity Type with comments,likes I just extends BaseClass to receive these features.
If I understand correctly what you're trying to do is have many different entity types which can be commented on.
First thing to do would be to step back from Doctrine and thing about the simplest table structure you would need to accomplish this.
Something like this may suffice:
Comments
_______________________
| id | type | entity_id |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
It is nice in Doctrine to have bi-directional relationships in your base class for convenience but sometimes they are not the best choice. Maybe it would simplify your architecture to perform a query directly on the comments table by entity type and id.
You may also want to consider removing the Base class and having each entity be standalone.
Since a blog post can exist in a context where it does not have comments (on a blog that doesn't allow commenting for example) then $blog->getComments() wouldn't make much sense.
Making this change you could do something like this instead:
$comments = $commentsRepository->findCommentsForEntity($entity);
$commentsCount = count($comments);
and the repository could generate the needed query passing the entity as the entity_id parameter and setting the required comment type based on the entity type.

Interface or type-hint for single object and multiple object class in php

Recently started working with OOP in PHP. Following the "code to an Interface" principle, i got confused as to the type hint to use when passing a single object or multiple as argument to a method.
Currently, i have a "Student" class - represents a row in my students table, i also have a "Students" class that holds multiple student objects in an array.
To fetch the profile of one student, i pass the Students object (holding a single student object) to the profile class. I set a Students type hint in the profile class.
Now i feel this is bad code as i have lines like this
student = new Students();
and students = new Students();
question is,
am i on the right path?
if i remove the Students class and work with Student alone, based on the principle, how do i pass multiple Student objects (assuming array) to the profile class if it accepts a Student type hint?
what options do i have?
Thanks.
If by Students you mean a collection of Student objects, perhaps a better name would be StudentCollection or StudentSet.
There are two ways around the type hint problem:
Introduce a method on StudentCollection called ->getProfiles(); it would return an array of profiles for each Student instance it's managing by calling methods on Profile.
Introduce a (static) method on Profile that operates on a StudentCollection instance.
The first option has feature envy, which is why I've included a workaround.
Instead of reinventing the wheel you might want to try Doctrine or at least take a look at its architecture.
I'm not sure if I get your exact issue... But if you want to go for your own code I would first abstract the DB layer as well and have some base classes like Database, Table, Row, Field that an describe the DB stack and extend them as needed with some magic methods. So when you do Student extends Table it would automatically check for a "students" table or whatever else convention you like to implement. Alternatively you could just pass the table name as arg.
Whatever Object is returning the result set from the database would have to construct a single Row object for each row and add it to a collection of rows that I would name ResultSet and contains all the row objects and return that collection.

Categories