laravel 学习指南 第四章

  • TOC
    {:toc}

4 快速入门进阶版

4.1. 安装

4.1.1 使用composer

$ composer create-project laravel/laravel quickstart --prefer-dist

4.1.2 使用git克隆完整包

$ git clone https://github.com/laravel/quickstart-intermediate quickstart
$ cd quickstart
$ composer install
$ php artisan migrate

4.2 准备数据库

4.2.1 数据迁移

需要用到users表与tasks表。

users表的已经在database/migratiions目录下。

tasks表,需要使用命令创建一个:

$ php artisan make:migration create_tasks_table --create=tasks

在生成的文件中加入

1
2
$table->integer('user_id')->index();
$table->string('name');

->index()标识增加索引。user_id用于建立tasks表与user表之间的关联。

使用下方命令导入数据库

$ php artisan migrate

4.2.2 Eloquent模型

user模型

laravel中自带了user模块。

Task模型

使用下方命令创建task模型。

$ php artisan make:model Task

Task模型中,声明name属性支持批量赋值

批量赋值:将一个数组发送到模型类用于创建新模型实例是使用。

laravel有两种批量赋值属性,一种是$fillable可以通过批量赋值进行赋值。另一种是$guarded在批量赋值时会被过滤掉。

添加到app/Task.php

protected $fillable = ['name'];

4.2.3 Eloquent关联关系

tasks关联关系

user模型中定义tasks关联关系。这里使用一对多关系,laravel的ELoquent中提供了hasMany方法。

app/user.php文件中添加:

use App\Task;

public function tasks()
{
    return $this->hasMany(Task::class);
}

user关联关系

要定义与hasMany相对的关联关系,需要在子模型中定义一个关联方法去调用belongsTo方法:

添加到app/Task.php

use App\User;

public function user()
{
    return $this->belongsTo(User::class);
}

4.3 路由

routes.php中可以使用闭包定义所有的业务逻辑。实际上,大部分应用都会使用控制器来组织路由。

4.3.1 显示视图

使用view函数从路由中返回一个模板:

1
2
3
Route::get('/', function () {
return view('welcome');
});

4.3.2 用户认证

web中用户认证工作是非常乏味的,laravel提供一个auth组件让他变得轻松。

可以直接使用Artisan指令做完成auth的创建。

$ php artisan make:auth --views

随后只需要将auth添加到路由列表。

1
Route::auth();

4.3.3 任务控制器

接下来建立TaskController控制器,来处理任务。默认情况下控制器放置在app/Http/controllers目录下。使用下方命令创建:

$ php artisan make:controller TaskController

现在添加一些对应控制器的路由,在app/Http/routes.php中添加:

1
2
3
Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');

设置所有任务路由需要登录才能访问

我们希望用户必须登陆到系统才能创建新任务。所以需要限制访问用户,为登陆用户。

laravel使用中间件来处理这种限制。

1
2
3
4
public function __construct()
{
$this->middleware('auth');
}

4.4 创建布局与视图

布局方案与之前的版本相同。

4.4.1 定义布局

共用模板在resources/views/layouts/app.blade.php

@yield('content')为使用blade模板要插入的功能模块。

4.4.2 定义子视图

随后定义resources/views/tasks/index.blade.php。它会对应TaskController控制器的index方法。

随后在TaskController控制其中添加index方法的返回视图。

1
2
3
4
5
6

public function index(Request $request)
{
return view('tasks.index');
}

4.5 添加任务

4.5.1 验证表单输入

编写TaskController@store路由对应的处理方法,处理一个表单请求并创建一个新任务。

1
2
3
4
5
6
7
public function store(Request $request){
$this->validate($request, [
'name' => 'required|max:255',
]);

// Create The Task...
}

在控制器中,可以直接使用ValidatesRequests类中的validate方法。

而不用手动判断验证失败后的重定向,如果验证失败,用户会自动被重定向到来源页面,而错误信息也会存放到一次性Session中。

$errors

使用@include('common.errors')来渲染表单验证错误信息。

文件位置resources/views/common/errors.blade.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@if (count($errors) > 0)
<!-- Form Error List -->
<div class="alert alert-danger">
<strong>Whoops! Something went wrong!</strong>

<br><br>

<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

4.5.2 创建任务

验证完成后,要新建一个任务。当任务新建完成,页面会跳转到/tasks

laravel的关联提供了save方法,该方法接收一个关联模型实例,并且会在保存的数据库之前自动设置外键值到关联模型上。

这里save方法会自动将当前用户的,用户ID赋予给定的user_id属性。通过$request->user()获取当前用户实例:

1
2
3
4
5
$request->user()->tasks()->create([
'name' => $request->name,
]);

return redirect('/tasks');

4.6 显示己存在的任务

现在需要编辑TaskController@index传递所有已存在任务到视图。view函数接收一个数组作为第二个参数,数组中的每个值都会在视图中作为变数。

1
2
3
4
5
6
7
8
public function index(Request $request)
{
$tasks = Task::where('user_id', $request->user()->id)->get();

return view('tasks.index', [
'tasks' => $tasks,
]);
}

4.6.1 依赖注入

laravel的服务容器是整个框架中最重要的特性。

创建Repository

正如之前说的,我们要定义一个TaskRepository来处理所有对Task模式的数据访问,随着应用的增长,需要在应用中共享一些Eloquent查询时,就变得特别有用。

创建app/Repositories目录并在其中创建一个TaskRepository类。记住,laravel项目的app文件夹下的所有目录都是使用PSR-4自动加载标准,所以可以随意创建需要的目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\Repositories;

use App\User;
use App\Task;

class TaskRepository{
/**
* Get all of the tasks for a given user.
*
* @param User $user
* @return Collection
*/
public function forUser(User $user)
{
return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->get();
}
}

注入Repository

Repository创建好之后,通过在TaskController的构造函数以类型提示的方式注入该Repository,然后就可以在index方法中使用。

由于laravel使用容器来解析所有控制器,所以依赖会被自动注入到控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

namespace App\Http\Controllers;

use App\Task;use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Repositories\TaskRepository;

class TaskController extends Controller{
/**
* The task repository instance.
*
* @var TaskRepository
*/
protected $tasks;

/**
* Create a new controller instance.
*
* @param TaskRepository $tasks
* @return void
*/
public function __construct(TaskRepository $tasks)
{
$this->middleware('auth');
$this->tasks = $tasks;
}

/**
* Display a list of all of the user's task.
*
* @param Request $request
* @return Response
*/
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}

4.6.2 显示任务

tasks/index.blade.php中以表格形式显示所有任务。Blade中使用@foreach处理循环数据。

4.7 删除任务

4.7.1 添加删除按钮

当一个DELETE /task请求被发送到应用,它会触发TaskController@destroy方法:

在表单中同样要使用方法欺骗。

4.7.2 路由模型绑定

定义TaskControllerdestroy方法。但是首先,重新查看路由的定义以及控制器方法。

4.7.3 用户授权

现在要将Task实例注入到destroy方法。然而为了保证当前登录用户是给定任务的用。

例如:一些恶意请求可能尝试通过传统随机任务ID到/tasks/{task}链接删除另一个用户的任务。

所有需要使用laravel的授权功能来确保,当前登录用户拥有操作Task实例的权限。

创建Policy

laravel使用策略来将授权组织到单个类中,通常,每个策略都对应一个模型。

因此,使用Artisan命令创建一个TaskPolicy,生成的文件位于app/Policies/TaskPolicy.php:

$ php artisan make:policy TaskPolicy

随后将destroy方法添加到策略中,该方法会获取一个user实例和task实例,检查用户ID和任务的user_id是否相同。

实际上,所有的策略方法都会返回truefalse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Policies;

use App\User;
use App\Task;
use Illuminate\Auth\Access\HandlesAuthorization;

class TaskPolicy{
use HandlesAuthorization;

/**
* Determine if the given user can delete the given task.
*
* @param User $user
* @param Task $task
* @return bool
*/
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
}

最后,需要关联Task模型和TaskPolicy,通过在app/Providers/AuthServiceProvider.phppolicies属性添加注册实现。

注册后会告知Laravel无论何时尝试授权到Task实例时,该使用哪个策略类进行判断:

1
2
3
protected $policies = [
'App\Task' => 'App\Policies\TaskPolicy',
];

授权动作

编写好策略后,在destroy方法中使用它。所有的laravel控制器都可以调用authorize方法,该方法由AuthorizesRequest提供:

app/Controllers/TaskController.php文件。

1
2
3
4
public function destroy(Request $request, Task $task){
$this->authorize('destroy', $task);
// Delete The Task...
}

4.7.4 删除任务

最后在destroy方法中实际删除任务。使用Eloquent的delete方法从数据库中删除给定的模型实例。

删除后返回/tasks连接:

app/Controllers/TaskController.php文件中完整的TaskController@destroy方法。

1
2
3
4
5
public function destroy(Request $request, Task $task){
$this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}