10. 更新数据及渴求式加载Eager Loading - 从零开始学Laravel

从零学Laravel目录列表

读取数据,插入数据我们都学过了,还有一个非常重要的就是更新数据。

我们打开这个链接:http://localhost:8000/posts/2

QQ20161121-0.png

下面我们要做两件事:

  1. 我们要编辑这个评论。
  2. 建立这个评论与用户之间的关系,通常我们要知道是谁发表了这个评论.

好,我们先来做编辑评论的这个功能,简单的流程是我点击这个评论,跳转到编辑评论的页面,然后保存这个评论。

我们先来想一下,显示编辑评论的这个页面的路由该怎么写,通常是这样表达的,我要编辑某个帖子下的某个评论,那可能会这么写:

// 编辑属于帖子ID为3的评论ID为1的评论
// posts/3/comments/1/edit
posts/{post}/comments/{comment}/edit

嗯,上面的路由可以清晰的表达我们想要的意思,能很好的表达出具体功能的意思,但是路径太深了,我们简化下:

Route::get('comments/{comment}/edit', 'CommentsController@edit');

好的,我们到CommentsController中写上edit()方法:

    public function edit(Comment $comment)
    {
        // 加载视图层,并传递$comment数据到视图
        return view('comments.edit', compact('comment'));
    }

然后,我们到pages/show.blade.php中为我们的评论都加上一个编辑的链接:

<ul class="list-group">
    @foreach ($post->comments as $comment)
        <li class="list-group-item">
            {{ $comment->content }}
            <a href="/comments/{{ $comment->id }}/edit">Edit</a>
        </li>
    @endforeach
</ul>

下一步呢,当然是建立我们的comments/edit.balde.php视图层,路径为resources/views/comments/edit.balde.php

@extends('layout')

@section('content')
    <h1>Edit the Comment</h1>

    <form method="{{-- 未知 --}}" action="{{-- 这里的路径现在还没写 --}}">
        {{ csrf_field() }}
        <div class="form-group">
            <textarea name="content" class="form-control">{{ $comment->content }}</textarea>
        </div>

        <div class="form-group">
            <button type="submit" class="btn btn-primary">Update Comment</button>
        </div>
    </form>
@stop

访问:http://localhost:8000/posts/2 点击Edit,跳转到http://localhost:8000/comments/11/edit页面,如下:

编辑帖子页

然后当我们点击Update Comment按钮时,这时候我们的路由该怎么写,对于将一条数据存入到数据库,我们用post方式,而对于更新一条数据,我们应该使用patchput方式,我们来写这个路由:

// 更新评论ID为X的评论
Route::patch('comments/{comment}', 'CommentsController@update');

进入CommentsController编写update()方法:

    public function update(Request $request, Comment $comment)
    {
        // update()只会更新Comment模型中$fillable允许的字段
        $comment->update($request->all());

        // 跳转到该评论所属的帖子页
        return redirect('posts/' . $comment->post->id);
    }

下面修改下视图:

@extends('layout')

@section('content')
    <h1>Edit the Comment</h1>

    <form method="POST" action="/comments/{{ $comment->id }}">
        {{ method_field('PATCH') }}
        {{ csrf_field() }}
        <div class="form-group">
            <textarea name="content" class="form-control">{{ $comment->content }}</textarea>
        </div>

        <div class="form-group">
            <button type="submit" class="btn btn-primary">Update Comment</button>
        </div>
    </form>
@stop

上面有一点要注意,我们现在是使用了PATCH的请求,但是表单中的method只能识别getpost方法,所以对于patch, put, delete这些方法,我们要这么写:

    <form method="POST" action="">
        {{ method_field('PATCH') }}
    </form>

当然也可以直接这么写:

    <form method="POST" action="">
        <input type="hidden" name="_method" value="PATCH">
    </form>

我们的编辑评论的最简单的功能做完了,下面我们看下如何为评论添加用户,我们先创建一个users的migration文件,默认安装laravel的时候都是有这个文件的,我们最初把它删除了,现在再来创建一下:

php artisan make:migration create_users_table --create=users 

编辑下这个文件的up()函数:

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('username')->unique();
            $table->string('email')->unique();
            $table->string('password');
            $table->timestamps();
        });
    }

然后在create_comments_table.php这个migration文件中,添加user_id外键:

$table->integer('user_id')->unsigned()->index();

现在如果我们直接执行php artisan migrate, users表可以被创建,但是我们在comments的migration文件中添加的user_id外键是不会被创建的,对于这种添加一个字段,我们也要单独写一个migration文件。

不过在项目的初期,我们也可以直接更改migration文件,然后执行:

php artisan migrate:refresh

上面这条命令会清空所有的表并重新创建,需慎用, 如果只想撤销上一次的migrate,可以使用php artisan migrate:rollback命令,该命令会执行migration文件中的down()方法。

这里我们执行下php artisan migrate:refresh

我们进入tinker, 重新插入一些数据:

>>> namespace App;
=> null
>>> $user = New User;
=> App\User {#634}
>>> $user->username = 'zhoujiping';
=> "zhoujiping"
>>> $user->email = 'zhoujiping@zhoujiping.com';
=> "zhoujiping@zhoujiping.com"
>>> $user->password = bcrypt('123456');
=> "$2y$10$OM9pBb.xU58hvulnyhq1jeiWPxi8SbddYBW.rhhttEJMraBuVyWdq"
>>> $user->save();
=> true
>>> $post = New Post;
=> App\Post {#627}
>>> $post->title = 'My First Post';
=> "My First Post"
>>> $post->save();
=> true
>>> $comment = New Comment;
=> App\Comment {#639}
>>> $comment->user_id = 1;
=> 1
>>> $comment->content = 'The content of the comment';
=> "The content of the comment"
>>> $post->addComment($comment);
=> App\Comment {#639
     user_id: 1,
     content: "The content of the comment",
     post_id: 1,
     updated_at: "2016-11-21 06:02:33",
     created_at: "2016-11-21 06:02:33",
     id: 1,
   }

我们再到pages/show.blade.php视图,加上用户的用户名:

<ul class="list-group">
    @foreach ($post->comments as $comment)
        <li class="list-group-item">
            {{ $comment->content }}
            <a href="/comments/{{ $comment->id }}/edit">Edit</a>
            <a href="#" class="pull-right">{{ $comment->user->username }}</a>
        </li>
    @endforeach
</ul>

不要忘记去Comment.php中写上评论与用户的关系

    public function user()
    {
        return $this->belongsTo(User::class);
    }

像上面这样写,程序跑通没有问题,但是我们看{{ $comment->user->username }}这条语句会在每次循环的时候都去查询一次用户表信息,导致数据库的查询次数过多,我们看一下:

数据库查询次数

如何解决这个问题呢,laravel提供了热加载和懒加载的方式可以解决这个问题,我们进入到
postsController文件中,修改下show()函数:

    public function show(Post $post)
    {
        $post = Post::with('comments.user')->find($post->id);

       return view('posts.show', compact('post'));
    }

我们看这句话Post::with('comments.user'), 意思是查询的时候加载post的关系comments, 而.user是指加载comments的关系时,还需要加载comments的关系user。 这样就能解决多次查询的问题了。

我们把上面的代码优化下,因为我们已经做了Post的路由模型绑定,我们可以使用懒加载,所以代码改成这样:

    public function show(Post $post)
    {
        $post->load('comments.user');
        
        return view('posts.show', compact('post'));
    }

在看下我们的语句查询次数:


查询次数

关于热加载和懒加载的具体区别,以后再说,大家现在可以不用区分的去使用它们。

好,本节到这里结束。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容