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

Eager Loading Polymorphic Relation's Related Models #6368

Closed
MovingGifts opened this issue Nov 9, 2014 · 9 comments
Closed

Eager Loading Polymorphic Relation's Related Models #6368

MovingGifts opened this issue Nov 9, 2014 · 9 comments

Comments

@MovingGifts
Copy link

Hi @taylorotwell,

I can eager load polymorphic relations/models without any n+1 issues. However, if I try to access a model related to the polymorphic model, the n+1 problem appears and I can't seem to find a fix.

Here is an easily reproducible example I put together: http://stackoverflow.com/questions/26727088/laravel-eager-loading-polymorphic-relations-related-models/26825205#26825205

I am sure the laravel community would love to be able to eager load those polymorphic models related models using the same familiar syntax as this: $histories = History::with('historable.company')->get(); where company is a polymorphic models related model.

For now, I went with the accepted stack overflow answer since calling it like that was not possible.

Please let us know your thoughts on this.

@GrahamCampbell
Copy link
Member

Does anyone have anything to say here?

@damiani
Copy link
Contributor

damiani commented Dec 28, 2014

The question and accepted answer on SO outline the issue clearly—it would be useful to be able to eager load distant relations of a polymorphic model, either using the dot syntax (preferably) or some by some other means. It is possible to do now, but only with a hack involving extra models and accessors.

Or, at the very least, if adding this functionality isn't practical, then trying to chain relations when using a polymorphic model should throw an exception. At the moment, the first relation is eager loaded as expected, but the model after the dot is simply ignored.

@YOzaz
Copy link

YOzaz commented Jan 19, 2015

👍 Eager load should care about relationship type - is it 1 to 1, N to N, or polymorphic, and would load it correctly. Calling with('imageable.file') looks quite logical - and even if some associated model doesn't have "file" relationship, if can have either an exception, either null returned (I would prefer latter).

@RomainSauvaire
Copy link

Hi, I just bumped into this problem and my first "need" would be at least a raised Exception. Any news on that proposal ?

@pdiddy
Copy link

pdiddy commented Feb 21, 2015

+1 for being able to eager load a polymorphic model's nested relations using dot notation

@GrahamCampbell
Copy link
Member

Closing due to 6 months of inactivity and the PR being rejected.

@20TRIES
Copy link
Contributor

20TRIES commented Apr 30, 2016

Hey

I've just come across this issue and am having similar problems.

What i need to be able to do is load nested relations on a morphTo() relation.

For example, i have the following model:

class Tag extends Model
{
    public function model()
    {
        return $this->morphTo();
    }
}

The model() relation may return many different types of model, one of which is a User.

I need to be able to load the "profile" of a user in the normal ->with() way.

For example:

$results = Tag::with('model.profile')->get();

Currently this would just ignore the profile relation.

The workaround i have found is by customising the "getResultsByType" method of the MorphTo relation object as follows:

 protected function getResultsByType($type)
    {
        $instance = $this->createModelByType($type);

        $key = $instance->getTable().'.'.$instance->getKeyName();

        $query = $instance->newQuery();

        $query = $this->useWithTrashed($query);

+        $dynamic_eager_loads = $this->query->getEagerLoads();
+
+        $dynamic_eagers_filtered_to_model = [];
+
+        foreach ($dynamic_eager_loads AS $relation => $constraints) {
+          $components = array_filter(preg_split('/\./', $relation));
+          $related = $query->getModel();
+          foreach($components AS $method) {
+             if(method_exists($related, $method)) {
+                 $related = ($related->{$method}())->getRelated();
+                 continue;
+              } else {
+                  $related = null;
+                  break;
+              }
+            }
+            if(!is_null($related)) {
+                $dynamic_eagers_filtered_to_model[$relation] = $constraints;
+            }
+      }
+
+       $default_model_eager_loads = $query->getEagerLoads();
+
+       $eager_loads = array_merge($default_model_eager_loads, $dynamic_eagers_filtered_to_model);
+
+       $query = $query->setEagerLoads($eager_loads);

        return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
    }

References to this model can then be overridden in the base Model class so that the custom relation is used.

This enables me to load nested relations on morphTo() models. Maybe others will be able to use this to solve their own issues.

If you agree with the way this works and are interested in implementing this functionality, i can make it into a pull request @GrahamCampbell / @taylorotwell

@OskarPersson
Copy link

OskarPersson commented May 17, 2016

@20TRIES I just tried this with my project as well and it works as expected with nested polymorphic related models. Don't know if it messes up things in other places though.

@phroggyy
Copy link
Contributor

@20TRIES @OskarPersson I fixed this in #13737, tagged in 5.2.34

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

No branches or pull requests

9 participants