在 Laravel 中,Eloquent ORM(物件關聯映射)提供了與資料庫表互動的強大方法。在 Eloquent 中處理關係時,我們經常會遇到三種基本方法:sync、attach 和 detach。這些方法對於管理應用程式中模型之間的關係至關重要。在本文中,將探討這些方法之間的差異,並探索何時以及如何有效地使用它們。
Relationships in Laravel
Laravel 支援多種類型的模型關係(Relationships),包括:
這些關係使我們能夠在不同的資料庫表上連接相關資料。
Attach(附加):新增記錄附加方法主要用於多對多關係
用於在連接兩個模型的資料透視表中新增記錄。例如,假設一個使用者可以擁有多個角色,一個角色也可以擁有多個使用者。你可以使用 Attach 方法,透過在關係的中間表中插入一筆記錄,將角色附加到使用者:使用方法如下:
use App\Models\User;
$user = User::find(1);
$user->roles()->attach($roleId);Detach(分離):從多對多關係中刪除記錄
有時可能需要從使用者中刪除角色。若要刪除多對多關係記錄,請使用 detach 方法。 detach 方法將從中間表中刪除對應的記錄;但是,兩個模型仍將保留在資料庫中:
// Detach a single role from the user...
$user->roles()->detach($roleId);
// Detach all roles from the user...
$user->roles()->detach();sync:在多對多關係中同步記錄
sync 方法是一種強大的多對多關係記錄同步方法。它以相關模型 ID 的陣列作為參數,並確保資料透視表僅包含這些記錄。任何不在所提供數組中的現有記錄都將被刪除:
$roleIds = [1, 2, 3];
$user->roles()->sync($roleIds);ER-Model
假設文章(Blog)與 標籤(Tag )的 ORM (物件關聯映射)如下圖

建立 Migration
建立 blog_post_tag 表格,作為 blog_posts & blog_tags 的關連
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 標籤
Schema::create('blog_tags', function (Blueprint $table) {
$table->id();
$table->string('name');
});
// 部落格文章
Schema::create('blog_posts', function (Blueprint $table) {
$table->id(); //Primary Key
$table->string('title');
$table->text('content')->nullable();
});
Schema::create('blog_post_tag', function (Blueprint $table) {
$table->foreignId('blog_post_id');
$table->foreignId('blog_tag_id');
$table->primary(['blog_post_id', 'blog_tag_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('blog_post_tag');
Schema::dropIfExists('blog_tags');
Schema::dropIfExists('blog_posts');
}
};建立 Model: BlogPost
在 Model 中用 Eloquent 使用 belongsToMany 來將 blog_posts & blog_tags 關連起來。
class BlogPost extends Model
{
use HasFactory, CreatedUpdatedBy, BlogPostTrait;
protected $fillable = [
'title',
'content',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function tags()
{
return $this->belongsToMany(BlogTag::class,'blog_post_tag');
}
}新增文章時(createPost)
在下面的程式碼中,需要先建立 標籤(Tag ) 的資料,然後再使用 $post->tags()->sync($tag_id); 就會自動在 table: blog_post_tag 建立相對應的記錄了。
/**
* 新增文章
*/
public function createPost(array $data): ?Model
{
$form_data = $data['form_data'] ?? [];
// 驗證資料
$validator = Validator::make($form_data, [
'title' => 'required|string|max:255',
'user_id' => 'required|exists:users,id',
'blog_category_id' => 'required|exists:blog_categories,id',
'tags' => 'array'
]);
if ($validator->fails()) {
throw new \InvalidArgumentException($validator->errors()->first());
}
// 文章/tag...儲存
return DB::transaction(function () use ($form_data) {
// 文章
$post = self::create([
'title' => $form_data['title'],
'content' => $form_data['content'],
'user_id' => $form_data['user_id']
]);
// tag
if (!empty($form_data['tags'])) {
// 先尋找 tag 是否存在, 不存在就新增
$i = 0;
foreach ($form_data['tags'] as $tag) {
// Retrieve flight by name or create it if it doesn't exist...
$t = BlogTag::firstOrCreate([
'name' => $tag
]);
$tag_id[$i] = $t->id;
$i++;
}
// 更新table: blog_post_tag 關連
$post->tags()->sync($tag_id);
}
return $post;
});
}更新資料時(update)
更新與新增資料大致相同,需要先 找到 或 建立 標籤(Tag ) 的資料,然後再使用 $post->tags()->sync($tag_id); 就會自動在 table: blog_post_tag 建立相對應的記錄了。
/**
* 更新作業
* Update the specified resource in storage.
*/
public function update(Request $request)
{
$input = $request->all();
$form_data = $request->form_data;
$id = $form_data['id'];
$post = BlogPost::findOrFail($id);
// 開始儲存記錄
DB::transaction( function() use ($post, $form_data) {
$post->update($form_data);
// tag
if (!empty($form_data['tags'])) {
// 先尋找 tag 是否存在, 不存在就新增
$i=0;
foreach ($form_data['tags'] as $tag) {
// Retrieve flight by name or create it if it doesn't exist...
$t = BlogTag::firstOrCreate([
'name'=>$tag
]);
$tag_id[$i] = $t->id;
$i++;
}
// 更新table: blog_post_tag 關連
$post->tags()->sync($tag_id);
}
});
}
return $this->sendResponse($post, __('response.data_updated_successfully', [], env('APP_LOCALE')));
}刪除資料時(deletePost)
在刪除文章後,只要使用 $post->tags()->detach(); ,就可以移除table: blog_post_tag 相對應的記錄了
/**
* 刪除單一記錄
* @param int $id
* @return bool
*/
public function deletePost(int $id): bool
{
$post = self::findOrFail($id);
$post->tags()->detach();
return $post->delete();
}以上適用於大多數的基本用法



