Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.5] Automatic self referencing relationships on model getAttribute #22401

Closed

Conversation

brysem
Copy link

@brysem brysem commented Dec 12, 2017

This pull request aims to improve Eloquent performance when accessing relationships as an attribute on a model by setting the originating relationship on a newly loaded relationship automatically.

Let's take the following two models as our example.

class User extends Eloquent
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

class Post extends Eloquent
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

In the following situation, we will be executing unnecessary queries.

$user = User::first();

$posts = $user->posts;
$post = $posts->first();

$postUser = $post->user;

The above will execute a new database call to fetch the user when accessing its relationship as an attribute on the Post model instance.

This pull request will automatically set the relationship user on all posts.
In other words $user and $post->user in the example above, are exactly the same object instance.

This is particularly useful when passing post models to functions or methods elsewhere in your code where you need to access the original user. Before this pull request, a new query would be executed for every $post->user call.

An example where the same effect can be produced without this PR.

$user = User::first();

$posts = $user->posts;
$posts->each($post, function() use ($user) {
    $post->setRelation('user', $user);
});

$post = $posts->first();

$postUser = $post->user;

Bryse Meijer added 2 commits December 12, 2017 22:53
Signed-off-by: Bryse Meijer <bryse.meijer@atabix.nl>
Signed-off-by: Bryse Meijer <bryse.meijer@atabix.nl>
@brysem brysem changed the title Automatic self referencing relationships on model getAttribute [5.5] Automatic self referencing relationships on model getAttribute Dec 12, 2017
@sisve
Copy link
Contributor

sisve commented Dec 12, 2017

This implementation will fail for anything more complex than the provided example. What if Post has two user-relations (maybe an author and a last-edited-by reference), how would this work?

You would need to look at all of Post's relations and verify that the column for the Post relation (BelongsTo's getForeignKey()) matches the User relation column (HasMany's getForeignkey()) to make sure you've found the correct relation. This, however, is probably impossible since you would need to use reflection to iterate through all model methods, invoke them and check if they return any relationship objects...

It makes more sense to me (a Doctrine user) to have a cache (what Doctrine calls a identity map) that simplified is a $cache['User:1'] = $user;, and at every automatic lazy-loading we check this cache before issuing database requests. This functionality, however, is not present in Eloquent, and I'm not even sure it would fit into an Active Record setup either.

So...

  1. We cannot guess the relation key. We could either get wrong relation (which is really bad) or just not finding the relation at all.
  2. The new HasRelations::hasRelation method is a lie. It is in no way trustworthy, I bet it would even claim that __toString is a relation...
  3. The new Collection::hasRelation cannot be trusted; it only checks the first model in the collection. The docs is also misleading, it states that it "checks whether a collection has a relationship on it", but collections never have relationships.

May I suggest that you abandon this current implementation and start a new discussion over in https://github.com/laravel/internals to discuss what you want to build and the problems you run into?

@taylorotwell
Copy link
Member

This has been suggested before and while the concept as a whole is interesting I haven't seen an implementation I was thrilled with. I know Rails tried an identity map on their ActiveRecord before and ended up pulling it out entirely if memory serves me right.

@brysem
Copy link
Author

brysem commented Dec 13, 2017

Thank you @sisve for the comments and feedback. It is really appreciated. The feature was meant to only work for guessing singular named relationships that follow the pattern of naming the relationship after a model. While this use-case may or may not be very limited, it offers some additional performance boosts when often forgotten.

Hi @taylorotwell, interesting to hear about Rails. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants