Laravel 8 如何通过Eloquent ORM实现带自定义属性的关联关系

Laravel Eloquent 带额外属性关联实现方案
你需要实现的是多对多关联附带中间表额外字段的需求,具体实现步骤如下:
1. 定义中间表迁移
以作者(authors)和书籍(books)的关联为例,中间表author_book需要携带贡献排名、合同编号两个额外属性,迁移代码如下:
Schema::create('author_book', function (Blueprint $table) { $table->foreignId('author_id')->constrained(); $table->foreignId('book_id')->constrained(); $table->integer('contribution_rank'); // 额外属性:作者对本书的贡献排名 $table->string('contract_no'); // 额外属性:合作合同编号 $table->primary(['author_id', 'book_id']); // 设置联合主键 });
2. 模型中声明带中间字段的关联
定义关联时使用withPivot方法指定需要读取的额外中间字段:
// App/Models/Author.php public function books() { return $this->belongsToMany(Book::class) ->withPivot('contribution_rank', 'contract_no') ->withTimestamps(); // 中间表有created_at/updated_at字段时添加 } // App/Models/Book.php public function authors() { return $this->belongsToMany(Author::class) ->withPivot('contribution_rank', 'contract_no'); }
3. 常用操作示例
- 读取中间表属性
$author = Author::with('books')->first(); foreach ($author->books as $book) { // 直接通过pivot属性访问中间表字段 echo $book->pivot->contribution_rank; echo $book->pivot->contract_no; } - 新增关联时携带额外属性
// 单条关联新增 $author->books()->attach($bookId, [ 'contribution_rank' => 1, 'contract_no' => 'CT2024001' ]); // 批量关联新增 $author->books()->attach([ 1 => ['contribution_rank' => 1, 'contract_no' => 'CT2024001'], 3 => ['contribution_rank' => 2, 'contract_no' => 'CT2024002'] ]); - 更新已有关联的中间属性
$author->books()->updateExistingPivot($bookId, [ 'contract_no' => 'CT2024003' ]); - 自定义pivot别名:如果不想用默认的
pivot作为属性名,可以用as方法重命名// 关联定义时添加as方法 public function books() { return $this->belongsToMany(Book::class) ->withPivot('contribution_rank', 'contract_no') ->as('authorship'); } // 访问时使用自定义别名 echo $book->authorship->contribution_rank;
进阶:自定义中间表模型(可选)
如果中间表有独立的业务逻辑,可以自定义中间表模型继承Pivot类:
use Illuminate\Database\Eloquent\Relations\Pivot; class AuthorBook extends Pivot { protected $casts = [ 'contribution_rank' => 'integer' ]; // 可以在这里添加中间表专属的作用域、方法等逻辑 }
关联定义时通过using方法指定自定义中间表模型即可:
public function books() { return $this->belongsToMany(Book::class) ->using(AuthorBook::class) ->withPivot('contribution_rank', 'contract_no'); }
内容的提问来源于stack exchange,提问作者Ihda Anwari
相关产品推荐
相关产品推荐

