weixin_39838231
weixin_39838231
2021-01-09 10:37

2016 版 Laravel 系列入门教程(四)【最适合中国人的 Laravel 教程】

本教程示例代码见:https://github.com/johnlui/Learn-Laravel-5

在任何地方卡住,最快的办法就是去看示例代码。

本篇文章中,我将跟大家一起实现 Article 的新增、编辑和删除功能,仔细解读每一段代码,相信本篇文章看完,你就能够 get Laravel 使用之道。

RESTful 资源控制器

资源控制器是 Laravel 内部的一种功能强大的约定,它约定了一系列对某一种资源进行“增删改查”操作的路由配置,让我们不再需要对每一项需要管理的资源都写 N 行重复形式的路由。中文文档见:http://www.golaravel.com/laravel/docs/5.1/controllers/#restful-resource-controllers

我们只需要写一行简单的路由:

 php
Route::resource('photo', 'PhotoController');

就可以得到下面 7 条路由配置:

左边是 HTTP 方法,中间是 URL 路径,右边是 控制器中对应的函数,只要某个请求符合这七行中某一行的要求,那么这条请求就会触发最后一列的方法。这是 Laravel 对于 RESTful 的规范,它不仅仅帮我们省去了几行路由配置代码,更是如何合理规划 URL 的指路明灯,相信你会从中学到很多。

下面我们正式开始一项一项地实现 Article 的新增、编辑、删除功能:

开始行动

配置资源路由

将当前路由配置中的 Route::get('article', 'ArticleController'); 改成 Route::resource('article', 'ArticleController');,哦了。

新增 Article

新增一篇文章需要两个动作:第一步,获取“新增Article”的页面;第二步,提交数据到后端,插入一篇文章到数据库。

获取“新增Article”的页面

第二行路由可以满足我们的需求:

那下一步就明了了,在 ArticleController 中新增 create 方法,放回一个可以输入文章的页面:

ArticleController 中:

 php
public function create()
{
    return view('admin/article/create');
}

新增视图文件 learnlaravel5/resources/views/admin/article/create.blade.php

 php
('layouts.app')

('content')
<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">新增一篇文章</div>
                <div class="panel-body">

                     (count($errors) > 0)
                        <div class="alert alert-danger">
                            <strong>新增失败</strong> 输入不符合要求<br><br>
                            {!! implode('<br>', $errors->all()) !!}
                        </div>


                    <form action="%7B%7B%20url('admin/article')%20%7D%7D" method="POST">
                        {!! csrf_field() !!}
                        <input type="text" name="title" class="form-control" required="required" placeholder="请输入标题">
                        <br>
                        <textarea name="body" rows="10" class="form-control" required="required" placeholder="请输入内容"></textarea>
                        <br>
                        <button class="btn btn-lg btn-info">新增文章</button>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>

点击文章管理页面最上面的“新增”按钮,你将得到以下页面:

视图调用

上文中我使用 return view('admin/article/create'); 返回了视图文件。

view() 方法是 Laravel 中一个全局的方法,用于调用视图文件,他接受一个字符串参数,并会按照这个参数去调取对应的路由,这很容易理解。实际上 'admin/article/create''admin.article.create' 是等价的,而且看起来后者更加优雅,不过本宝宝十分推荐前者。代码优雅是好事儿,不过本质上代码是写给人看的,一切提高代码理解成本的行为都是错误的。

提交数据到后端

“新增Article”的页面已经展示出来,下一步就是提交数据到后端了,理解提交数据,要从表单开始。

表单

视图文件中有一个表单:

 php
<form action="%7B%7B%20url('admin/article')%20%7D%7D" method="POST">
    {!! csrf_field() !!}
    <input type="text" name="title" class="form-control" required="required" placeholder="请输入标题">
    <br>
    <textarea name="body" rows="10" class="form-control" required="required" placeholder="请输入内容"></textarea>
    <br>
    <button class="btn btn-lg btn-info">新增文章</button>
</form>

这是一个非常普通的 form 表单,只有两点需要我们费点心思去理解。

第一,表单的 action。form 是 HTML 规范,在点击了表单中的提交按钮后,浏览器会使用 method 方法将某些数据组装好发送给 action,这里我们动态生成了一个 URL 作为 action,并且指定了表单提交需要使用 POST 方法。

第二,csrf_field。这是 Laravel 中内置的防止应对 CSRF 攻击的防范措施,任何 POST PUT PATCH 请求都会被检测是否提交了 CSRF 字段。

 php
{!! csrf_field() !!}

实际上会生成一个隐藏的 input:

 php
<input type="hidden" name="_token" value="GYZ8OHDAbZICMcEvcTiS82qlZs2XrELklpEl159S">

这一行也可以这么写:

 php
<input type="hidden" name="_token" value="{{ csrf_token() }}">

如果你的系统有很多的 Ajax,而你又不想降低安全性,这里的 csrf_token() 函数将会给你巨大的帮助。

后端接收数据

我们在页面中填入了一些数据,点击了提交按钮,这条请求会被分配到那里呢?

第三行解答了我们的问题。好了,新建 store 方法:

 php
public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|unique:articles|max:255',
        'body' => 'required',
    ]);

    $article = new Article;
    $article->title = $request->get('title');
    $article->body = $request->get('body');
    $article->user_id = $request->user()->id;

    if ($article->save()) {
        return redirect('admin/article');
    } else {
        return redirect()->back()->withInput()->withErrors('保存失败!');
    }
}

检验成果

填入数据:

点击按钮,页面跳转到“文章管理”页,将此页面拉到最底部:

恭喜你,文章新增成功!

详细注释

下面我已注释的形式细细解析每一段代码的作用:

 php
public function store(Request $request) // Laravel 的依赖注入系统会自动初始化我们需要的 Request 类
{
    // 数据验证
    $this->validate($request, [
        'title' => 'required|unique:articles|max:255', // 必填、在 articles 表中唯一、最大长度 255
        'body' => 'required', // 必填
    ]);

    // 通过 Article Model 插入一条数据进 articles 表
    $article = new Article; // 初始化 Article 对象
    $article->title = $request->get('title'); // 将 POST 提交过了的 title 字段的值赋给 article 的 title 属性
    $article->body = $request->get('body'); // 同上
    $article->user_id = $request->user()->id; // 获取当前 Auth 系统中注册的用户,并将其 id 赋给 article 的 user_id 属性

    // 将数据保存到数据库,通过判断保存结果,控制页面进行不同跳转
    if ($article->save()) {
        return redirect('admin/article'); // 保存成功,跳转到 文章管理 页
    } else {
        // 保存失败,跳回来路页面,保留用户的输入,并给出提示
        return redirect()->back()->withInput()->withErrors('保存失败!');
    }
}

编辑 Article

这两行路由配置可以满足我们的需求:

上面一行:展示“编辑某一篇文章”的页面;下面一行:上传数据更新这篇文章。

这个就当做第二个小作业留给你们,尝试自己去构建吧~这里面还有个小坑,参考我的代码就可以迅速地解决呦~

删除 Article

删除某个资源跟新增、编辑相比最大的不同就是运行方式的不同:删除按钮看起来是一个独立的按钮,其实它是一个完整的表单,只不过只有这一个按钮暴露在页面上:

 php
<form action="%7B%7B%20url('admin/article/'.%24article->id)%20%7D%7D" method="POST" style="display: inline;">
    {{ method_field('DELETE') }}
    {{ csrf_field() }}
    <button type="submit" class="btn btn-danger">删除</button>
</form>

大家可能注意到了这句代码 {{ method_field('DELETE') }},这是什么意思呢?这是 Laravel 特有的请求处理系统的特殊约定。虽然 DELETE 方法在 RFC2616 中是可以携带 body 的(甚至 GET 方法都是可以携带的),但是由于历史的原因,不少 web server 软件都将 DELETE 方法和 GET 方法视作不可携带 body 的方法,有些 web server 软件会丢弃 body,有些干脆直接认为请求不合法拒绝接收。所以在这里,Laravel 的请求处理系统要求所有非 GET 和 POST 的请求全部通过 POST 请求来执行,再将真正的方法使用 _method 表单字段携带给后端代码。上面小作业中的小坑便是这个,PUT/PATCH 请求也要通过 POST 来执行。

另外,这里还有一些小问题:PUT/PATCH 请求通过 POST 方法执行仅限于 form 提交,对 Ajax 请求目前来看是无效的,看来我要去给 Laravel 提交一个 patch 了。🐳

在控制器中增加删除文章对应的是 destroy 方法:

 php
public function destroy($id)
{
    Article::find($id)->delete();
    return redirect()->back()->withInput()->withErrors('删除成功!');
}

点击删除按钮,检验效果:

恭喜你,文章新增、编辑、删除功能构建成功!

下一步:2016 版 Laravel 系列入门教程(五)【最适合中国人的 Laravel 教程】

该提问来源于开源项目:johnlui/Learn-Laravel-5

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

35条回答

  • weixin_39758229 weixin_39758229 4月前

    I have followed the steps to finish the function of store and i tried to understand the code ultimately. But when i finished the update function , errors generated. It always shows "BadMethodCallException Method [show] does not exist.".

    Here is my code. image image image

    I serched in google but most of the answers indicated that was the fault of "ValidatesRequests not in use" or "resource route problem". I don't think it was a good idea to fix it.

    Now what i should do?

    点赞 评论 复制链接分享
  • weixin_39651735 weixin_39651735 4月前

    我英文不太好,看你的代码,你不应该使用method=PUT

    You should not use the "method = PUT"

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    Laravel 5 以后 PUT DELETE 方法都是通过 POST 方法来实现的

    点赞 评论 复制链接分享
  • weixin_39528289 weixin_39528289 4月前

    very good.

    点赞 评论 复制链接分享
  • weixin_39889487 weixin_39889487 4月前

    更新文章后的跳转页面总是出错啊,它会加上id。怎么改啊?

    点赞 评论 复制链接分享
  • weixin_39895181 weixin_39895181 4月前

    csrf_token是在这里校验的

    
    \App\Http\Middleware\VerifyCsrfToken
    
    点赞 评论 复制链接分享
  • weixin_39573535 weixin_39573535 4月前

    删除的参数$id是怎么来的有个说法么?怎么就来了一个参数的?请求url里面虽然带了文章的article的id,但是执行删除的函数怎么就知道那个是参数呢?

    点赞 评论 复制链接分享
  • weixin_39871562 weixin_39871562 4月前

    ErrorException in ViewErrorBag.php line 82: call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Support\MessageBag' does not have a method 'getAll' (View: F:\learnlaravel5\resources\views\admin\article\index.blade.php) 删除后跳转的报错,有没有遇到这样的问题的?怎么解决

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    这个错误看起来是一个版本兼容的问题,报某个方法不存在

    点赞 评论 复制链接分享
  • weixin_39871562 weixin_39871562 4月前

    谢谢,我写错了,模板里的$errors->all()写成getAll()了,是5.2的版本

    点赞 评论 复制链接分享
  • weixin_39708822 weixin_39708822 4月前

    woca我擦,不会写,有没有谁做好的,借我抄抄,发个做好的也好,多谢,多谢,

    点赞 评论 复制链接分享
  • weixin_39651735 weixin_39651735 4月前

    那下一步就明了了,在 ArticleController 中新增 create 方法,放回一个可以输入文章的页面

    应该是返回哦

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    感谢~

    点赞 评论 复制链接分享
  • weixin_39751453 weixin_39751453 4月前

    写的很棒,话说我第一次看博主文章实在iOS开发里的

    点赞 评论 复制链接分享
  • weixin_39623355 weixin_39623355 4月前

    解释一下写update的时候

    
    $this->validate($request, [
        'title' => 'required|unique:articles,title,' . $id . '|max:255',
        'body' => 'required',
    ]);
    

    的问题。

    如果这个validate里面的规则照搬store的话,修改时若不改标题就提交,会显示错误

    The title has already been taken.

    文档里面给出了unique的用法: unique:table,column,except,idColumn,上面的代码中的$id所占位置即是except的位置,意思是检查除了$id的title以外是否唯一。

    除此之外,5.3.18以后引入了Rule类,所以也可以写

    
    use Illuminate\Validation\Rule;
    
            $this->validate($request, [ // 排除掉当前编辑的数据
                'title' => [
                    'required',
                    'max:255',
                    Rules::unique('articles','title')->ignore($id)
                    ],
                'body' => 'required',
            ]);
    
    
    点赞 评论 复制链接分享
  • weixin_39769740 weixin_39769740 4月前

    请问一下,csrf_token生成在哪校验的

    点赞 评论 复制链接分享
  • weixin_39808877 weixin_39808877 4月前

    另外,这里还有一些小问题:PUT/PATCH 请求通过 POST 方法执行仅限于 form 提交,对 Ajax 请求目前来看是无效的,看来我要去给 Laravel 提交一个 patch 了。:whale:

    ajax用表单方式通过_methon字段也可以把真正的方法传给后端吧?

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    可以

    点赞 评论 复制链接分享
  • weixin_39541212 weixin_39541212 4月前

    大作页已写

    么么哒

    点赞 评论 复制链接分享
  • weixin_39946798 weixin_39946798 4月前

    现在删除功能是关联表的id列 , 我如果想关联表中的title 列怎么办?请指教

    点赞 评论 复制链接分享
  • weixin_39630735 weixin_39630735 4月前

    例如store(Request $request)这样,变量带个修饰符是什么意思,好像php得变量和属性都没有这些修饰符

    点赞 评论 复制链接分享
  • weixin_39607710 weixin_39607710 4月前

    update 的提交表单一直报错不能提交,是什么原因?把title改了才可以,

    the title has aready token.

    点赞 评论 复制链接分享
  • weixin_39645308 weixin_39645308 4月前

    Request 是类名 -> http://php.net/manual/zh/language.oop5.typehinting.php

    点赞 评论 复制链接分享
  • weixin_39663933 weixin_39663933 4月前

    我没啥问题,就是觉得样例图有些显示不完全的。

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    添加例外可以在 app/Http/Middleware/VerifyCsrfToken.php 中给 protected $except 数组增加一个元素呦

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    看这篇文章就明白喽 https://lvwenhan.com/laravel/423.html

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    PHP 新引入的“参数类型”,用来明确参数的类型。这里主要用于“自动依赖注入”来自动生成、注册 $request 对象。

    点赞 评论 复制链接分享
  • weixin_39574928 weixin_39574928 4月前

    问个小白问题

    create.blade.php中的新增按钮执行后,如何跟ArticleController.php中的store(Request $request)对应起来?

    同样问题,在index.blade.php中的删除按钮执行后,如何跟ArticleController.php中的destroy($id)对应

    点赞 评论 复制链接分享
  • weixin_39541212 weixin_39541212 4月前

    method_field(method)

    点赞 评论 复制链接分享
  • weixin_39911066 weixin_39911066 4月前

    模板复制create的模板 主要修改的地方 <form action="{{ url('admin/article/'.$article->id) }}" method="POST"> 以及 {{ method_field('PUT') }}
    PHP文件修改: public function edit($id) { $article = Article::find($id); return view('admin/article/edit')->with('article', $article); } `public function update($id, Request $request) { $this->validate($request, [ // 排除掉当前编辑的数据 'title' => 'required|unique:articles,title,' . $id . '|max:255', 'body' => 'required', ]);

        $article          = Article::findOrFail($id);
        $article->title   = $request->get('title');
        $article->body    = $request->get('body');
        $article->user_id = $request->user()->id;
    
        if ($article->save()) {
            return redirect('admin/article');
        } else {
            return redirect()->back()->withInput()->withErrors('保存信息失败!');
        }
    }`
    
    点赞 评论 复制链接分享
  • weixin_39713833 weixin_39713833 4月前

    用的laravel5.2,被这篇教程坑成狗

    点赞 评论 复制链接分享
  • weixin_39789979 weixin_39789979 4月前

    第一节的数据库表名是 articles ,在本节数据库表名用的是article。

    点赞 评论 复制链接分享
  • weixin_39838231 weixin_39838231 4月前

    不是呀,这个是 Eloquent 默认的, Model 名词的复数,这一节的表名称也是 articles,莫非哪里有错?

    点赞 评论 复制链接分享
  • weixin_39789979 weixin_39789979 4月前

    是我搞错误了。我又花了1个小时不到,从第一节到本节重做了一遍。没有问题。

    点赞 评论 复制链接分享
  • weixin_39839018 weixin_39839018 4月前

    删除成功! Garbled ɾ���ɹ�! --!

    点赞 评论 复制链接分享

相关推荐