laravel 加载模型的所有关系

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27742382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 10:37:46  来源:igfitidea点击:

Load all relationships for a model

phplaravel

提问by Helen Che

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

通常急切加载关系我会做这样的事情:

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

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

一个解决方案可能是设置$with = ['foo','bar','baz']但是每当我打电话时总是加载这三个关系Model

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

是否有可能做这样的事情:Model::with('*')

回答by Marcel Gwerder

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 Relationobject. 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.

我在自己的一个 Laravel 包中遇到了这个问题。无法获得模型与 Laravel 的关系列表。如果你看看它们是如何定义的,那就很明显了。返回Relation对象的简单函数。你甚至无法通过php的反射类获取函数的返回类型,因此无法区分关系函数和任何其他函数。

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).

您可以做的事情是定义一个添加所有关系的函数。为此,您可以使用eloquents 查询范围(感谢 Jarek Tkaczyk 在评论中提到它)。

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 wherein any order:

使用作用域而不是静态函数,您不仅可以直接在模型上使用您的函数,而且例如where在以任何顺序链接查询构建器方法时:

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.

虽然 Laravel 不支持这样的开箱即用的东西,但你总是可以自己添加它。

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.

我使用注释来确定函数是否是上述包中的关系。注释不是 php 的正式组成部分,但很多人使用 doc 块来模拟它们。Laravel 5 也将在其路由定义中使用注释,所以我认为在这种情况下这不是坏习惯。优点是,您不需要维护支持关系的单独列表。

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:

并编写一个函数,解析模型中所有方法的doc块并返回名称。您可以在模型或父类中执行此操作:

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 withAllfunction:

然后在你的withAll函数中使用它们:

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.

有些人喜欢 php 中的注释,有些人不喜欢。我喜欢这个简单的用例。

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 withAllfunction:

然后在你的withAll函数中使用它们:

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

You can of course also override withlike 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.

您当然也可以with像他的回答中提到的 lukasgeiter 那样覆盖。这似乎比使用withAll. 如果您使用注释或配置数组,则是见仁见智。

回答by Jarek Tkaczyk

I wouldn't use staticmethods like suggested since... it's Eloquent ;) Just leverage what it already offers - a scope.

我不会使用static建议的方法,因为......它是 Eloquent ;) 只是利用它已经提供的东西 -一个范围

Of course it won't do it 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); 
}

This way you're free to use this as you like:

这样你就可以随意使用它:

// static-like
SomeModel::withAll()->get();

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


Also, in fact you canlet Eloquent do it for you, just like Doctrinedoes - using doctrine/annotationsand DocBlocks. You could do something like this:

此外,事实上你可以让 Eloquent 为你做这件事,就像Doctrine使用doctrine/annotationsDocBlocks 一样。你可以这样做:

// SomeModel

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

It's a bit too long story to include it here, so learn how it works: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

把它包括在这里有点太长了,所以了解它是如何工作的:http: //docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

回答by Eternal1

Since i've met with a similar problem, and found a good solution that isn't described here and doesn't require filling some custom arrays or whatever, i'll post it for the future.

由于我遇到了类似的问题,并找到了一个在这里没有描述的很好的解决方案,并且不需要填充一些自定义数组或其他任何东西,我会在将来发布它。

What i do, is first create a trait, called RelationsManager:

我所做的是首先创建一个trait名为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, like -

现在你可以在任何类中使用它,比如 -

class Project extends Model
{
    use RelationsManager;

    //... some relations

}

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

然后当您需要从数据库中获取它们时:

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

Some notes - my example relation classes list doesn't include morph relations, so if you want to get them as well - you need to add them to $relationClassesvariable. Also, this solution only works with PHP 7.

一些注意事项 - 我的示例关系类列表不包括变形关系,因此如果您也想获得它们 - 您需要将它们添加到$relationClasses变量中。此外,此解决方案仅适用于 PHP 7。

回答by lukasgeiter

There's no way to know what all the relations are without specifying them yourself. How the other answers posted are good, but I wanted to add a few things.

如果不自己指定它们,就无法知道所有关系是什么。其他答案发布的方式很好,但我想添加一些内容。

Base Model

基础模型

I kind of have the feeling that you want to do this in multiple models, so at first I'd create a BaseModelif you haven't already.

我有点感觉你想在多个模型中这样做,所以首先我会创建一个,BaseModel如果你还没有的话。

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

"Config" array

“配置”数组

Instead of hard coding the relationships into a method I suggest you use a member variable. As you can see above I already added $allRelations. Be aware that you can't name it $relationssince Laravel already uses that internally.

我建议您使用成员变量,而不是将关系硬编码到方法中。正如你在上面看到的,我已经添加了$allRelations. 请注意,您无法命名它,$relations因为 Laravel 已经在内部使用了它。

Override with()

覆盖 with()

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

既然你想要,with(*)你也可以这样做。将此添加到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)

(顺便说一下,这个函数的某些部分来自原始的 Model 类)

Usage

用法

class MyModel extends BaseModel {
    public $allRelations = array('foo', 'bar');
}

MyModel::with('*')->get();

回答by Mauro Colella

You could attempt to detect the methods specific to your model using reflection, such as:

您可以尝试使用反射来检测特定于您的模型的方法,例如:

$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 attempt to load each in a well-controlled try/catch. The Modelclass of Laravel has a loadand a loadMissingmethods for eager loading.

然后尝试将每个加载到一个控制良好的try/catch. Laravel的Model类有一个load和一个用于预先加载loadMissing方法。

See the api reference.

请参阅api 参考。

回答by Kyle Emmanuel

You can't have a dynamicloading of relationships for a certain model. you need to tell the model which relations to support.

您不能为某个模型动态加载关系。你需要告诉模型支持哪些关系。

回答by xAoc

You can create method in your Model

您可以在模型中创建方法

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

And call Model::withAllRelations()

并打电话 Model::withAllRelations()

Or

或者

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

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