Laravel 5.4 入門系列 6. 文章的建立

心智極客發表於2019-02-16

基本功能

建立文章的第一步是使用者發請求,然後返回建立文章的頁面。

路由:處理使用者「建立文章」的請求

/routes/web.php
Route::get(`/posts/create`,`PostsController@create`);

控制器: 返回文章編輯檢視

/app/Http/Controllers/PostsController.php
public function create()
{
    return view(`posts.create`);
}

檢視: 使用 Bootstrap 元件來建立文章編輯頁面

/resources/views/posts/create.blade.php
@extends(`layouts.master`)

@section(`content`)
    <div class="col-sm-8 blog-main">
        <h1>建立文章</h1>
        <hr>
        <form action="{{ action(`PostsController@store`) }}" method="post">
            <div class="form-group">
                <label for="title">標題</label>
                <input type="text" id="title" name="title" class="form-control">
            </div>
            <div class="form-group">
                <label for="body">內容</label>
                <textarea class="form-control" id="body" name="body" rows="10"></textarea>
            </div>
            <button type="submit" class="btn btn-primary">提交</button>
        </form>
    </div>
@endsection

action 方法根據控制器來生成對應的路由,也可以用之前學過的 url 方法:

<form action="{{ url(`posts`) }}" method="post">

生成的 url 如下:

<form action="http://localhost:8000/posts" method="post">

使用者提交之後,需要在路由中處理使用者提交的資料的請求:

/routes/web.php
Route::post(`/posts`,`PostsController@store`);

最後是儲存文章實現,我們使用 request() 方法獲取請求欄位,儲存完之後跳轉到部落格首頁:

use AppPost;
public function store()
{
    $post = new Post();
    $post->title = request(`title`);
    $post->body = request(`body`);
    $post->save();
    
    return redirect(`posts`);
}

現在,訪問 posts/create,建立文章後點選提交,檢視下效果。實際上,會報錯:

TokenMismatchException in VerifyCsrfToken.php line 68

新增 CSRF 保護

雖然我們完成了基本功能,但是提交請求的時候還是會報錯,其實這是防止 CSRF 攻擊。

舉一個簡單的例子,你登入一個投票網站,通過傳送該請求向編號為 25 的人投票:

http://example.com/vote/25

CSRF 如何進行攻擊呢,顧名思義,CSRF 是 Cross-site request forgery 的縮寫,即跨站請求偽造,因此需要具備兩個條件:

  1. 跨站。首先,我登入了該投票網站,網站儲存了我的登入資訊,然後我又登入了另外一個網站;

  2. 偽造請求。在另外一個網站的介面中,可能包含了類似 <img src="http://example.com/vote/30" /> 這樣的 HTML 程式碼。由於投票網站無法區分你在哪裡傳送的請求,因此,就等於你向 30 號選手進行了投票;

解決方式也很簡單:

  1. 登入 A 網站的時候,生成一條 token

  2. 提交請求的時候,該 token 也跟著提交

  3. 兩者進行驗證即可

第一步,Laravel 已經幫我們實現了:

/vendor/laravel/framework/src/Illuminate/Session/Store.php
public function start()
{
    $this->loadSession();

    if (! $this->has(`_token`)) {
        $this->regenerateToken();
    }

    return $this->started = true;
}

第二步,Laravel 也幫我們封裝好了,直接使用 csrf_field() 函式即可,我們在文章編輯的表單中加入即可:

/resources/views/posts/create.blade.php
<h1>建立文章</h1>
    <hr>
    <form action="{{ url(`posts`) }}" method="post">
        {{ csrf_field() }}
        <div class="form-group">
            <label for="title">標題</label>
            <input type="text" id="title" name="title" class="form-control">
        </div>

可以看看該函式長什麼樣:

function csrf_field()
{
    return new HtmlString(`<input type="hidden" name="_token" value="`.csrf_token().`">`);
}

因此,我們也可以寫成:

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

最後一步,Laravel 通過中介軟體來進行自動檢驗:

public function handle($request, Closure $next)
{
    if (
        $this->isReading($request) || 
        $this->runningUnitTests() || 
        $this->inExceptArray($request) ||
        $this->tokensMatch($request)
    ) {
        return $this->addCookieToResponse($request, $next($request));
    }

    throw new TokenMismatchException;
}

簡單解讀下該中介軟體的處理流程:

  1. 判斷請求型別,如果是 GETHEADOPTIONS 等不會更改資源的請求就通過;

  2. 如果處於測試環境下就通過;

  3. $except 陣列內新增的 url 預設通過;

  4. tokens 匹配也通過;

通過之後,就會新增名為 XSRF-TOKEN 的cookie;如果沒通過,就丟擲異常,也就是我們上一節顯示的錯誤資訊了。

批量建立文章

剛才我們採用是 save() 方法來儲存文章,實際上,也可以使用 create() 方法,該方法允許一次性插入多條資料,因此必須指定允許批量插入的欄位:

/app/Post.php
class Post extends Model
{
    protected $fillable = [
        `title`,
        `body`,
    ];
}

store() 方法可以寫成:

/app/Http/Controllers/PostsController.php
public function store(Request $request)
{
    Post::create([
       `title` => request(`title`),
       `body`  => request(`body`)
   ]);

    return redirect("posts");
}

或者傳入陣列給 request():

/app/Http/Controllers/PostsController.php
public function store(Request $request)
{
    Post::create(request([`title`,`body`]));

    return redirect("posts");
}

新增欄位驗證

接下來進一步完善建立文章的功能,即欄位驗證。可以直接使用 validate 方法:

/app/Http/Controllers/PostsController.php
public function store(Request $request)
{
    $this->validate(request(), [
        `title` => `required|unique:posts|max:255`,
        `body` => `required|min:5`,
    ]);

    Post::create(request([`title`, `body`]));

    return redirect("posts");
}

我們為 title 新增了非空、唯一性以及最大字元的驗證規則,對 body 欄位新增了非空和最小字元的規則。

假如違反了規則,錯誤資訊 $errors 會自動被儲存在快閃記憶體的 Session 中,即只對下一次請求生效。並且,我們不需要將其返回給檢視,Laravel 幫我們做了處理,我們所有的檢視都可以獲取到 $errors 變數,可以令其顯示出來:

/resources/views/layouts/master.blade.php
@include(`layouts.errors`); 
@include(`layouts.footer`)

具體錯誤訊息:

/resources/views/layouts/errors.blade.php
@if (count($errors))
    <div class="form-group">
        <div class="alert alert-warning" role="alert">
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    </div>
@endif

相關文章