iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0
佛心分享-IT 人自學之術

後端小白自學 Laravel系列 第 5

第 5 天:數據模型與遷移

  • 分享至 

  • xImage
  •  

介紹 Laravel 的 Eloquent ORM


Eloquent ORM 是 Laravel 框架的官方物件關聯映射(ORM)系統,它提供了一個簡單的 Active Record 實現,可以用面向對象的方式操作數據庫。
使用 Eloquent 可以透過模型對象來處理數據庫操作,並且可以輕鬆地定義和管理數據表之間的關係。

主要特性:

  • 簡單的數據庫操作:用模型方法來進行查詢和操作,無需直接寫 SQL 語句。
  • 數據表與模型的對應:每個模型對應一個數據表,模型的屬性和方法代表了表中的列和操作。
  • 關聯管理:輕鬆定義和管理表之間的關聯(如一對多、多對多等)。

💡 ORM 是一種技術,它把資料庫中的資料表格和記錄映射成程式中的對象,讓你可以像操作物件一樣操作資料庫中的資料,而不需要直接寫 SQL 查詢。

創建模型和遷移文件


創建模型和遷移文件

💡 模型(Model) 是 Laravel Eloquent ORM 的核心組件之一,每個模型對應到資料庫中的一個表格,他也提供了進行 CRUD 操作的能力。

使用 Artisan 命令 php artisan make:model ModelName 來創建模型,通常會在 app/Models 目錄下創建一個模型類,然而每個模型通常對應一個數據表。
創建模型

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * 使用 create 或 update 方法時,只有 name、email 和 password 欄位可以被批量賦值
     * @var array
     */
    protected $fillable = ['name', 'email', 'password',];

    /**
     * 不能被批量賦值
     * @var array
     */
    protected $guarded = ['id'];

    /**
     * The attributes that should be hidden for serialization.
     */
    protected $hidden = ['password', 'remember_token',];

    /**
     * 定義模型屬性應如何轉換
     * is_active 屬性轉換為布林值
     * email_verified_at 屬性轉換為日期時間對象
     * @var array
     */
    protected $casts = ['is_active' => 'boolean', 'email_verified_at' => 'datetime',];

    /**
     * 自動轉換為 Carbon 實例的日期屬性
     * @var array
     */
    protected $dates = ['created_at', 'updated_at'];
}

💡 遷移文件(Migration) 用於定義資料表的結構,包括創建資料表、添加欄位、設置索引等操作。

使用 Artisan 命令 php artisan make:migration create_table_name 遷移文件用於定義數據表的結構,這些文件通常放在 database/migrations 目錄中,這會生成一個新的遷移文件,可以在裡面定義表的結構和欄位。
創建遷移文件

<?php

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

return new class extends Migration
{
    /**
     * Run the migrations. -> 定義如何創建或修改資料表
     *
     * @return void
     */
    public function up()
    {
        /**
         * 用 Schema 的 create 方法建立一個新的資料表
         * 第一個參數是表名
         * 第二個參數是一個閉包,Blueprint 實例的 table 屬性代表的是資料表的名稱
         * 
         */
        Schema::create('flights', function (Blueprint $table) {
            $table->id();                  // 自動增量的主鍵欄位
            $table->string('name');        // 名稱欄位
            $table->string('airline');     // 航線欄位
            $table->timestamps();          // 自動生成 created_at 和 updated_at 欄位
        });
    }

    /**
     * Reverse the migrations. -> 取消 up 方法所做的變更
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('flight');
    }
};

🔔 在 Blueprint 實例中,$table 屬性存儲了資料表的名稱。
Blueprint實例中,$table 是什麼

修改更新模型和遷移文件
通常更新資料,是要看上下文使用 laravel 方法,模型和遷移文件比較不需要變更。

  • step 1:引用模型 Model
    一般來說,這樣的感變通常寫在 service(邏輯處理),現階況先依照文件指引寫在控制器
    namespace App\Http\Controllers;
    
    use App\Models\RegisterUsers;
    
  • step 2:使用方法
    指定 RegisterUsers 模型查詢 id = 2 的資料,然後更新他的 email 變成 'aa@aa.com'
    變更前
    1. update() 更新儲存 -> 需要一個表示應該更新的列的列和值對數組。
      $user = RegisterUsers::query()->where('id', 2)->update(['email' => 'aa@aa.com']);
      
      變更後
    2. save() 更新 -> 更新資料庫中已經存在的模型
      $user = RegisterUsers::query()->find(2);
      $user->email = '123@aa.com';
      $user->save();
      
      save 後再次改回
    3. updateOrCreate 新增或更新 -> 如果不存在匹配的模型,您可能需要更新現有模型或建立新模型
      $user = RegisterUsers::query()->updateOrCreate(
         ['email' => 'aa@qwe.com', 'id' => 1],
         ['email' => 'no@qwe.com', 'password' => 'aa123']
      );
      
    4. sole() 檢索單一唯一的模型 -> 如果只要找出一條表中唯一的記錄
      RegisterUsers::query()->where(['email'=>'123@123.com'])->sole()
      

刪除模型和遷移文件

  • 刪除模型:呼叫模型實例的 delete 方法,然後可以呼叫 truncate 方法來刪除所有模型關聯的資料庫記錄。
    use App\Models\RegisterUsers;
    
    $user = RegisterUsers::find(1);
    
    $user->delete();
    
    RegisterUsers::truncate();
    
  • 軟刪除:軟刪除不會真的從資料庫中刪除記錄。
    • step 1:如果表格啟用,盡量不修改過去歷史避免覆蓋問題,可以跟開新的表格建立在相同表單名稱下(類似分支管理概念),下指令 php artisan make:migration add_soft_delete_on_register_users_table 建立新的遷移文件,然後建立欄位
      <?php
      
      use Illuminate\Database\Migrations\Migration;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Support\Facades\Schema;
      
      return new class extends Migration
      {
          /**
           * Run the migrations.
           */
          public function up(): void
          {
              Schema::table('register_users', function (Blueprint $table) {
                  // 軟刪除所需的 deleted_at 時間戳
                  $table->softDeletes();
              });
          }
      
          /**
           * Reverse the migrations.
           */
          public function down(): void
          {
              Schema::table('register_users', function (Blueprint $table) {
                  $table->dropSoftDeletes($column = 'deleted_at', $precision = 0);
              });
          }
      };
      
    • step 2:下指令 php artisan migrate 自動生成表格
      新增刪除欄位
    • step 3:在引入的位置用 delete() 方法進行軟刪除,執行後可以看到表格的 'deleted_at' 欄位有多一個時間戳
      $user = RegisterUsers::query()->where('email', 'no@qwe.com')->delete();
      
      啟動方法後的表格

運行遷移(php artisan migrate)


使用 Artisan 命令 php artisan migrate 運行遷移命令會根據遷移文件中的定義來創建或更新數據表結構,指令下後會執行所有尚未執行的遷移文件,並且將數據表創建或更新到數據庫中。
運行遷移

遷移指令

  • 目前已經執行了哪些遷移 -> php artisan migrate:status
    status
  • 會回滾最後「一批」的遷移 -> php artisan migrate:rollback
  • 回滾最後五個遷移 -> php artisan migrate:rollback --step=5
    rollback之前
    rollback之後
  • 運行所有未執行過的遷移 -> php artisan migrate
    重新建立表格
    打開表格觀察資料
  • 回滾已執行過的所有遷移,隨後會執行 migrate -> php artisan migrate:refresh
  • 刪除資料庫中的所有表,隨後執行指令 migrate -> php artisan migrate:fresh
    可以在 migrations table 的 batch 看到有下過幾次遷移

✍🏻 每日任務


專案已經建好以下表格,目前在 'register_users' 需要新增欄位 'name',要怎麼操作呢?

方法一:延伸合併
step 1:下指令 php artisan make:migration add_column_on_register_users_table 新增一個遷移文件,並且新增需求

重點 1 -> 表格名稱,因為要在同一張表格新增欄位,所以表格名稱要一致
重點 2 -> 因為是同一張表格不用重新創建,只需要用 Schema::table('表格名稱', 閉包放要新增的條件)

step 2:下指令 php artisan migrate 讓延伸表格合併

方法二:更新資料
step 1:下指令 php artisan migrate:rollback 回到過去,再新增欄位
step 2:下指令 php artisan migrate:fresh 通知他這個表格要重新確認更新

數據表和模型之間的關係(hasOne、hasMany、belongsTo、belongsToMany)


文件:模型关联
參考資料:「筆記」- 實體關聯圖[資料庫] 關聯介紹 一對一、一對多、多對多

hasOne:一對一關聯
模型 A 擁有一個模型 B。模型 B 只有一個模型 A 的實例。
例如,User 擁有一個 Profile。

// User.php
public function profile()
{
    return $this->hasOne(Profile::class);
}

hasMany:一對多關聯
模型 A 擁有多個模型 B。模型 B 可能對應到模型 A 的多個實例。
例如,Post 可以有多個 Comment。

// Post.php
public function comments()
{
    return $this->hasMany(Comment::class);
}

belongsTo:多對一關聯
模型 B 屬於模型 A。模型 A 可能擁有多個模型 B 的實例。
例如,Comment 屬於一個 Post。

// Comment.php
public function post()
{
    return $this->belongsTo(Post::class);
}

belongsToMany:多對多關聯
模型 A 和模型 B 之間有多對多的關聯。這種關聯通常需要一個中間表來存儲兩者之間的關係。
例如,Student 和 Course 之間的關係,學生可以選擇多門課程,每門課程也可以有多名學生。

// Student.php
public function courses()
{
    return $this->belongsToMany(Course::class);
}
// Course.php
public function students()
{
    return $this->belongsToMany(Student::class);
}

✍🏻 每日任務 - 一對多


按照 laravel 9.x 的模型关联創建一個部落格系統作為練習。
任務步驟:
step - 1:規劃有哪些模型要建立,並且思考彼此間的關聯
這裡只規劃發文 post 和評論 comment,而且一篇文章可以對好多則評論。
關聯

step - 2:建立模型和遷移檔案
用指令 php artisan make:model Post -mphp artisan make:model Comment -m 產生 PostComment 模型及遷移檔案。

step - 3:定義資料表結構 & 定義模型關係
create_posts_table 遷移檔案中新增欄位:title, content。
Post模型和遷移文件
create_comments_table 遷移檔案中新增欄位:post_id, content,並設定 post_id 為外鍵。
Comment模型和遷移文件

step - 4:運行遷移
指令:php artisan migrate

step - 5:測試 model 關聯

  • 方法 1:控制器

    $post = Post::query()->find(1); // 替換 1 為實際的 Post ID
    $comments = $post->comments;
    dd($comments->toArray());
    

    一對多關聯結果

  • 方法 2:Laravel Tinker

    Day13 laravel 使用 model 下篇

    • 指令 php artisan tinker 啟動 Tinker
    • 在 terminal 使用 Eloquent 語法,直接讀取資料庫,使用 Tinker 測試模型關係
      // 取得一個 Post 實例,這是測試 hasMany 關係
      $post = App\Models\Post::find(1); // 替換 1 為你實際的 Post ID
      
      // 取得該 Post 相關聯的 Comments
      $comments = $post->comments;
      
      // 取得一個 Comment 實例,這是測試 belongsTo 關係
      $comment = App\Models\Comment::find(1); // 替換 1 為實際的 Comment ID
      
      // 取得與該 Comment 關聯的 Post
      $post = $comment->post;
      

✍🏻 每日任務 - 多對多


假設我們有一個學生和課程的系統。
每位學生可以選修多個課程,每個課程也可以有多位學生,這就是一個多對多的關係。
step - 1:規劃有哪些模型要建立,並且思考彼此間的關聯
學生和課程的系統,代表需要學生名單和一個課程表,所以需要創建兩個模型:Student 和 Course。
它們之間透過一個中間表 course_student 進行關聯。
多對多

step - 2:建立模型和遷移檔案

php artisan make:model Student -m
php artisan make:model Course -m
php artisan make:migration create_course_student_table --create=course_student

step - 3:定義資料表結構 & 定義模型關係
create_students_table.php 遷移文件中新增欄位:name。
Student模型和遷移文件
create_courses_table.php 遷移文件中新增欄位:title。
Course模型和遷移文件
在中間表 create_course_student_table.php 的遷移文件中新增欄位:外鍵約束。
中間表

這裡不能再定義 id,定義後做遷移會提醒 「course_student」有多個主鍵
「course_student」有多個主鍵

step - 4:運行遷移
指令:php artisan migrate
students &amp; courses表格
course_student表格
step - 5:測試 model 關聯

  • 方法 1:控制器

    // 創建學生和課程
    $student = Student::create(['name' => 'John Doe']);
    $course = Course::create(['title' => 'Math 101']);
    
    // 關聯課程和學生
    $student->courses()->attach($course->id);
    
    // 檢查學生的課程
    $studentCourses = $student->courses;
    $studentCoursesTitles = $studentCourses->pluck('title')->toArray();
    
    // 檢查課程的學生
    $courseStudents = $course->students;
    $courseStudentsNames = $courseStudents->pluck('name')->toArray();
    
    return response()->json([
       'student_courses' => $studentCoursesTitles,
       'course_students' => $courseStudentsNames
    ]);
    
  • 方法 2:Laravel Tinker

    • 指令 php artisan tinker 啟動 Tinker
    • 在 terminal 使用 Eloquent 語法,直接讀取資料庫,使用 Tinker 測試模型關係
      // 創建學生和課程
      $student = App\Models\Student::create(['name' => 'John Doe']);
      $course = App\Models\Course::create(['title' => 'Math 101']);
      
      // 關聯課程和學生
      $student->courses()->attach($course->id);
      
      // 檢查學生的課程
      $studentCourses = $student->courses;
      foreach ($studentCourses as $course) {
          echo $course->title . "\n";
      }
      
      // 檢查課程的學生
      $courseStudents = $course->students;
      foreach ($courseStudents as $student) {
          echo $student->name . "\n";
      }
      

上一篇
第 4 天:控制器
下一篇
第 6 天:數據庫操作基礎
系列文
後端小白自學 Laravel30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
kuku
iT邦新手 4 級 ‧ 2024-09-29 21:52:24

延續第 5 天:數據模型與遷移知道模型之間有所謂的關聯,在建立中「後來」才需要加上關聯,可以用

  1. 有關聯的模型之間先建立橋樑
  2. 「新增欄位」的方法再建立一個遷移文件
  3. 指令 php artisan migrate -> 萬一失敗不要殺他!直接上 php artisan migrate:rollback --step=1

🔔 非必要,千萬不要輕易嘗試 php artisan migrate:refresh
🔔 使用了外鍵,要留意在遷移文件中設定的條件...

  1. 如果是要後來新增資料到資料庫,一開始一定是空值,測試很容易過不了,可以用字段修饰符 ->nullable() 允許先用 null 暫掛
  2. 索引中有介紹 unique 方法做唯一值,但是...這個很容易跟外鍵互相影響,個人踩雷多次,建議可以用其他方法取代,例如測試去判斷是不是唯一值

我要留言

立即登入留言