iT邦幫忙

2021 iThome 鐵人賽

DAY 17
0
Modern Web

Laravel 實務筆記系列 第 17

Eloquent ORM - 一對多關聯

  • 分享至 

  • xImage
  •  

接著要來給 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 。


上一篇
Eloquent ORM - 一對一關聯
下一篇
Eloquent ORM - 多對多關聯
系列文
Laravel 實務筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言