基本任务列表
介绍
本快速入门指南提供了对 Laravel 框架的基本介绍,包括数据库迁移、Eloquent ORM、路由、验证、视图和 Blade 模板的内容。如果您是 Laravel 框架或 PHP 框架的新手,这是一个很好的起点。如果您已经使用过 Laravel 或其他 PHP 框架,您可能希望查阅我们更高级的快速入门指南。
为了体验 Laravel 的基本功能,我们将构建一个简单的任务列表,用于跟踪我们想要完成的所有任务(典型的“待办事项”示例)。该项目的完整源代码可在 GitHub 上找到。
安装
当然,首先您需要一个全新的 Laravel 框架安装。您可以使用 Homestead 虚拟机 或您选择的本地 PHP 环境来运行框架。一旦您的本地环境准备就绪,您可以使用 Composer 安装 Laravel 框架:
composer create-project laravel/laravel quickstart --prefer-dist
您可以选择仅阅读本快速入门的其余部分;但是,如果您想下载本快速入门的源代码并在本地计算机上运行它,您可以克隆其 Git 仓库并安装其依赖项:
git clone https://github.com/laravel/quickstart-basic quickstart
cd quickstart
composer install
php artisan migrate
有关构建本地 Laravel 开发环境的更完整文档,请查看完整的 Homestead 和 安装 文档。
准备数据库
数据库迁移
首先,让我们使用迁移来定义一个数据库表来保存我们所有的任务。Laravel 的数据库迁移提供了一种使用流畅、富有表现力的 PHP 代码定义数据库表结构和修改的简便方法。与其告诉您的团队成员手动向他们的本地数据库副本添加列,不如让您的队友简单地运行您推送到源代码控制的迁移。
因此,让我们构建一个数据库表来保存我们所有的任务。Artisan CLI 可用于生成各种类,并在构建 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(对象关系映射器)。Eloquent 使得使用清晰定义的“模型”在数据库中检索和存储数据变得轻而易举。通常,每个 Eloquent 模型直接对应一个数据库表。
因此,让我们定义一个与我们刚刚创建的 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 文档 以获取更多信息。
路由
路由的初步设置
接下来,我们准备向应用程序添加一些路由。路由用于将 URL 指向控制器或匿名函数,当用户访问给定页面时应执行这些控制器或函数。默认情况下,所有 Laravel 路由都在每个新项目中包含的 app/Http/routes.php
文件中定义。
对于此应用程序,我们知道我们至少需要三个路由:一个用于显示我们所有任务列表的路由,一个用于添加新任务的路由,以及一个用于删除现有任务的路由。因此,让我们在 app/Http/routes.php
文件中初步设置所有这些路由:
<?php
use App\Task;
use Illuminate\Http\Request;
/**
* 显示所有任务
*/
Route::get('/', function () {
//
});
/**
* 添加新任务
*/
Route::post('/task', function (Request $request) {
//
});
/**
* 删除现有任务
*/
Route::delete('/task/{id}', function ($id) {
//
});
显示视图
接下来,让我们填写我们的 /
路由。从此路由中,我们希望渲染一个包含添加新任务的表单以及所有当前任务列表的 HTML 模板。
在 Laravel 中,所有 HTML 模板都存储在 resources/views
目录中,我们可以使用 view
助手从路由返回这些模板之一:
Route::get('/', function () {
return view('tasks');
});
当然,我们需要实际定义此视图,所以让我们现在就这样做!
构建布局和视图
此应用程序只有一个视图,其中包含一个用于添加新任务的表单以及一个列出所有当前任务的列表。为了帮助您可视化视图,以下是应用程序完成后的截图,应用了基本的 Bootstrap CSS 样式:
定义布局
几乎所有的 Web 应用程序在页面之间共享相同的布局。例如,此应用程序有一个顶部导航栏,通常会出现在每个页面上(如果我们有多个页面)。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">
<!-- 导航栏内容 -->
</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">任务</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>
<!-- TODO: 当前任务 -->
@endsection
一些解释说明
在继续之前,让我们稍微谈谈这个模板。首先,@extends
指令通知 Blade 我们正在使用在 resources/views/layouts/app.blade.php
中定义的布局。所有在 @section('content')
和 @endsection
之间的内容将被注入到 app.blade.php
布局中的 @yield('content')
指令位置。
现在我们已经为应用程序定义了一个基本布局和视图。请记住,我们正在从我们的 /
路由返回此视图,如下所示:
Route::get('/', function () {
return view('tasks');
});
接下来,我们准备向我们的 POST /task
路由添加代码,以处理传入的表单输入并向数据库添加新任务。
@include('common.errors')
指令将加载位于 resources/views/common/errors.blade.php
的模板。我们尚未定义此模板,但我们很快就会这样做!
添加任务
验证
现在我们在视图中有了一个表单,我们需要向我们的 POST /task
路由添加代码,以验证传入的表单输入并创建一个新任务。首先,让我们验证输入。
对于此表单,我们将使 name
字段为必填项,并声明其必须包含少于 255
个字符。如果验证失败,我们将重定向用户回到 /
URL,并将旧输入和错误闪存到 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)
调用将给定验证器实例的错误闪存到会话中,以便可以通过视图中的 $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
Blade 构造允许我们编写简洁的循环,这些循环编译成快速的纯 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>任务</th>
<th> </th>
</thead>
<!-- 表体 -->
<tbody>
@foreach ($tasks as $task)
<tr>
<!-- 任务名称 -->
<td class="table-text">
<div>{{ $task->name }}</div>
</td>
<td>
<!-- TODO: 删除按钮 -->
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection
我们的任务应用程序几乎完成了。但是,当任务完成时,我们没有办法删除现有任务。让我们接下来添加这一点!
删除任务
添加删除按钮
我们在代码中留下了一个“TODO”注释,指出删除按钮应该在哪里。因此,让我们在 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">
删除任务
最后,让我们向我们的路由添加逻辑以实际删除给定任务。我们可以使用 Eloquent findOrFail
方法通过 ID 检索模型,或者如果模型不存在则抛出 404 异常。一旦我们检索到模型,我们将使用 delete
方法删除记录。记录删除后,我们将重定向用户回到 /
URL:
Route::delete('/task/{id}', function ($id) {
Task::findOrFail($id)->delete();
return redirect('/');
});