首页 > 解决方案 > 即时加载集合

问题描述

我有这个,用户通过 pivot property_users 拥有多对多的属性。

我正在我的 web 应用程序中制作某种可重用的类

这些是具有急切加载功能的模型:

//User model
public function properties()
{
    return $this->belongsToMany(Property::class, 'property_users', 'user_id', 'property_id');
}

//Property model
public function property_users()
{
    return $this->hasMany(PropertyUser::class, 'property_id', 'id');
}

//PropertyUser model
public function user()
{
    return $this->belongsTo(User::class);
}

//GetProperties class
public function handle()
{
    return auth()->user()->properties()->get();
}

//somewhere in a feature
$properties = $this->run(GetProperties::class);
//this returns valid properties under the logged in user

我现在需要在 property_users 中获取属于该用户的 chat_username

如果我遍历属性然后动态执行它,我设法使它工作。

$properties = $properties->map(function($property) {
    $propertyUsers = $property->property_users()->get();

    $chatUsername = null;
    foreach($propertyUsers as $propertyUser) {
        if($propertyUser->property_id == $property->id) {
           $chatUsername = $propertyUser->chat_username;
        }
    }

    return [
        'name' => $property->name,
        'id' => $property->id,
        'chat_username' => $chatUsername
    ];
});

但我试图减少循环查询以减少命中,尤其是当它们位于数据库中的多个属性上时

另一种方法是,我可以将 property_users 添加到 GetProperties 类下的即时加载中,方法是将其更新为:

$query = Property::query();

$query->with(['property_users']);

$query->whereHas('property_users', function($qry) {
    $qry->where('user_id', Auth::user()->id);
});

$properties = $query->get();

return $properties;

但是我不想依赖于向原始的 GetProperties 类添加更多的急切加载,因为 GetProperties 会变胖而且我并不真正需要这些数据(比如说添加 property_invoices、property_schedules 等,但在某些区域并不真正需要它)。

相反,我想在运行中进行急切的加载,但要有所改变!这就是我的想象:

从属性中收集所有 id,使用 where 进行提取并将所有用户应用于单个查询中的属性。这样会更漂亮。

也许是这样的:(使用原始的 GetProperties 类)

$properties = $this->run(GetProperties::class);
//this does not work. The error is: Method Illuminate\Database\Eloquent\Collection::property_users does not exist.
$property->property_users = $properties->property_users()->get();

如果有人能告诉我怎么做,那就太好了。

标签: laraveleloquent

解决方案


只急切地加载您实际需要的字段怎么样?

$query->with('property_users:id,user_id');

该模型不会变胖,并且您不需要在循环中进行单独的查询。

它记录在官方文档中:https://laravel.com/docs/5.8/eloquent-relationships#eager-loading,请参阅Eager Loading Specific Columns

编辑:如果要在 GetProperties 类之后执行查询,则需要收集所有 id 并执行第二次查询。老实说,我不喜欢第二种方法,因为它要慢得多,性能较差,而且我认为它不如在 GetProperties 类中添加一行那么优雅,但它会起作用:

$properties = $this->run(GetProperties::class);
$ids = $properties->pluck('id'); // Get all the ids as an array
$propertyUsers = propertyUsers::whereIn('property_id', $ids)->get(); // Get the propertyUsers model
foreach($properties as $property) {
    $property->property_users = $propertyUsers->where('property_id', $property->id); // Not even sure you can do that, proerty_users may not been writable
}

推荐阅读