Search code examples
phplaraveleager-loading

Laravel 5.7 - Eager loading with morphMany relationship and custom attribute getter


So I have the following models:

class TemplateEntity extends Model {
    protected $table = "TemplateEntities";

    const UPDATED_AT = null;
    const CREATED_AT = null;

    public function element() {
        return $this->morphTo("element", "entity_type", "id_Entity");
    }

    public function getEntityTypeAttribute($entity_type) {
        return 'App\\' . $entity_type;
    }
}

class Template extends Model {
    protected $table = "Template";

    const UPDATED_AT = null;
    const CREATED_AT = null;

    public function entities() {
        return $this->hasMany("App\TemplateEntity", "id_Template");
    }
}

class TemplateEntity extends Model {
    protected $table = "TemplateEntities";

    const UPDATED_AT = null;
    const CREATED_AT = null;

    public function element() {
        return $this->morphTo("element", "entity_type", "id_Entity");
    }

    public function getEntityTypeAttribute($entity_type) {
        return 'App\\' . $entity_type;
    }
}

I want to eager load template entity elements using Eloquent ORM's ::with() method, however whenever I do this I get an error:

//$template_id is defined as a controller param
$template = Template::with("entities", "entities.element")->where("id", "=", $template_id)->get()

"Class 'App\' not found"

I did some debugging and when I echo $entity_type in TemplateEntity's GetEntityTypeAttribute() method I get an empty value. However, my models generally work fine if I don't use eager loading, but I would like to add it to my application if possible to make it more efficient.

Any help you all can provide would help!

edit: fixed a typo, should have been Template::with instead of $template::with


Solution

  • Part of the problem might be a blank class in that variable. Suggest you use the class name when calling get(). So \App\Template:: instead of $template::.

    Another item to help may be the way you are calling the relationship's eager load. Perhaps try to call through the function. This might work better for you:

     \App\Template::with(['entities' => function($query){
            $query->with('element');
        }])->get();
    

    The accessor function might be interfering with the Laravel morph function. I realise you want to use the shortened name of the class in the DB. To do this without the use of the getter (and globally), I suggest using a morphMap.

    In AppServiceProvider inside the boot() method:

      \Illuminate\Database\Eloquent\Relations\Relation::morphMap([
            'MyTemplate' => \App\MyTemplate::class,  
            'Section' => \App\Section::class,  
             // etc.        
        ]);
    

    This will allow you to add only 'Section' to the DB and remove the accessor function from your class.