在本章,我们通过搭建一个很简单的迷你博客把前面的知识再走一遍,
只有通过一次又一次的实战才能达到炉火纯青写起代码手上带风的效果:)
一、学习提醒
因为本实验篇幅较长,为防止发生大家做实验做到一半下次又要重新来的悲剧。
建议非会员用户可以使用实验楼提供的 Git 仓库来储存代码(点此查看使用方法),或者直接升级为会员即可直接保存环境。
二、设计与思路
在开始写第一行代码之前,一定要尽量从头到尾将我们要做的产品设计好,避免写完又改,多写不必要的代码。
需求分析:我们的迷你博客应该至少包含:新增/编辑/查看/删除文章,以及文章列表展示功能。
数据库分析:基于这个功能,我们只需要一张 Articles 数据表来存放文章即可。
页面结构分析:应该使用模板继承建立一张基础模板包含:导航栏/底部信息栏/作者信息等。
最后我们可以简单画个草图:
此处输入图片的描述
三、创建路由
完成这个博客大概需要以下几条路由:
路由 | 功能 |
---|---|
文章列表页面路由 | 返回文章列表页面 |
新增文章页面路由 | 返回新增文章页面 |
文章保存功能路由 | 将文章保存到数据库 |
查看文章页面路由 | 返回文章详情页面 |
编辑文章页面路由 | 返回编辑文章页面 |
编辑文章功能路由 | 将文章取出更新后重新保存到数据库 |
删除文章功能路由 | 将文章从数据库删除 |
可以看到几乎全部是对文章的数据操作路由,针对这种情况,Laravel 提供了非常方便的办法:RESTful 资源控制器和路由。
打开routes.php加入如下代码:
Route::resource('articles', 'ArticlesController');
尤其注意是resource ,不是一般的get!!!
只需要上面这样一行代码,就相当于创建了如下7条路由,且都是命名路由,我们可以使用类似route(‘articles.show’) 这样的用法。
Route::get('/articles', 'ArticlesController@index')->name('articles.index');
Route::get('/articles/{id}', 'ArticlesController@show')->name('articles.show');
Route::get('/articles/create', 'ArticlesController@create')->name('articles.create');
Route::post('/articles', 'ArticlesController@store')->name('articles.store');
Route::get('/articles/{id}/edit', 'ArticlesController@edit')->name('articles.edit');
Route::patch('/articles/{id}', 'ArticlesController@update')->name('articles.update');
Route::delete('/articles/{id}', 'ArticlesController@destroy')->name('articles.destroy');
app/Http/routes.php
里面的代码:
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::resource('articles', 'ArticlesController');
四、创建控制器
利用 artisan 创建一个文章控制器:
php artisan make:controller ArticlesController
五、创建基础视图
在 resources/views 目录下新建一个文件夹 layouts, 然后在 resources/views/layouts 目录下新建一个文件 app.blade.php。
打开 app.blade.php:
resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html>
<head>
<title>Myweb</title>
<style type="text/css">
body{margin: 0;padding: 0;background-color: #DEDEDE;}
a{text-decoration:none;}
.header{padding:10px 50px 10px 50px;border-bottom: 1px solid #eeeeee;}
.header>.logo{display:inline-block;}
.header>.menu{display:inline-block;margin-left:20px;}
.content{}
.left{background-color: white;margin: 25px 300px 25px 25px;padding: 25px;box-shadow: 1px 1px 2px 1px #848484;}
.right{background-color: white;width: 200px;margin: 25px;padding: 25px;box-shadow: 1px 1px 2px 1px #848484;position: absolute;top: 92px;right: 0;}
.footer{padding:10px 50px 10px 50px;background-color:gray;}
</style>
</head>
<body>
<!-- header -->
<div class="header">
<div class="logo">
<a href="#"><h2>Myweb</h2></a>
</div>
<div class="menu">
<a href="{{ route('articles.index') }}">Articles</a>
</div>
</div>
<div class="content">
<div class="left">
@yield('content')
</div>
<div class="right">
</div>
</div>
<!-- footer -->
<div class="footer">
<p>contact me : 1234567</p>
</div>
</body>
</html>
接下来的所有视图我们都将继承这个视图,我们可以让控制器先返回基础视图看一下效果。
打开 ArticlesController.php:
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class ArticlesController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('layouts.app');
}
然后访问 localhost/articles 就可以看到基础视图了。
六、新建文章表单
我们首先创建一个 文章列表展示页,作为我们的迷你博客首页。
在 resources/views 目录下新建一个文件夹 articles ,然后在 resources/views/articles 目录下新建一个文件 index.blade.php。
打开 index.blade.php:
resources/views/articles/index.blade.php
@extends('layouts.app')
@section('content')
<a href="{{ route('articles.create') }}" style="padding:5px;border:1px dashed gray;">
+ New Article
</a>
@endsection
然后打开 ArticlesController.php 将刚才的index函数修改为正确的视图:
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class ArticlesController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('articles.index');
}
然后访问 localhost/articles 页面可以看到我们的文章列表详情页。
当然了现在还没有任何文章,但是我们创建了一个“新建文章”的按钮,用来新建文章,点击后会跳转到新建文章表单页面,注意这里的跳转,使用了命名路由。
<a href="{{ route('articles.create') }}" style="padding:5px;border:1px dashed gray;">
+ New Article
</a>
如果这时你点击新建文章,你会发现跳到了空白页,因为现在其实是跳到了 ArticlesController 的 create() 方法里,而这个方法现在是空白的,所以就出现了空白页,我们打开ArticlesController.php 来补全这个方法。
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class ArticlesController extends Controller
{
.
.
.
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('articles.create');
}
相应的,在resources/views/articles 目录下创建一个视图create.blade.php。
resources/views/articles/create.blade.php
@extends('layouts.app')
@section('content')
<form action="{{ route('articles.store') }}" method="post">
{{ csrf_field() }}
<label>Title</label>
<input type="text" name="title" style="width:100%;" value="{{ old('title') }}">
<label>Content</label>
<textarea name="content" rows="10" style="width:100%;">{{ old('content') }}</textarea>
<button type="submit">OK</button>
</form>
@endsection
注意上面代码中的 {{ csrf_field() }}
,这是 Laravel 的 CSRF 保护机制,你应该为你的每个表单添加这样一行代码,以使你的网站不受到跨网站请求伪造 攻击,详情参考官方文档。
此时我们访问localhost/articles 点击新建文章就可以看到新建文章的表单了。
七、文章存储
此时如果你填写新建文章表单点击提交也会跳到一个空白页面,同样的道理,因为我们后续的控制器代码还没写。
要实现文章存储,首先要配置数据库,创建数据表,创建模型,然后再完成存储逻辑代码。
1.配置数据库
(保存环境的同学可跳过这一步)根据上次实验学过的知识快速完成配置:
sudo service mysql start
mysql -u root -p
create database myweb;
.env文件
DB_HOST=localhost
DB_DATABASE=myweb
DB_USERNAME=root
DB_PASSWORD=
2.创建数据表
利用 artisan 命令生成迁移:
php artisan make:migration create_articles_table
然后打开迁移文件:
database/migrations/XXXX_creat_articles_table.php
生成的迁移文件
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateArticlesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->longText('content')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('articles');
}
}
我们创建了一张 articles 表,包含递增的 id 字段,字符串title字段,长文本content字段,和时间戳。
执行数据库迁移:
php artisan migrate
3.创建模型
利用 artisan 命令创建模型:
php artisan make:model Article
打开模型文件,输入以下代码:
app/Article.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
protected $table = 'articles';
protected $fillable = [
'title', 'content',
];
}
指定了数据表的名称和可写字段。
4.存储逻辑代码
打开 ArticlesController.php 控制器,找到 store() 方法。
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Article;// <---------注意这里
class ArticlesController extends Controller
{
.
.
.
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required|max:50',
]);
$article = Article::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('articles.index');
}
因为这段代码中用到了Eloquent 模型的构造器用法,所以需要引入我们之前创建的模型。
use App\Article;
在收到表单传过来的数据时,可以先对表单数据进行验证:
$this->validate($request, [
'title' => 'required|max:50',
]);
最后完成文章的创建,然后重定向到文章列表页:
$article = Article::create([
'title' => $request->title,
'content' => $request->content,
]);
return redirect()->route('article.index');
至此,新增文章功能就完成了,打开浏览器,访问 localhost/articles 点击新建文章,完成表单,点击添加。
添加后如果跳到了文章列表页,就说明成功了!
但是此时,我们并不知道是不是真的添加成功了,我们可以去数据库里看一下是否有了刚刚插入的数据:
mysql -u root -p
use myweb;
select * from articles;
可以看到我们刚刚创建的一篇文章已经被成功的写入了数据库。
八、文章列表展示
完成了添加文章功能后,就可以实现我们的文章列表展示页了。
打开 ArticlesController.php 找到 index() 方法,添加代码如下:
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Article;
class ArticlesController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$articles = Article::orderBy('created_at','desc')->get();
return view('articles.index', compact('articles'));
}
取出了全部的文章并根据 created_at (创建时间)倒序排列,然后传给视图。
现在我们就可以在视图文件中读取全部的文章并显示了。
转到文章列表展示页:
resoureces/views/articles/index.blade.php
@extends('layouts.app')
@section('content')
<a href="{{ route('articles.create') }}" style="padding:5px;border:1px dashed gray;">
+ New Article
</a>
@foreach ($articles as $article)
<div style="border:1px solid gray;margin-top:20px;padding:20px;">
<h2>{{ $article->title }}</h2>
<p>{{ $article->content }}</p>
</div>
@endforeach
@endsection
添加了一段循环输出的代码。
现在访问localhost/articles,可以看到文章正确的显示了出来。
九、编辑文章表单
编辑文章表单其实和之前创建的新建文章表单很类似,只是需要额外将现有的数据读取出来填在表单上。
首先我们在文章列表页的每个文章上添加一个编辑按钮:
resoureces/views/articles/index.blade.php
@extends('layouts.app')
@section('content')
<a href="{{ route('articles.create') }}" style="padding:5px;border:1px dashed gray;">
+ New Article
</a>
@foreach ($articles as $article)
<div style="border:1px solid gray;margin-top:20px;padding:20px;">
<h2>{{ $article->title }}</h2>
<p>{{ $article->content }}</p>
<a href="{{ route('articles.edit', $article->id ) }}">Edit</a>
</div>
@endforeach
@endsection
然后打开 ArticlesController.php 添加相应的逻辑代码:
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Article;
class ArticlesController extends Controller
{
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$article = Article::findOrFail($id);
return view('articles.edit', compact('article'));
}
然后创建对应视图文件:
在 resources/views/articles 目录下新建一个文件 edit.blade.php 。
打开 edit.blade.php:
resources/views/articles/edit.blade.php
@extends('layouts.app')
@section('content')
<form action="{{ route('articles.update', $article->id) }}" method="post">
{{ csrf_field() }}
{{ method_field('PATCH') }}
<label>Title</label>
<input type="text" name="title" style="width:100%;" value="{{ $article->title }}">
<label>Content</label>
<textarea name="content" rows="10" style="width:100%;">{{ $article->content }}</textarea>
<button type="submit">OK</button>
</form>
@endsection
注意这段代码中的{{ method_field('PATCH') }}
,这是跨站方法伪造,HTML 表单没有支持 PUT、PATCH 或 DELETE 动作。所以在从 HTML 表单中调用被定义的 PUT、PATCH 或 DELETE 路由时,你将需要在表单中增加隐藏的 _method 字段来伪造该方法,详情参考 官方文档。
另一种更通俗点的解释!!!链接
大家可能注意到了这句代码 {{ 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 了。
这个表单将会把数据传到 update() 方法中,所以接下来我们补全 update() 方法。
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Article;
class ArticlesController extends Controller
{
.
.
.
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate($request, [
'title' => 'required|max:50',
]);
$article = Article::findOrFail($id);
$article->update([
'title' => $request->title,
'content' => $request->content,
]);
return back();
}
到此,编辑文章功能就完成了,点击编辑后将出现编辑表单,输入新的内容后点击 “OK” 就可以成功更新数据。
十、删除文章
首先我们在文章列表页的每个文章上添加一个删除按钮:
resoureces/views/articles/index.blade.php
@extends('layouts.app')
@section('content')
<a href="{{ route('articles.create') }}" style="padding:5px;border:1px dashed gray;">
+ New Article
</a>
@foreach ($articles as $article)
<div style="border:1px solid gray;margin-top:20px;padding:20px;">
<h2>{{ $article->title }}</h2>
<p>{{ $article->content }}</p>
<a href="{{ route('articles.edit', $article->id ) }}">Edit</a>
<form action="{{ route('articles.destroy', $article->id) }}" method="post" style="display: inline-block;">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" style="color: #F08080;background-color: transparent;border: none;">Delete</button>
</form>
</div>
@endforeach
@endsection
然后打开 ArticlesController.php 添加相应的逻辑代码:
app/Http/Controllers/ArticlesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Article;
class ArticlesController extends Controller
{
.
.
.
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$article = Article::findOrFail($id);
$article->delete();
return back();
}
删除功能这样就做好了,现在访问文章列表展示页 localhost/articles 点击删除按钮,可以看到文章被删除。
十一、作者信息
最后,让我们添加作者信息。
打开基础视图:
resources/views/layouts/app.blade.php
<!-- header -->
<div class="header">
<div class="logo">
<a href="#"><h2>Myweb</h2></a>
</div>
<div class="menu">
<a href="{{ route('articles.index') }}">Articles</a>
</div>
</div>
<div class="content">
<div class="left">
@yield('content')
</div>
<div class="right">
<!-- 这里 -->
<div style="padding:20px;border:1px solid black;">
<h3>Author</h3>
<p>name : SadCreeper</p>
<p>age : 22</p>
<p>Tel : 150-XXXX-XXXX</p>
</div>
<!-- 这里 -->
</div>
</div>
全部完成了!
这个我之前的照片都是已经编辑好的了,再贴一次!!!
十二、小结
中间还是出现了好多问题,多数是因为粗心造成的,练习了这个demo之后感觉整体运行都有了很大的了解,还是需要多动手啊!!!