配置资源路由
将当前路由配置中的 Route::get('article', 'ArticleController@index');
改成 Route::resource('article', 'ArticleController');
。
其中Route::resource('article', 'ArticleController');
是RESTful 资源控制器它约定了一系列对某一种资源进行“增删改查”操作的路由配置,让我们不再需要对每一项需要管理的资源都写 N 行重复形式的路由。我们只要写下这条路由就可以得到下面7条路由配置
左边是 HTTP 方法,中间是 URL 路径,右边是 控制器中对应的函数,只要某个请求符合这七行中某一行的要求,那么这条请求就会触发最后一列的方法。
新增 Article
两个动作
- 获取“新增Article”的页面
- 提交数据到后端,插入一篇文章到数据库。
获取“新增Article”的页面
第二行路由可以满足我们的要求
在ArticleController 中写入:
public function create()
{
return view('admin/article/create');
}
新增视图文件 learnlaravel5/resources/views/admin/article/create.blade.php
:
@extends('layouts.app')
@section('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">
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>新增失败</strong> 输入不符合要求<br><br>
{!! implode('<br>', $errors->all()) !!}
</div>
@endif
<form action="{{ url('admin/article') }}" 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>
@endsection
提交数据到后端
视图文件中有一个表单:
<form action="{{ url('admin/article') }}" 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>
其中csrf_field
。这是 Laravel 中内置的防止应对 CSRF 攻击的防范措施,任何 POST PUT PATCH 请求都会被检测是否提交了 CSRF 字段。
{!! csrf_field() !!}
后端接收数据
新建store方法:
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
使用edit和updata两个方法
模板复制create的模板 主要修改的地方
<form action="{{ url('admin/article/'.$article->id) }}" method="POST">
{{ method_field('PUT') }}
{!! csrf_field() !!}
PHP文件修改:
public function update($id,Request $request)
{
$this->validate($request, [
'title' => 'required|unique:articles|max:255', // 必填、在 articles 表中唯一、最大长度 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('保存信息失败!');
}
//dd($request->all());
}
删除 Article
<form action="{{ url('admin/article/'.$article->id) }}" method="POST" style="display: inline;">
{{ method_field('DELETE') }}
{{ csrf_field() }}
<button type="submit" class="btn btn-danger">删除</button>
</form>
其中{{ method_field('DELETE') }}
的意思简单的来说就是
Laravel 的请求处理系统要求所有非 GET 和 POST 的请求全部通过 POST 请求来执行,再将真正的方法使用 _method 表单字段携带给后端代码。
在控制器中增加删除文章对应的是 destroy 方法:
public function destroy($id)
{
Article::find($id)->delete();
return redirect()->back()->withInput()->withErrors('删除成功!');
}
开始构建评论系统
建立 Model 类和数据表
创建名为 Comment 的 Model 类,并顺便创建附带的 migration,在 learnlaravel5 目录下运行命令:
php artisan make:model Comment -m
这样一次性建立了 Comment 类和 20160603220325createcommentstable 两个文件。修改 migration 文件的 up 方法为:
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->string('nickname');
$table->string('email')->nullable();
$table->string('website')->nullable();
$table->text('content')->nullable();
$table->integer('article_id');
$table->timestamps();
});
}
之后运行命令:
php artisan migrate
建立“一对多关系”
在 Article 模型中增加一对多关系的函数:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
public function hasManyComments()
{
return $this->hasMany('App\Comment', 'article_id', 'id');
}
}
构建前台 UI
修改前台的视图文件,想办法把评论功能加入进去。
创建前台的 ArticleController 类
运行命令:
php artisan make:controller ArticleController
增加路由:
Route::get('article/{id}', 'ArticleController@show');
此处的 {id} 指代任意字符串,在我们的规划中,此字段为文章 ID,为数字,
给 ArticleController 增加 show 函数:
public function show($id) { return view('article/show')->withArticle(Article::with('hasManyComments')->find($id));}
创建前台文章展示视图
新建 learnlaravel5/resources/views/article/show.blade.php
文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Learn Laravel 5</title>
<link href="//cdn.bootcss.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<script src="//cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<script src="//cdn.bootcss.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</head>
<div id="content" style="padding: 50px;">
<h4>
<a href="/"><< 返回首页</a>
</h4>
<h1 style="text-align: center; margin-top: 50px;">{{ $article->title }}</h1>
<hr>
<div id="date" style="text-align: right;">
{{ $article->updated_at }}
</div>
<div id="content" style="margin: 20px;">
<p>
{{ $article->body }}
</p>
</div>
<div id="comments" style="margin-top: 50px;">
@if (count($errors) > 0)
<div class="alert alert-danger">
<strong>操作失败</strong> 输入不符合要求<br><br>
{!! implode('<br>', $errors->all()) !!}
</div>
@endif
<div id="new">
<form action="{{ url('comment') }}" method="POST">
{!! csrf_field() !!}
<input type="hidden" name="article_id" value="{{ $article->id }}">
<div class="form-group">
<label>Nickname</label>
<input type="text" name="nickname" class="form-control" style="width: 300px;" required="required">
</div>
<div class="form-group">
<label>Email address</label>
<input type="email" name="email" class="form-control" style="width: 300px;">
</div>
<div class="form-group">
<label>Home page</label>
<input type="text" name="website" class="form-control" style="width: 300px;">
</div>
<div class="form-group">
<label>Content</label>
<textarea name="content" id="newFormContent" class="form-control" rows="10" required="required"></textarea>
</div>
<button type="submit" class="btn btn-lg btn-success col-lg-12">Submit</button>
</form>
</div>
<script>
function reply(a) {
var nickname = a.parentNode.parentNode.firstChild.nextSibling.getAttribute('data');
var textArea = document.getElementById('newFormContent');
textArea.innerHTML = '@'+nickname+' ';
}
</script>
<div class="conmments" style="margin-top: 100px;">
@foreach ($article->hasManyComments as $comment)
<div class="one" style="border-top: solid 20px #efefef; padding: 5px 20px;">
<div class="nickname" data="{{ $comment->nickname }}">
@if ($comment->website)
<a href="{{ $comment->website }}">
<h3>{{ $comment->nickname }}</h3>
</a>
@else
<h3>{{ $comment->nickname }}</h3>
@endif
<h6>{{ $comment->created_at }}</h6>
</div>
<div class="content">
<p style="padding: 20px;">
{{ $comment->content }}
</p>
</div>
<div class="reply" style="text-align: right; padding: 5px;">
<a href="#new" onclick="reply(this);">回复</a>
</div>
</div>
@endforeach
</div>
</div>
</div>
</body>
</html>
构建评论存储功能
我们需要创建一个 CommentsController 控制器,并增加一条“存储评论”的路由。运行命令:
php artisan make:controller CommentController
控制器创建成功,接下来我们增加一条路由:
Route::post('comment', 'CommentController@store');
给这个类增加 store 函数:
public function store(Request $request)
{
if (Comment::create($request->all())) {
return redirect()->back();
} else {
return redirect()->back()->withInput()->withErrors('评论发表失败!');
}
}
批量赋值
采用批量赋值方法来减少存储评论的代码
给 Comment 类增加 $fillable 成员变量:
protected $fillable = ['nickname', 'email', 'website', 'content', 'article_id'];
效果如下