加载模型的所有关系

Hel*_*Che 13 php laravel

通常为了加载关系我会做这样的事情:

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

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

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

Mar*_*der 20

不,它不是,至少没有一些额外的工作,因为你的模型在实际加载之前不知道它支持哪种关系.

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

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

public function scopeWithAll($query) 
{
    $query->with('foo', 'bar', 'baz');
}
Run Code Online (Sandbox Code Playgroud)

使用范围而不是静态函数不仅允许您直接在模型上使用函数,而且还可以在where以任何顺序链接查询构建器方法时使用:

Model::where('something', 'Lorem ipsum dolor')->withAll()->where('somethingelse', '>', 10)->get();
Run Code Online (Sandbox Code Playgroud)

获得支持关系的替代方案

虽然Laravel不支持开箱即用的东西,但你可以自己添加它.

注释

我使用注释来确定函数是否是我上面提到的包中的关系.注释不是php的正式部分,但很多人使用doc块来模拟它们.Laravel 5也将在其路线定义中使用注释,因此我认为在这种情况下它不是不好的做法.优点是,您不需要维护单独的受支持关系列表.

为每个关系添加注释:

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

并编写一个函数来解析模型中所有方法的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;
}
Run Code Online (Sandbox Code Playgroud)

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

public function scopeWithAll($query) 
{
    $query->with($this->getSupportedRelations());
}
Run Code Online (Sandbox Code Playgroud)

有些像php中的注释,有些则没有.我喜欢这个简单的用例.

支持的关系数组

您还可以维护所有受支持关系的数组.然而,这需要您始终将其与可用关系同步,尤其是如果涉及多个开发人员,这并不总是那么容易.

protected $supportedRelations = ['foo','bar', 'baz'];
Run Code Online (Sandbox Code Playgroud)

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

public function scopeWithAll($query) 
{
    return $query->with($this->supportedRelations);
}
Run Code Online (Sandbox Code Playgroud)

你当然也可以覆盖with他的答案中提到的lukasgeiter.这似乎比使用更清洁withAll.如果您使用注释或配置数组,则是一个意见问题.


luk*_*ter 5

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

基础型号

我感觉你想在多个模型中执行此操作,所以BaseModel如果你还没有的话,首先我会创建一个。

class BaseModel extends Eloquent {
    public $allRelations = array();
}
Run Code Online (Sandbox Code Playgroud)

“配置”数组

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

覆盖with()

既然你愿意,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);
}
Run Code Online (Sandbox Code Playgroud)

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

用法

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

MyModel::with('*')->get();
Run Code Online (Sandbox Code Playgroud)

  • 请注意,它不会影响 `Eloquent\Builder` 上的 `with` 方法,因此您将无法执行: `Model::where(..)->with('*')->get( )` (2认同)