Laravel - Eager Loading Polymorphic Relation's Related Models

前端 未结 5 2191
轮回少年
轮回少年 2020-12-12 18:07

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

5条回答
  •  独厮守ぢ
    2020-12-12 18:57

    As João Guilherme mentioned, this was fixed in version 5.3 However, I've found myself facing the same bug in an App where it's not feasible to upgrade. So I've created an override method that will apply the fix to Legacy APIs. (Thanks João, for pointing me in the right direction to produce this.)

    First, create your Override class:

    namespace App\Overrides\Eloquent;
    
    use Illuminate\Database\Eloquent\Relations\MorphTo as BaseMorphTo;
    
    /**
     * Class MorphTo
     * @package App\Overrides\Eloquent
     */
    class MorphTo extends BaseMorphTo
    {
        /**
         * Laravel < 5.2 polymorphic relationships fail to adopt anything from the relationship except the table. Meaning if
         * the related model specifies a different database connection, or timestamp or deleted_at Constant definitions,
         * they get ignored and the query fails.  This was fixed as of Laravel v5.3.  This override applies that fix.
         *
         * Derived from https://github.com/laravel/framework/pull/13741/files and
         * https://github.com/laravel/framework/pull/13737/files.  And modified to cope with the absence of certain 5.3
         * helper functions.
         *
         * {@inheritdoc}
         */
        protected function getResultsByType($type)
        {
            $model = $this->createModelByType($type);
            $whereBindings = \Illuminate\Support\Arr::get($this->getQuery()->getQuery()->getRawBindings(), 'where', []);
            return $model->newQuery()->withoutGlobalScopes($this->getQuery()->removedScopes())
                ->mergeWheres($this->getQuery()->getQuery()->wheres, $whereBindings)
                ->with($this->getQuery()->getEagerLoads())
                ->whereIn($model->getTable().'.'.$model->getKeyName(), $this->gatherKeysByType($type))->get();
        }
    }
    

    Next, you'll need something that lets your Model classes actually talk to your incarnation of MorphTo rather than Eloquent's. This can be done by either a trait applied to each model, or a child of Illuminate\Database\Eloquent\Model that gets extended by your model classes instead of Illuminate\Database\Eloquent\Model directly. I chose to make this into a trait. But in case you chose to make it a child class, I've left in the part where it infers the name as a heads-up that that's something you'd need to consider:

    =')) {
                return parent::morphTo($name, $type, $id);
            }
    
            list($type, $id) = $this->getMorphs($name, $type, $id);
    
            if (empty($class = $this->$type)) {
                return new MorphTo($this->newQuery(), $this, $id, null, $type, $name);
            }
    
            $instance = new $this->getActualClassNameForMorph($class);
            return new MorphTo($instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name);
        }
    }
    

提交回复
热议问题