2

本节将学习 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(\App\Comment::class);
}

comments 方法中,我们并没有指定对应的外键,这是因为我们在定义迁移的时候,严格按照约定 (posts_id),因此 Laravel 会去自动寻找对应的外键。::class 方法也可以写成:

return $this->hasMany('\App\Comment');

一对多关系的作用

定义好了文章与评论的一对多关系之后,我们就可以方便的进行相关操作了,先来练习下:

$ php artisan tinker

为了方便操作,我们先允许评论内容 body 字段批量赋值:

/app/Comment.php
protected $fillable = ['body'];

首先是根据文章来直接创建评论:

>>> $post = \App\Post::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(\App\Post::class);
}

重启 tinker

>>> $comment = \App\Comment::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 App\Post;

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(),该方法生成一个重定向响应让用户返回到之前的位置。



心智极客
1k 声望646 粉丝