即时收集的渴望负载

时间:2019-05-21 07:12:29

标签: laravel eloquent

我有这个,用户通过数据透视表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
    ];
});

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

另一种方法是,我可以通过将其更新为以下内容,将属性_用户添加到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,使用其中进行提取,然后在单个查询中将所有用户应用于属性。这样,它会更加美丽。

也许是这样的:(使用原始的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();

如果有人可以告诉我该怎么做会很棒。

2 个答案:

答案 0 :(得分:0)

渴望只加载您实际需要的字段吗?

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

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

它在官方文档https://laravel.com/docs/5.8/eloquent-relationships#eager-loading中有记录,请参见急于加载特定列

编辑:如果要在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
}

答案 1 :(得分:0)

好的,在这里和那里读完之后。

我找到了这篇文章:https://laravel-news.com/eloquent-eager-loading

解决方案真的很好。简单地:

$properties = $this->run(GetProperties::class);

//lazy load
$properties->load('property_users');

$properties = $properties->map(function($property) {
    $user = $property->property_users->first(function($user) {
        if($user->user_id == Auth::user()->id) {
            return $user;
        }
    })->only('chat_username');

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

检查查询日志后:

//auth query done by middleware
[2019-05-21 07:59:11] local.INFO: select * from `users` where `auth_token` = ? or `temporary_auth_token` = ? limit 1 ["token_removed_for_security_purpose","token_removed_for_security_purpose"] 
//These are the queries made:
[2019-05-21 07:59:11] local.INFO: select `properties`.*, `property_users`.`user_id` as `pivot_user_id`, `property_users`.`property_id` as `pivot_property_id` from `properties` inner join `property_users` on `properties`.`id` = `property_users`.`property_id` where `property_users`.`user_id` = ? [8] 
[2019-05-21 07:59:11] local.INFO: select * from `property_users` where `property_users`.`property_id` in (2, 4)  

这样,我可以将GetProperties保持尽可能的小,然后在需要的地方随意加载它。