本節將學習 Eloquent Relations
,表與表之間存在著多種關係,舉例如下:
-
一對一:文章與作者
-
一對多:文章與評論
-
多對多:標籤與文章
文章與評論的一對多關係
一對多關係,主要理解兩點:
-
如何實現一對多關係
-
實現了之後能給開發帶來什麼便利
一對多關係實現
首先建立 comments
相關:
$ php artisan make:model Comment -mc
同樣,為了遵循以前的約定,把生成的 CommentController
改成複數形式。
編輯遷移檔案:
/database/migrations/2017_04_15_062905_create_comments_table.php
public function up()
{
Schema::create(`comments`, function (Blueprint $table) {
$table->increments(`id`);
$table->unsignedInteger(`post_id`);
$table->string(`body`);
$table->timestamps();
$table->foreign(`post_id`)
->references(`id`)
->on(`posts`)
->onDelete(`cascade`);
});
}
我們為 comments
表格新增了 post_id
外來鍵,同時生定義了 onDelete cascade
約束,該約束允許刪除父表(文章)的時候,自動刪除關聯的子表(評論)。
最後,執行遷移:
$ php artisan migrate
接下來,我們就可以定義文章與評論的一對多關係了:
/app/Post.php
public function comments()
{
return $this->hasMany(AppComment::class);
}
在 comments
方法中,我們並沒有指定對應的外來鍵,這是因為我們在定義遷移的時候,嚴格按照約定 (posts_id
),因此 Laravel 會去自動尋找對應的外來鍵。::class
方法也可以寫成:
return $this->hasMany(`AppComment`);
一對多關係的作用
定義好了文章與評論的一對多關係之後,我們就可以方便的進行相關操作了,先來練習下:
$ php artisan tinker
為了方便操作,我們先允許評論內容 body
欄位批量賦值:
/app/Comment.php
protected $fillable = [`body`];
首先是根據文章來直接建立評論:
>>> $post = AppPost::first()
>>> $post->comments()->create([`body`=>`評論1`])
>>> $post->comments()->create([`body`=>`評論2`])
可以發現,我們可以根據文章的例項來直接建立對應的評論,而且不需要去確定評論post_id
欄位。
建立好之後,我們可以方便的獲取文章的評論:
>>> $post->comments;
我們傳入的是 comments
屬性而不是方法,Laravel 會返回該文章對應評論的集合,比如我們可以將其轉化為其他格式:
>>> $post->comments->toJson()
當然了,也可以使用 comments()
方法返回 Eloquent 模型,再進行進一步操作:
>>> $post->comments()->get()->toArray()
>>> $post->comments()->pluck(`body`)
同樣的,如果我們要根據評論來操作相關文章,我們需要先定義評論與文章的多對一關係:
/app/Comment.php
public function post()
{
return $this->belongsTo(AppPost::class);
}
重啟 tinker
:
>>> $comment = AppComment::first()
>>> $comment->post;
>>> $comment->post->title;
評論的顯示與建立
顯示評論
顯示評論,比較簡單,直接使用 `Bootstrap 的 card 模板即可:
/resources/views/posts/show.blade.php
<div class="blog-post">
<h2 class="blog-post-title">{{ $post->title }}</h2>
<p class="blog-post-meta">{{ $post->created_at->toFormattedDateString() }} by <a href="#">Zen</a></p>
<p>{{$post->body}}</p>
</div>
@foreach ($post->comments as $comment)
<div class="card">
<div class="card-header">
{{$comment->created_at->diffForHumans() }}
</div>
<div class="card-block">
<p class="card-text">{{ $comment->body }}</p>
</div>
</div>
<br>
@endforeach
同時,我們使用了 Carbon
的 diffForHumans
方法,用來顯示「距離現在多久」。
建立評論
最後是評論的建立,首先是檢視,放在顯示評論下方即可:
/resources/views/posts/show.blade.php
<div class="card">
<div class="card-header">
新增評論
</div>
<div class="card-block">
<form method="post" action="/posts/{{$post->id}}/comments">
{{ csrf_field() }}
<fieldset class="form-group">
<textarea class="form-control" id="body" rows="3" name="body" required placeholder="請輸入評論內容"></textarea>
</fieldset>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
</div>
對應的路由:
Route::post(`/posts/{post}/comments`,`CommentsController@store`);
最後是控制器:
<?php
use AppPost;
class CommentsController extends Controller
{
public function store(Post $post)
{
$this->validate(request(),[
`body` => `required|min:5`
]);
$post->addComment(request(`body`));
return back();
}
}
首先,依舊是使用路由模型的自動繫結功能,然後將新增評論的方法進行封裝,方便重複使用:
/app/Post.php
public function addComment($body)
{
$this->comments()->create(compact(`body`));
}
最後使用輔助方法 back()
,該方法生成一個重定向響應讓使用者返回到之前的位置。