【问题标题】:hasMany relation including nullhasMany 关系,包括 null
【发布时间】:2020-04-22 23:13:24
【问题描述】:

我的设置:

我有 2 个模型:UserSetting

一个用户可以有很多设置:

public function settings(): HasMany
{
    return $this->hasMany(Setting::class);
}
$user = User::with('settings')->find($id);

这里是迁移:

Schema::create('settings', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->uuid('user_id')->nullable()->index();
    $table->text('description');
    $table->string('setting_key');
    //$table->json('setting_value')->nullable();
    $table->text('setting_value')->nullable();
    $table->timestamps();
});

user_id 列可以为空,因为settings 表具有默认设置。

我的问题:

我想让settings() 关系也返回user_id 为空的行(这些是默认设置,没有链接到特定用户)。

到目前为止我尝试了什么:

这不起作用:

public function settings(): HasMany
{
    return $this->hasMany(Setting::class)->orWhereNull('settings.user_id');
}

这也不起作用,即使我的调试栏中的 sql 请求看起来不错:

$user = User::query()
    ->with([
        'settings' => function (HasMany $query) {
            $query->orWhereNull('settings.user_id');
        },
        // irrelevant stuff
    ])
    ->findOrFail($userId);

运行:

select * from `settings` where `settings`.`user_id` in ('123') or `settings`.`user_id` is null

无论如何,$user->settings 是一个集合,它不包含我的settings 表中的任何行,其中user_id 为空。它只包含user_id 等于用户id 的行。

【问题讨论】:

  • 编写总是需要检查是否存在 user_id 的代码会随着应用程序的增长而变得很累。为什么不给系统定义一个user_id 1?
  • 编写总是需要检查 user_id 是否等于 1 的代码会随着应用程序的增长而变得很累。为什么不给系统定义一个user_id null?
  • 当涉及到数据库时,使用默认值通常比使用不存在的值更容易。但这只是一个想法......
  • 我不这么认为。 “null”更具语义性,占用更少的磁盘/内存空间,并且允许保留外键约束。最后不得不处理“null”或“1”是一样的,但是当你有getter方法时这是一种享受。感谢您的建议,但我认为这超出了我的问题范围。
  • 为什么不先查询设置再获取与用户的反比关系,而不是先查询用户再查询设置。因为hasMany 不会以任何方式返回在您使用的父模型上没有任何关系标识符的字段,在这种情况下是User 模型。

标签: php laravel eloquent


【解决方案1】:

合并两个结果或原始表达式怎么样?

我实现了你所有的代码,我也看到了同样的困难......所以我发现这些解决方案不是很优雅但很实用。

备选方案 1 - 合并结果:

//user
$u = DB::table('users')->join('settings', 'users.id', '=', 'settings.user_id')->get();
//settings without user_id
$n = DB::table('settings')->where('settings.user_id', '=', null)->get();
//all together
$result = $u->merge($n);

我在 php artisan tinker 上的输出

>>> $result = $u->merge($n);
=> Illuminate\Support\Collection {#3019
     all: [
       {#3015
         +"id": "2",
         +"name": "name 123",
         +"email": "teste123@teste.com",
         +"email_verified_at": "2020-04-22 20:54:06",
         +"password": "1111111111111111111",
         +"remember_token": "1111111111111111111",
         +"created_at": "2020-04-22 21:13:50",
         +"updated_at": "2020-04-22 21:13:50",
         +"user_id": "123",
         +"description": "desc",
         +"setting_key": "conf",
         +"setting_value": "configuration-configuration",
       },
       {#3031
         +"id": "1",
         +"user_id": null,
         +"description": "desc",
         +"setting_key": "conf",
         +"setting_value": "configuration-configuration",
         +"created_at": "2020-04-22 21:13:50",
         +"updated_at": "2020-04-22 21:13:50",
       },
     ],
   }

备选方案 2 - 原始表达式:

$id = 123;

$r = DB::select(DB::raw("select * from settings where settings.user_id in ('{$id}') or settings.user_id is null"));

【讨论】:

  • 这可能是一种有效/有效的方法,但确实不优雅。我更愿意通过“Laravel 方式”实现它,急切加载,并通过 $user 变量访问关系:$user->settings、$user->friends、$user->likes 等...不是 $user 、$userSettings、$userFriends 等。我可以在 $user 上进一步修改“setRelation()”,但这会更不优雅。
  • 是的,我理解你。不幸的是,当我们没有时间时,我们需要的不是最好的解决方案,而是一个解决方案。但如果您有时间,我鼓励您寻找最佳方法。
猜你喜欢
  • 1970-01-01
  • 2017-10-17
  • 2010-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
相关资源
最近更新 更多