首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

Laravel Eloquent 關聯模型完整指南:從一對一到多態多對多

     編輯:PHP 綜合知識

前言

資料表之間的關聯是資料庫設計的核心。Laravel Eloquent 提供直觀、強大的關聯定義語法,支援六大類型:一對一、一對多、多對多、遠程一對多、多態關聯、多態多對多。本文將系統整理各類型的定義、反向關聯、中間表操作、查詢技巧與效能優化(預加載)。

關聯類型總覽

  • 一對一:hasOne / belongsTo
  • 一對多:hasMany / belongsTo
  • 多對多:belongsToMany(需中間表)
  • 遠程一對多:hasManyThrough(跨中間層)
  • 多態關聯:morphTo / morphMany(單表對應多模型)
  • 多態多對多:morphToMany / morphedByMany

一對一關聯

定義

// User.php
public function phone() {
    return $this->hasOne(Phone::class, 'foreign_key', 'local_key');
}

// Phone.php
public function user() {
    return $this->belongsTo(User::class, 'foreign_key', 'other_key');
}

慣例外鍵為 user_id,主鍵為 id,可透過第二、三參數自訂。

使用

$phone = User::find(1)->phone; // 動態屬性
$phone = User::find(1)->phone()->where('active', 1)->first(); // 查詢建構器

一對多關聯

定義

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

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

鏈式查詢

$comments = Post::find(1)->comments()->where('title', 'like', '%foo%')->get();

多對多關聯

資料表結構

users, roles, role_user (user_id, role_id)

定義

// User.php
public function roles() {
    return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id');
}

// Role.php
public function users() {
    return $this->belongsToMany(User::class, 'role_user', 'role_id', 'user_id');
}

中間表欄位存取

$user = User::find(1);
foreach ($user->roles as $role) {
    echo $role->pivot->created_at; // 需在關聯加上 ->withPivot('created_at')
}
// 自動維護時間戳
return $this->belongsToMany(Role::class)->withTimestamps();

依中間表欄位過濾

return $this->belongsToMany(Role::class)->wherePivot('approved', 1);

遠程一對多

範例:Country → User → Post。Country 沒有直接外鍵到 Post,透過 User 中介。

// Country.php
public function posts() {
    return $this->hasManyThrough(Post::class, User::class,
        'country_id', // User 表外鍵
        'user_id',    // Post 表外鍵
        'id',         // Country 主鍵
        'id'          // User 主鍵
    );
}

多態關聯

資料表結構

posts, comments, likes (likeable_id, likeable_type)

模型定義

class Like extends Model {
    public function likeable() { return $this->morphTo(); }
}
class Post extends Model {
    public function likes() { return $this->morphMany(Like::class, 'likeable'); }
}
class Comment extends Model {
    public function likes() { return $this->morphMany(Like::class, 'likeable'); }
}

自訂多態類型名稱

Relation::morphMap([
    'posts' => Post::class,
    'comments' => Comment::class,
]);

多態多對多

範例:Post、Video 共用 Tag。

// Post.php
public function tags() { return $this->morphToMany(Tag::class, 'taggable'); }

// Tag.php
public function posts() { return $this->morphedByMany(Post::class, 'taggable'); }
public function videos() { return $this->morphedByMany(Video::class, 'taggable'); }

關聯查詢進階

查詢關聯是否存在

Post::has('comments')->get(); // 至少一則評論
Post::has('comments', '>=', 3)->get(); // 三則以上
Post::has('comments.votes')->get(); // 巢狀關聯
Post::whereHas('comments', fn($q) => $q->where('content', 'like', 'foo%'))->get();

統計關聯數量

$posts = Post::withCount(['votes', 'comments' => fn($q) => $q->where('content', 'like', 'foo%')])->get();
echo $posts[0]->comments_count;

預加載解決 N+1 問題

// 未預加載:26 次查詢 (1 本書 + 25 作者)
$books = Book::all();
foreach ($books as $book) { echo $book->author->name; }

// 預加載:2 次查詢
$books = Book::with('author')->get();

// 多關聯預加載
$books = Book::with(['author', 'publisher'])->get();

// 巢狀預加載
$books = Book::with('author.contacts')->get();

// 條件預加載
$users = User::with(['posts' => fn($q) => $q->where('title', 'like', '%first%')])->get();

延遲預加載

$books = Book::all();
if ($someCondition) {
    $books->load('author', 'publisher');
    $books->load(['author' => fn($q) => $q->orderBy('published_date')]);
}

關聯模型寫入操作

新增

$post->comments()->save(new Comment(['message' => '...']));
$post->comments()->saveMany([...]);
$post->comments()->create(['message' => '...']); // 需 $fillable

多對多附加/移除

$user->roles()->attach($roleId, ['expires' => $expires]);
$user->roles()->detach($roleId);
$user->roles()->sync([1 => ['expires' => true], 2, 3]);

更新中間表

$user->roles()->updateExistingPivot($roleId, $attributes);

觸發父層時間戳

class Comment extends Model {
    protected $touches = ['post'];
    public function post() { return $this->belongsTo(Post::class); }
}

總結

熟練掌握 Eloquent 關聯,能以極少代碼表達複雜資料關係,並透過預加載、查詢建構器、統計方法大幅提升效能與可維護性。實務上建議:先設計資料表結構,再對應定義模型關聯,善用遷移檔管理 Schema。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved