Laravel Eloquent - 急切地加载查询范围


Laravel Eloquent - Loading query scopes eagerly

我有一个数据结构,我需要对象知道它们加载所需的依赖项。

我能做什么

目前,我可以这样做来加载第一层关系,这显然是一个非常基本的模型:

class Ticket {
    public function notes(){}
    public function events(){}
    public function tags(){}
    public function scopeWithAll($query)
    {
        $query->with('notes', 'events', 'tags');
    }
}
// Loads Ticket with all 3 relationships
$ticket = Ticket::withAll();

这很好用! 问题是,我需要将此功能链接到 3-5 个级别的依赖关系。 3 个加载模型中的每一个都有自己的 n 个关系。

我知道如果我指定所有关系名称,我可以通过预先加载来做到这一点,如下所示:

public function scopeWithAll($query)
{
    $query->with('notes.attachments', 'notes.colors', 'events', 'tags', 'tags.colors.', 'tags.users.email');
}

这也很好用。 但是我需要我的代码比这更聪明。

我需要做什么

在我的项目中,静态定义每个对象加载的范围是不可取的。 我需要能够加载票证,并且票证加载其所有关系,并且每个关系加载其所有关系。

我能想到的唯一方法是找到某种方法来急切地为类上的每个关系加载查询范围。 类似的东西

public function scopeWithAll($query)
{
    $query->with('notes.withAll()', 'events.withAll()', 'tags.withAll()');
}

目前有没有办法在 Eloquent 中做到这一点?

也许你可以尝试这样的事情:

User::withRelatives()->find(1);

好的,这是一个想法,如何实现它?例如,如果您的User模型有一些相关的方法,例如"帖子","角色"等,则将所有相关方法(建立关系的方法)保留在单独的trait中,例如:

trait UserRelatives {
    public function posts()
    {
        // ...
    }
    public function roles()
    {
        // ...
    }
}

现在,在User模型中,您可以创建一个类似withAllscopeMethod,您可以在其中尝试如下操作:

public function scopeWithAll($query)
{
    // Get all the related methods defined in the trait
    $relatives = get_class_methods(UserRelatives::class);
    return $query->with($relatives);
}

所以,如果你做这样的事情:

$user = User::withAll()->find(1);

您将能够加载所有相关模型。顺便说一句,get_class_methods(UserRelatives::class)将为您提供该特征中定义的所有方法的数组,这些方法可能如下所示:

['posts', 'roles']

因此,User::withAll()将加载所有相关模型,然后运行query。因此,结果范围将执行以下操作:

$query->with(['posts', 'roles']);

嗯,这是一个抽象的想法,但希望你明白了。如果您发现更好的东西,请分享您的想法。

更新:

根据您的模型和相关方法,这可能如下所示:

class Ticket {
    use TicketRelativesTrait;
    public function scopeWithAll($query)
    {
        $relatives = get_class_methods(TicketRelativesTrait::class);
        return $query->with($relatives);
    }
}

特性:

trait TicketRelativesTrait {
    public function notes(){}
    public function events(){}
    public function tags(){}
}

// Loads Ticket with all relationships
$ticket = Ticket::withAll()->find(1);

这是更动态的,无需提及相关方法,并且每当在特征中添加新的关系方法时,也会加载该方法。