When would it be worth it to maintain an inverse relationship in Doctrine2?
Asked Answered
B

2

13

In the Doctrine manual, under Constrain relationships as much as possible, it gives the advice "Eliminate nonessential associations" and "avoid bidirectional associations if possible". I don't understand what criteria would make an association "essential".

I say this because it seems that you would often want to go from the One side of a One-to-Many association rather than from the Many side. For example, I would want to get all of a User's active PhoneNumbers, rather than get all active PhoneNumbers and their associated User. This becomes more important when you have to traverse multiple One-to-Many relations, e.g. if you wanted to see all Users with a MissedCall from the last two days (MissedCall->PhoneNumber->User).

This is how the simple case would look with an inverse association:

SELECT * FROM User u
LEFT JOIN u.PhoneNumbers p WITH p.active

It would make it more sensible if there were a way to go across a given relation in the opposite direction in DQL, like the following raw SQL:

SELECT * FROM User u
LEFT JOIN PhoneNumber p ON p.User_id = u.id AND p.active

Can someone explain why they give this advice, and in what cases it would be worth ignoring?

-- Edit --

If there are mitigating factors or other workarounds, please give me simple example code or a link.

I do not see any way to traverse a relation's inverse when that inverse is not defined, so I'm going to assume that building custom DQL is not in fact a solution -- there are some joins that are trivial with SQL that are impossible with DQL, and hydration probably wouldn't work anyway. This is why I don't understand why adding inverse relations is a bad idea.

Boote answered 10/8, 2011 at 0:20 Comment(0)
E
4

Using Doctrine, I only define relationships when they're needed. This means that all of the relationships defined are actually used in the codebase.

For projects with a large team working on different areas of the project, not everyone will be accustomed to Doctrine, it's current configuration, and eager/lazy loading relationships. If you define bi-directional relationships where they aren't essential and possibly don't make sense, it could potentially lead to extra queries for data that:

  1. may not be used
  2. may have been selected previously

Defining only essential relationships will allow you greater control over how you and your team traverse through your data and reduce extra or overly large queries

Updated 22/08/2011

By essential relationships, I mean the ones you use. It doesn't make sense to define a relationship you wouldn't use. For example:

  • \Entity\Post has a defined relationship to both \Entity\User and \Entity\Comment
    • Use $post->user to get author
    • Use $post->comments to get all comments
  • \Entity\User has a defined relationship to both \Entity\Post and \Entity\Comment
    • Use $user->posts to get all user posts
    • Use $user->comments to get all user comments
  • \Entity\Comment only has a relationship to \Entity\User
    • Use $comment->user to get author
    • Cannot use $comment->post as I don't retrieve the post it belongs to in my application

I wouldn't think of them as "Inverse" relationships. Think of them as "Bi-directional", if using the data in both directions makes sense. If it doesn't make sense, or you wouldn't use the data that way around, don't define it.

I hope this makes sense

Euler answered 20/8, 2011 at 2:54 Comment(1)
Thanks for taking the time to answer my question. Can you give some guidelines for what you would consider an "essential" relationship? Should I add an inverse relation as soon as I write a query that requires it, or is there a way to work around the need for an inverse relation?Boote
T
2

I think this is a great question, and am looking forward to others' answers.

Generally, I've interpreted the advice you cited in the down to the following rule of thumb:

If I don't need to access the (inverse) association inside my entity, then I typically make it unidirectional. In your example of users and (missed) calls, I'd probably keep it unidirectional, and let some service class or repository handle putting together custom DQL for the odd occurrence when I needed to get a list of all users with recent missed calls. That's a case I'd consider exceptional -- most of the time, I'm just interested in a particular user's calls, so the unidirectional relationship works (at least until I've got so many records that I feel the need to optimize).

Thicken answered 10/8, 2011 at 0:40 Comment(1)
What would your custom DQL for all users with a missed call be, if you don't maintain the inverse relations? I couldn't figure out how to go that direction.Boote

© 2022 - 2024 — McMap. All rights reserved.