基本任務清單
簡介
這個快速入門導引為 Laravel 框架提供了基本的介紹,內容概括了資料庫遷移、Eloquent ORM、路由、驗證、視圖,及 Blade 樣板。如果你是第一次使用 Laravel 框架或 PHP 框架,那麼這會是個很好的起始點。如果你已經在使用 Laravel 或者其它的框架,不妨參考我們進階的快速入門。
要在 Laravel 功能中為樣本做基本的選擇,我們會建構一個簡單的任務清單,可以使用它追蹤所有想完成的任務。換句話說,就是典型的「代辦事項清單」範例。此專案完整並完成的原始碼在 GitHub 上。
安裝
安裝 Laravel
當然,首先你需要一個全新安裝的 Laravel 框架。你可以選擇使用 Homestead 虛擬機器或是其他本機 PHP 環境來執行框架。只要你的本機環境準備好,就可以使用 Composer 安裝 Laravel 框架:
composer create-project laravel/laravel quickstart --prefer-dist
安裝此快速入門(選擇性)
你可以選擇只閱讀快速入門的剩餘部分;不過,如果你想下載這個快速入門的原始碼並在你的本機機器執行,那麼你需要 clone 它的 Git 資源庫並安裝依賴套件:
git clone https://github.com/laravel/quickstart-basic quickstart
cd quickstart
composer install
php artisan migrate
欲了解更多關於建構本機 Laravel 開發環境的完整文件,請查閱 Homestead 及安裝的整個文件。
準備資料庫
資料庫遷移
首先,讓我們使用遷移定義資料表來容納我們所有的任務。Laravel 的資料庫遷移提供了一個簡單的方式,使用流暢、一目了然的 PHP 程式碼來定義資料表的結構與修改。不必再告訴你的團隊成員手動增加欄位至他們本機的資料庫副本中,你的團隊成員可以簡單的執行你提交至版本控制的遷移。
所以,讓我們建構來一張我們容納所有任務的資料表。Artisan 指令列介面可以被用於產生各種類別,在建構 Laravel 專案時為你節省大量的手動輸入。在這個範例中,讓我們使用 make:migration
指令為 tasks
資料表產生新的資料庫遷移:
php artisan make:migration create_tasks_table --create=tasks
此遷移會被放置於你專案的 database/migrations
目錄中。你可能已經注意到,make:migration
指令已經增加了自動遞增的 ID 及時間戳記至遷移檔。讓我們編輯這個檔案並為任務的名稱增加額外的 string
欄位:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTasksTable extends Migration
{
/**
* 執行遷移。
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
}
/**
* 還原遷移。
*
* @return void
*/
public function down()
{
Schema::drop('tasks');
}
}
我們將會使用 migrate
Artisan 指令執行遷移。如果你使用 Homestead,你必須在你的虛擬機器執行這個指令,因為你的主機無法直接存取資料庫:
php artisan migrate
這個指令會建立我們所有的資料表。如果你使用你選擇的資料庫客戶端去檢查資料表,你應該看到新的 tasks
資料表,並包含了我們遷移中所定義的欄位。接著,我們已經準備好為我們的任務定義 Eloquent ORM 模型!
Eloquent 模型
Eloquent 是 Laravel 預設的 ORM(物件關聯對映)。Eloqunet 透過明確的定義「模型」,讓你無痛的在資料庫取得及儲存資料。一般情況下,每個 Eloqunet 模型會直接對應一張資料表。
所以,讓我們定義一個對應至 tasks
資料表的 Task
模型。同樣的,我們可以使用 Artisan 指令來產生此模型。在這個範例中,我們會使用 make:model
指令:
php artisan make:model Task
這個模型會放置在你應用程式的 app
目錄中。預設中,此模型類別會是空的。我們不必明確告知 Eloquent 模型要對應哪張資料表,因為它會假設資料表是模型名稱的複數型態。所以,在這個範例中,Task
模型會假設要對應至 tasks
資料表。我們的空模型看起來應該如下:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
//
}
在為我們的應用程式增加路由時,我們會學習到更多如何使用 Eloquent 模型的相關內容。當然,你可以自由參考完整的 Eloquent 文件來取得更多資訊。
路由
建置路由
接著,我們已經準備好開始增加一些路由至應用程式中。路由被用來將 URLs 指向控制器或是匿名函式,當使用者進入特定頁面時就會被執行。預設中,Laravel 所有的路由被定義在 app/Http/routes.php
檔案,每個新的專案都會包含此檔案。
對於本應用程式,我們知道我們最後會需要三個路由:一個用於顯示我們所有任務的清單、一個用於新增任務、一個用於刪除已有的任務。我們會將這些路由包在 web
中介層內,這樣它們會擁有 session 狀態及 CSRF 保護。所以,讓我們在 app/Http/routes.php
中建置這所有的路由:
<?php
use App\Task;
use Illuminate\Http\Request;
Route::group(['middleware' => 'web'], function () {
/**
* 顯示所有任務
*/
Route::get('/', function () {
//
});
/**
* 增加新的任務
*/
Route::post('/task', function (Request $request) {
//
});
/**
* 刪除任務
*/
Route::delete('/task/{task}', function (Task $task) {
//
});
});
顯示視圖
接著,讓我們來填寫 /
路由。在此路由中,我們想要渲染一個包含新增任務的表單,以及目前所有任務清單的 HTML 樣板。
在 Laravel 裡,所有的 HTML 樣板都儲存在 resources/views
目錄,且我們可以在路由中使用 view
輔助方法來回傳這些樣板的其中一個:
Route::get('/', function () {
return view('tasks');
});
傳遞 tasks
至 view
函式會建立一個視圖物件實例,它會對應至 resources/views/tasks.blade.php
模板。當然,我們必須確實的定義這些視圖,所以現在讓我們開始動手做!
建構佈局與視圖
這個應用程式只會有一張視圖,包含新增任務的表單,以及目前所有任務的清單。為了幫助你想像此視圖,下方是完成後應用程式的截圖,套用了基本的 Bootstrap CSS 樣式:
定義佈局
幾乎所有的網頁應用程式都會在不同頁面共用相同的佈局。舉個例子,應用程式通常在每個頁面(如果我們有一個以上)的頂部都擁有導航欄。Laravel 使用了 Blade 佈局讓不同頁面共享這些相同的功能。
如同我們前面討論的,Laravel 所有的視圖都被儲存在 resources/views
。所以,讓我們定義一個新的佈局視圖至 resources/views/layouts/app.blade.php
。.blade.php
副檔名會告知框架使用 Blade 模板引擎渲染此視圖。當然,你可以在 Laravel 使用純 PHP 的樣板。不過,Blade 提供了方便的簡寫來撰寫乾淨、簡潔的模板。
我們的 app.blade.php
視圖看起來應該如下:
// resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<title>Laravel 快速入門 - 基本</title>
<!-- CSS 及 JavaScript -->
</head>
<body>
<div class="container">
<nav class="navbar navbar-default">
<!-- Navbar 內容 -->
</nav>
</div>
@yield('content')
</body>
</html>
注意佈局中的 @yield('content')
部分。這是特別的 Blade 指令,讓子頁面可以在此處注入自己的內容來延伸佈局。接著,讓我們定義將會使用此佈局並提供主要內容的子視圖。
定義子視圖
接下來,我們需要定義包含建立任務的表單及列出已有任務表格的視圖。讓我們將此視圖定義在 resources/views/tasks.blade.php
。
我們會跳過一些 Bootstrap CSS 樣板,只專注在重要的事物上。切記,你可以在 GitHub 下載應用程式的完整原始碼:
// resources/views/tasks.blade.php
@extends('layouts.app')
@section('content')
<!-- Bootstrap 樣板... -->
<div class="panel-body">
<!-- 顯示驗證錯誤 -->
@include('common.errors')
<!-- 新任務的表單 -->
<form action="/task" method="POST" class="form-horizontal">
{{ csrf_field() }}
<!-- 任務名稱 -->
<div class="form-group">
<label for="task" class="col-sm-3 control-label">Task</label>
<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control">
</div>
</div>
<!-- 增加任務按鈕-->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-plus"></i> 增加任務
</button>
</div>
</div>
</form>
</div>
<!-- 代辦:目前任務 -->
@endsection
一些注意事項的說明
在繼續之前,讓我們談談有關模板的一些事項。首先 @extends
指令會告知 Blade,我們使用定義於 resources/views/layouts/app.blade.php
的佈局。所有在 @section('content')
及 @endsection
之間的內容會被注入到 app.blade.php
佈局中的 @yield('content')
位置裡。
@include('common.errors')
指令會載入位於 resources/views/common/errors.blade.php
的模板。我們尚未定義此模板,但是我們將會在稍後定義它!
現在我們已經為我們的應用程式定義了基本的佈局及視圖。請記得,我們在 /
路由中回傳了此視圖,像這樣:
Route::get('/', function () {
return view('tasks');
});
接著,我們已經準備好增加程式碼至我們的 POST /task
路由,以處理接收到的表單輸入並增加新的任務至資料庫中。
增加任務
驗證
現在我們視圖中已經有一個表單,我們需要增加程式碼至我們的 POST /task
路由來驗證接收到的表單輸入並建立新的任務。首先,讓我們驗證輸入。
對此表單來說,我們要讓 name
欄位為必填,且它必須少於 255
字元。如果驗證失敗,我們會將使用者重導回 /
URL,並將舊的輸入及錯誤訊息快閃至 session。快閃該輸入至 session 能讓我們保留使用者的輸入,即使有驗證錯誤:
Route::post('/task', function (Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
// 建立該任務...
});
$errors
變數
讓我們休息一下說說範例中 ->withErrors($validator)
的部分。->withErrors($validator)
的呼叫會透過給定的驗證器實例將錯誤訊息快閃至 session 中,所以我們可以在視圖中透過 $errors
變數存取它們。
記得我們在視圖中使用了 @include('common.errors')
指令來渲染表單的驗證錯誤訊息。common.errors
讓我們可以簡單的在我們所有的頁面顯示相同格式的驗證錯誤訊息。現在讓我們定義此視圖的內容:
// resources/views/common/errors.blade.php
@if (count($errors) > 0)
<!-- 表單錯誤清單 -->
<div class="alert alert-danger">
<strong>哎呀!出了些問題!</strong>
<br><br>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
注意:
errors
變數可用於每個 Laravel 的視圖中。如果沒有驗證錯誤訊息存在,那麼它就會是一個空的ViewErrorBag
實例。
建立任務
現在輸入已經被驗證處理完畢。讓我們繼續填寫我們的路由來實際的建立一筆新的任務。一旦新的任務被建立後,我們會將使用者重導回 /
URL。要建立該任務,我們可以在為新的 Eloquent 模型建立及設定屬性後使用 save
方法:
Route::post('/task', function (Request $request) {
$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return redirect('/')
->withInput()
->withErrors($validator);
}
$task = new Task;
$task->name = $request->name;
$task->save();
return redirect('/');
});
好極了!我們現在可以成功的建立任務。接著,讓我們繼續建構已有的任務清單,並增加至我們的視圖中。
顯示已有的任務
首先,我們需要編輯我們的 /
路由,以傳遞所有已有的任務至視圖。view
函式接收一個能在視圖中取用之資料的陣列作為第二個參數,陣列中的每個鍵都會在視圖中作為變數:
Route::get('/', function () {
$tasks = Task::orderBy('created_at', 'asc')->get();
return view('tasks', [
'tasks' => $tasks
]);
});
一旦資料被傳遞之後,我們在我們的 tasks.blade.php
視圖中將任務切分並將它們顯示至表格中。@foreach
指令結構讓我們可以撰寫簡潔的迴圈,並編譯成快速的純 PHP 程式碼:
@extends('layouts.app')
@section('content')
<!-- 建立任務表單... -->
<!-- 目前任務 -->
@if (count($tasks) > 0)
<div class="panel panel-default">
<div class="panel-heading">
目前任務
</div>
<div class="panel-body">
<table class="table table-striped task-table">
<!-- 表頭 -->
<thead>
<th>Task</th>
<th> </th>
</thead>
<!-- 表身 -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- 任務名稱 -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<td>
<!-- 代辦:刪除按鈕 -->
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection
我們任務應用程式大部分都完成了。但是,當我們完成已有的任務後,還沒有任何方式可以刪除它們。接著讓我們增加此功能!
刪除任務
增加刪除按鈕
我們在我們的程式碼中應該放刪除按鈕的地方留下了「待辦」的事項。所以,讓我們在 tasks.blade.php
視圖中列出任務的每一行增加一個刪除按鈕。我們會為列表中的每個任務建立一個只有單個按鈕的小表單。當按鈕被按下時,一個 DELETE /task
的請求將會被發送到應用程式:
<tr>
<!-- 任務名稱 -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<!-- 刪除按鈕 -->
<td>
<form action="/task/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button>刪除任務</button>
</form>
</td>
</tr>
方法欺騙的註記
注意,刪除按鈕的表單 method
被列為 POST
,即使我們回應的請求使用了 Route::delete
路由。HTML 表單只允許 GET
及 POST
HTTP 動詞,所以我們需要有個方式在表單假冒一個 DELETE
請求。
我們可以在表單中透過 method_field('DELETE')
函式輸出的結果假冒一個 DELETE
請求。此函式會產生一個隱藏的表單輸入,Laravel 會辨識並覆蓋掉實際使用的 HTTP 請求方法。產生的欄位看起來如下:
<input type="hidden" name="_method" value="DELETE">
刪除該任務
最後,讓我們增加實際刪除給定任務的邏輯。我們可以使用隱式模型綁定來自動取得對應 {task}
路由參數的 Task
模型。
在我們的路由回呼中,我們將使用 delete
方法來刪除該筆記錄。只要該記錄被刪除,我們會將使用者重導回 /
URL:
Route::delete('/task/{task}', function (Task $task) {
$task->delete();
return redirect('/');
});