Laravel 用户模型条件预加载(带)

Posted

技术标签:

【中文标题】Laravel 用户模型条件预加载(带)【英文标题】:Laravel user model conditional eager loading (with) 【发布时间】:2021-12-11 15:12:51 【问题描述】:

我正在使用 Laravel 和 Vue 构建一个混合应用程序。 我有一个用例,并非所有用户都有一定的关系。例如,一个客户可以有一个和多个业务单位

目前我是这样设置的:

<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Spatie\MediaLibrary\HasMedia;
use Illuminate\Notifications\Notifiable;
use Lab404\Impersonate\Models\Impersonate;
use Spatie\MediaLibrary\InteractsWithMedia;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements HasMedia

  use Traits\BaseModelTrait;
  use Traits\ActiveTrait;

  use InteractsWithMedia;
  use Impersonate;
  use HasApiTokens;
  use Notifiable;
  use HasFactory;

  protected $hidden = [
    'password', 'remember_token',
  ];

  protected $fillable = [
    'name', 'email', 'password', 'avatar',
  ];

  protected $casts = [
    'settings' => AsArrayObject::class,
    'is_admin' => 'boolean',
  ];

  protected $with = [
    'domain',
    'BusinessUnits'
  ];

  public function scopeAdmin($query)
  
    return $query->where('is_admin', true);
  

  public function scopeEmployee($query)
  
    return $query->whereNull('domain_id');
  

  public function scopeClient($query)
  
    return $query->whereNotNull('domain_id');
  

  public function BusinessUnits()
  
    return $this->belongsToMany(BusinessUnit::class, 'users_business_units_pivot');
  

  public function Domain()
  
    return $this->belongsTo(Domain::class);
  

这种方法的“问题”是,对于每个请求,每个用户都会执行 2 个查询。我希望仅当“domain_id”不为空(scopeClient)时才加载关系。 对于普通的“模型”,我可以每页选择应该加载哪些模型等,但是对于经过身份验证的用户来说,这实际上是不可能的,因为我知道。

我想我正在寻找这样的东西:

  protected $with = [
    (!$this->domain_id) ? 'domain' : null,
    (!$this->domain_id) ? 'BusinessUnits' : null
  ];

这当前会生成错误:“常量表达式包含无效操作。”

任何解决此问题的建议和/或想法将不胜感激!

【问题讨论】:

所以你想添加到用户模型,domain_id?请发布您所拥有的输出以及所需的输出 否,用户已有 domain_id。对于domain_id 不为空的经过身份验证的用户,我希望加载域关系。 您所问的内容非常令人困惑,答案就是您所问的,所以请澄清您想要什么,举例说明您正在做什么,您想要什么以及什么你得到了 我的意思是,只需预加载domain,如果domain_id 它将急切加载到一个空集合中...... 如果我已经知道他没有域,我不希望执行查询。 【参考方案1】:

您可以尝试使用事件:

// this code should be inside your model
public static function boot()

    parent::boot();

    self::retrieved(function($model)
        if($model->domain_id !== null)
        
             $model->load('domain', 'BusinessUnits');
        
    );

显然,您必须从 $with 中删除这些关系

【讨论】:

正是我想要的!你是真正的英雄,谢谢! @RobertFridzema 很高兴看到它有所帮助,对 cme​​ts 感到抱歉,但我想在给出可能的答案之前深入了解这个问题(另外,如果你需要它,你可以在这里找到 laravel.com/docs/8.x/eloquent#events您可以在boot 函数中使用的所有可能的事件)【参考方案2】:

要获取所有拥有域的用户,请使用whereHas()

$users = User::whereHas('Domain')->with(['Domain', 'BusinessUnits'])->get();

它将启动 3 个查询,一个针对用户,一个针对域,一个针对业务部门。

【讨论】:

感谢您的回复。我将这种方法用于除用户之外的所有其他模型。我希望有条件地为经过身份验证的用户加载关系,最好在用户模型中。 @RobertFridzema 然后将其添加到您的身份验证中间件中,以便在对用户进行身份验证时预加载域。 似乎不适合做这件事。您指的是“Authenticate”中间件类吗?在这里也找不到修改经过身份验证的用户(查询)的方法。 @RobertFridzema 您制作了一个新的中间件,该中间件与供应商的中间件相联系,将其放置在 kernel.php 中的供应商中间件并覆盖处理方法。现在在句柄方法中,执行与供应商相同的代码并添加您的代码。

以上是关于Laravel 用户模型条件预加载(带)的主要内容,如果未能解决你的问题,请参考以下文章

具有预加载关系的 Laravel 模型

Gorm 预加载及实现联表条件查询仿WhereHas

Gorm 预加载及实现联表条件查询仿WhereHas

从第一个表中选择特定列,在 Laravel 中进行预加载

如何从 Laravel 上的预加载中获取嵌套数据?

预加载 has_many 与动态条件的关联