Laravel 5.4 入門系列 8. 文章評論

心智極客發表於2019-02-16

本節將學習 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

同時,我們使用了 CarbondiffForHumans 方法,用來顯示「距離現在多久」。

建立評論

最後是評論的建立,首先是檢視,放在顯示評論下方即可:

/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(),該方法生成一個重定向響應讓使用者返回到之前的位置。


相關文章