Download all relationships for model

I usually did something like this to load relationships:

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

The solution may be to set $with = ['foo','bar','baz'] , however it will always load these three relationships when I call Model

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

+13
source share
7 answers

No, no, at least not without extra work, because your model does not know what kind of relationship it maintains until it is actually loaded.

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

To make things easier, you can define a function that adds all relationships. You can use the eloquents query areas for this (thanks to Jarek Tkaczyk for mentioning this in the comments).

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

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

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

Alternatives for Getting A Relationship

Although Laravel does not support something like this out of the box, you can always 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 many people use doc blocks to model them. Laravel 5 is also going to use annotations in its route definitions, so I decided that this would not be bad practice in this case. The advantage is that you do not need to maintain a separate list of supported relationships.

Add annotation to each of your relationships:

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

And write a function that parses document blocks of all methods in the model and returns a name. You can do this in the model or in the 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 php annotations, and some don't. I like this simple use case.

Array of maintained relationships

You can also maintain an array of all supported relationships. However, this requires that you always synchronize it with the available relationships, which is not always so simple, especially if several developers participate in it.

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

And then just use them in your withAll function:

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

Of course, you can also override with , like the Lucasgater mentioned in his answer . This seems cleaner than using withAll . If you use annotations or an array of configuration, however, this is a matter of opinion.

+20
source

I would not use static methods, as suggested, since ... it's Eloquent;) Just use what it already offers - a scope .

Of course, he will not do this for you (the main question), however this is definitely the way to go:

 // SomeModel public function scopeWithAll($query) { $query->with([ ... all relations here ... ]); // or store them in protected variable - whatever you prefer // the latter would be the way if you want to have the method // in your BaseModel. Then simply define it as [] there and use: // $query->with($this->allRelations); } 

So you can use it however you want:

 // static-like SomeModel::withAll()->get(); // dynamically on the eloquent Builder SomeModel::query()->withAll()->get(); SomeModel::where('something', 'some value')->withAll()->get(); 

Alternatively, you can actually allow Eloquent to do this for you, as Doctrine does - using doctrine/annotations and DocBlocks. You can do something like this:

 // SomeModel /** * @Eloquent\Relation */ public function someRelation() { return $this->hasMany(..); } 

This is too long a story to include here, so find out how it works: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

+4
source

Since I ran into a similar problem and found a good solution that is not described here and does not require filling in some custom arrays or anything else, I will send it for the future.

What I do, first create a trait called RelationsManager :

 trait RelationsManager { protected static $relationsList = []; protected static $relationsInitialized = false; protected static $relationClasses = [ HasOne::class, HasMany::class, BelongsTo::class, BelongsToMany::class ]; public static function getAllRelations($type = null) : array { if (!self::$relationsInitialized) { self::initAllRelations(); } return $type ? (self::$relationsList[$type] ?? []) : self::$relationsList; } protected static function initAllRelations() { self::$relationsInitialized = true; $reflect = new ReflectionClass(static::class); foreach($reflect->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { /** @var ReflectionMethod $method */ if ($method->hasReturnType() && in_array((string)$method->getReturnType(), self::$relationClasses)) { self::$relationsList[(string)$method->getReturnType()][] = $method->getName(); } } } public static function withAll() : Builder { $relations = array_flatten(static::getAllRelations()); return $relations ? self::with($relations) : self::query(); } } 

Now you can use it with any class, for example -

 class Project extends Model { use RelationsManager; //... some relations } 

and then when you need to extract them from the database:

 $projects = Project::withAll()->get(); 

Some notes. My list of example relationship classes does not contain morphic relationships, so if you want to get them, you need to add them to the $relationClasses variable. In addition, this solution only works with PHP 7.

+4
source

It is impossible to find out what all the relationships are without specifying them yourself. Like the other answers posted well, but I wanted to add a few things.

Base model

It seems to me that you want to do this in several models, so first I would create a BaseModel if you hadn't already.

 class BaseModel extends Eloquent { public $allRelations = array(); } 

"Config" array

Instead of hard coding the relationships in a method, I suggest you use a member variable. As you can see above, I have already added $allRelations . Keep in mind that you cannot name it $relations , as Laravel already uses this internally.

Override with()

Since you wanted with(*) , you can do this too. Add this to BaseModel

 public static function with($relations){ $instance = new static; if($relations == '*'){ $relations = $instance->allRelations; } else if(is_string($relations)){ $relations = func_get_args(); } return $instance->newQuery()->with($relations); } 

(By the way, some parts of this function come from the original model class)

Using

 class MyModel extends BaseModel { public $allRelations = array('foo', 'bar'); } MyModel::with('*')->get(); 
+3
source

You can try to define methods specific to your model using reflection, for example:

 $base_methods = get_class_methods('Illuminate\Database\Eloquent\Model'); $model_methods = get_class_methods(get_class($entry)); $maybe_relations = array_diff($model_methods, $base_methods); dd($maybe_relations); 

Then try loading everyone into a well-controlled try/catch . The Laravel class model has load and loadMissing methods for fast loading.

See the API link.

+2
source

You can create a method in your model.

 public static function withAllRelations() { return static::with('foo', 'bar', 'baz'); } 

And call Model::withAllRelations()

or

$instance->withAllRelations()->first(); // or ->get()

0
source

You cannot have dynamic loading relationships for a specific model. you need to specify the model whose relationship is maintained.

0
source

Source: https://habr.com/ru/post/1210254/


All Articles