Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load all relationships for a model

Tags:

php

laravel

Usually to eager load a relationship I would do something like this:

Model::with('foo', 'bar', 'baz')...

A solution might be to set $with = ['foo','bar','baz'] however that will always load these three relations whenever I call Model

Is it possible to do something like this: Model::with('*')?

like image 853
Helen Che Avatar asked Jan 02 '15 12:01

Helen Che


1 Answers

No it's not, at least not without some additional work, because your model doesn't know which relations it supports until they are actually loaded.

I had this problem in one of my own Laravel packages. There is no way to get a list of the relations of a model with Laravel. It's pretty obvious though if you look at how they are defined. Simple functions which return a Relation object. You can't even get the return type of a function with php's reflection classes, so there is no way to distinguish between a relation function and any other function.

What you can do to make it easier is defining a function that adds all the relationships. To do this you can use eloquents query scopes (Thanks to Jarek Tkaczyk for mentioning it in the comments).

public function scopeWithAll($query) 
{
    $query->with('foo', 'bar', 'baz');
}

Using scopes instead of static functions allows you to not only use your function directly on the model but for example also when chaining query builder methods like where in any order:

Model::where('something', 'Lorem ipsum dolor')->withAll()->where('somethingelse', '>', 10)->get();

Alternatives to get supported relations

Although Laravel does not support something like that out of the box you can allways add it yourself.

Annotations

I used annotations to determine if a function is a relation or not in my package mentioned above. Annotations are not officially part of php but a lot of people use doc blocks to simulate them. Laravel 5 is going to use annotations in its route definitions too so I figuered it not to be bad practice in this case. The advantage is, that you don't need to maintain a seperate list of supported relations.

Add an annotation to each of your relations:

/**
 * @Relation
 */
public function foo() 
{
    return $this->belongsTo('Foo');
}

And write a function that parses the doc blocks of all methods in the model and returns the name. You can do this in a model or in a parent class:

public static function getSupportedRelations() 
{
    $relations = [];
    $reflextionClass = new ReflectionClass(get_called_class());

    foreach($reflextionClass->getMethods() as $method) 
    {
        $doc = $method->getDocComment();

        if($doc && strpos($doc, '@Relation') !== false) 
        {
            $relations[] = $method->getName();
        }
    }

    return $relations;
}

And then just use them in your withAll function:

public function scopeWithAll($query) 
{
    $query->with($this->getSupportedRelations());
}

Some like annotations in php and some don't. I like it for this simple use case.

Array of supported relations

You can also maintain an array of all the supported relations. This however needs you to always sync it with the available relations which, especially if there are multiple developers involved, is not allways that easy.

protected $supportedRelations = ['foo','bar', 'baz'];

And then just use them in your withAll function:

public function scopeWithAll($query) 
{
    return $query->with($this->supportedRelations);
}

You can of course also override with like lukasgeiter mentioned in his answer. This seems cleaner than using withAll. If you use annotations or a config array however is a matter of opinion.

like image 65
Marcel Gwerder Avatar answered Sep 22 '22 23:09

Marcel Gwerder