Eloquent ORM - 一对多关联

接着要来给 Todo 加上与 User 的关联,区分各 User 建立的 Todo。

一个 User 拥有多个 Todo ,所以是一对多的关联。

hasMany 关联

跟前面一样先建立 migration 帮 Todo 加上 user_id 栏位。

sail artisan make:migration update_todo_relate_user --table todos

/database/migrations/2021_10_01_105954_update_todo_relate_user.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class UpdateTodoRelateUser extends Migration
{
    public function up()
    {
        Schema::table('todos', function (Blueprint $table) {
            // 加上 user_id
            $table->unsignedBigInteger('user_id'); 
        });
    }
    
    public function down()
    {
        Schema::table('todos', function (Blueprint $table) {
            $table->dropColumn('user_id');
        });
    }
}

接着在 User Model 中加入关联

/app/Models/User.php

@@ -9,6 +9,7 @@ use Illuminate\Notifications\Notifiable;
 use Laravel\Sanctum\HasApiTokens;
 use Illuminate\Database\Eloquent\SoftDeletes;
 use App\Models\UserSetting;
+use App\Models\Todo;
 
 class User extends Authenticatable
 {
@@ -50,4 +51,9 @@ class User extends Authenticatable
     {
         return $this->hasOne(UserSetting::class);
     }
+
+    public function todos()
+    {
+        return $this->hasMany(Todo::class);
+    }
 }

写法跟一对一关联相同,只是改成 hasMany 方法,简单明了。

如果想要更改关联栏位名称,写法与 hasOne 相同。

hasMany(<模型名称>,<目标模型的外键名称>,<模型关联键名称>)

至於从 Todo 反查询一对多关联的方法一样是 belongsTo

/app/Models/Todo.php

@@ -4,6 +4,7 @@ namespace App\Models;
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
+use App\Models\User;
 
 class Todo extends Model
 {
@@ -12,4 +13,9 @@ class Todo extends Model
     protected $fillable = [
         'name',
     ];
+    
+    public function user()
+    {
+        return $this->belongsTo(User::class);
+    }
 }

写入关联资料

接着要让 Todo 资料在新增的同时归属於 User ,要改写 TodoController 的 store 方法。

/app/Http/Controllers/TodoController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Todo;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
 
 class TodoController extends Controller
 {
@@ -39,13 +40,15 @@ class TodoController extends Controller
      */
     public function store(Request $request)
     {
         $data = $request->all(); 
 
         $todo = new Todo;
     
         $todo->name = $data['name'];
     
-        $todo->save();
+        $user = Auth::user();
+        $user->todos()->save($todo);
     }

原本 $todo->save() 的方法改为先用 $user->todos() 建立关联的 Query Builder 後再 save ,这样新建的 todo 就会自动带入 user_id 的资料。

也可以直接用 create() 建立,都是 Query Builder 的应用可以视情况使用。

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Models\Todo;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
 
 class TodoController extends Controller
 {
 
@@ -39,13 +40,12 @@ class TodoController extends Controller
      */
     public function store(Request $request)
     {
         $data = $request->all();  
         
-        $todo = new Todo;
-        $todo->name = $data['name'];
-        $todo->save();

+        $user = Auth::user();
+        $user->todos()->create([
+            'name' => $data['name'],
+        ]);
     }

save 或是 create 方法都有批量新增的版本,可以一次建立多笔关联资料

$user->todos()->saveMany([
    new Todo(['name' => 'todo 1']),
    new Todo(['name' => 'todo 2']),
]);

$user->todos()->createMany([
    ['name' => 'todo 1'],
    ['name' => 'todo 2'],
]);

读取关联资料

读取的方式跟一对一关联时相同

$user->todos

所以来改写 TodoController 的 index 方法,变成只回传登入用户的 todos

@@ -13,11 +14,9 @@ class TodoController extends Controller
      * @return \Illuminate\Http\Response
      */
     public function index()
-    {
-        $todos = Todo::get(); 
-
+    {  
          return inertia('Dashboard', [
-            'todos' => $todos,
+            'todos' => Auth::user()->todos,
         ]);
     }

这样就好了,可以试试不同帐号的画面上会显示不同的 todo 清单。

变更关联

目前介绍了一对一,一对多的建立关联资料方法,一般使用情况下关联资料建立後就不会再变更所属了,最多就是编辑资料。

不过偶尔会有需要移除关联或是改变所属的状况,这边先介绍一对一跟一对多情况的写法,两者是相同的,之後多对多的写法另外介绍。

移除关联

移除关联表示将资料中的外键清除,这样查询关联时就不会再查到他了,不过资料本身还是保留的。

首先要注意如果允许资料可以没有关联的话,在 Migration 时要将外键设定成 nullable,不然清除外键的时候会抵触资料库设定然後报错。

$table->unsignedBigInteger('user_id')->nullable(); //允许没有关联
$table->foreign('user_id')->references('id')->on('users');

然後,会由 belongsTo 的那一方发起移除关联,使用的是 dissociate 方法。

$todo->user()->dissociate();
$todo->save();

因为一对一跟一对多关联中从属的那一方都是用 belongsTo 方法,所以两者都可以用 dissociate 移除关联。

改变所属

跟移除关联相同,变更关联也是由 belongsTo 那一方的 Model 发起变更,使用 associate 方法。

$todo = Todo::find(1);
$todo->user()->associate($user);
$todo->save();

不管 todo 原先有没有所属都会被更新所属的 user 。


<<:  [Day18]程序菜鸟自学C++资料结构演算法 – 线性搜寻法(Linear Search)与二分搜寻法(Half-Interval Search)

>>:  Day 27:专案07 - 天气小助理01 | 气象资料API

JS [笔记] debounce、throttle

完全参考,此处为整理笔记 [有趣面试题] 网页效能问题改善之 Debounce & Thro...

12 - Metrics - 观察系统的健康指标 (6/6) - 使用 Metricbeat 掌握 Infrastructure 的健康状态 AWS 篇

Metrics - 观察系统的健康指标 系列文章 (1/6) - Metrics 与 Metricb...

SQL模拟资料汇出及印出新增资料插入~

这其实是遇到无法用大量汇入~ 却又想要将指定资料表汇入到另一个资料库的SQL方式@@ 因为在独立环境...

Day14-Machine Learning : Self-attention

因为之後要用到,今天就简单阅读了这个, 投影片和内容都是来自台大李弘毅教授的youtube http...

【Day25】反馈元件 - Skeleton

元件介绍 Skeleton 是一个骨架载入元件(Skeleton Screen Loading),跟...