Laravel后台系统实战(三):任务创建及管理

在实际应用中,订单类型的操作会很多,如订单操作中的订单创建、列表显示、删除、修改、分页等,本章以此为例,简要介绍其在Laravel中的应用。
此文借鉴了Laravel的quickshtart
本文项目的GitHub地址为:GitHub地址,可参考。

基本MVC实例

创建模型

使用Artisan创建Model:

1
php artisan make:model Task

任务与任务的创建者相关,因此,默认只显示当前用户对应的任务,故TaskModel为:

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
<?php

namespace App;

use App\User;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{

/**
* The attributes that are mass assignable.
*
* @var array
*/

protected $fillable = ['name'];

/**
* The attributes that should be cast to native types.
*
* @var array
*/

protected $casts = [
'user_id' => 'int',
];

/**
* Get the user that owns the task.
*/

public function user()
{

return $this->belongsTo(User::class);
}
}

创建控制器

使用Artisan创建Controller:

1
php artisan make:controller TaskController

将task相关的所有请求路由到TaskController中,因此,在routes.php中增加task的resource:

1
2
3
4
5
Route::group(['middleware' => ['web']], function () {
Route::resource('task', 'TaskController');
Route::resource('user', 'UserController');
Route::auth();
});

在Controller中,定义获取list、创建、删除的方法,根据Laravel Controller定义分别为indexstoredestroy,实现如下:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

use App\Task;

class TaskController extends Controller
{

/**
* 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' => Task::orderBy('created_at', 'asc')->get()
]);
}

/**
* Create a new task.
*
* @param Request $request
* @return Response
*/

public function store(Request $request)
{

$validator = Validator::make($request->all(), [
'name' => 'required|max:255',
]);
if ($validator->fails()) {
return redirect('/tasks')
->withInput()
->withErrors($validator);
}
$task = new Task;
$task->name = $request->name;
$task->save();
return redirect('/tasks');
}

/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/

public function destroy(Request $request, Task $task)
{

Task::findOrFail($id)->delete();

return redirect('/tasks');
}
}

创建视图

根据Controller中的定义,定义默认task的视图。在resources/views下创建tasks的文件夹,然后在其中创建index.blade.php的文件,其内容为:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@extends('layouts.app')

@section('content')
<div class="container">
<div class="col-sm-offset-2 col-sm-8">
<div class="panel panel-default">
<div class="panel-heading">
New Task
</div>

<div class="panel-body">
<!-- Display Validation Errors -->
@include('common.errors')

<!-- New Task Form -->
<form action="/task" method="POST" class="form-horizontal">
{{ csrf_field() }}

<!-- Task Name -->
<div class="form-group">
<label for="task-name" class="col-sm-3 control-label">Task</label>

<div class="col-sm-6">
<input type="text" name="name" id="task-name" class="form-control" value="{{ old('task') }}">
</div>
</div>

<!-- Add Task Button -->
<div class="form-group">
<div class="col-sm-offset-3 col-sm-6">
<button type="submit" class="btn btn-default">
<i class="fa fa-btn fa-plus"></i>Add Task
</button>
</div>
</div>
</form>
</div>
</div>

<!-- Current Tasks -->
@if (count($tasks) > 0)
<div class="panel panel-default">
<div class="panel-heading">
Current Tasks
</div>

<div class="panel-body">
<table class="table table-striped task-table">
<thead>
<th>Task</th>
<th>&nbsp;</th>
</thead>
<tbody>
@foreach ($tasks as $task)
<tr>
<td class="table-text"><div>{{ $task->name }}</div></td>

<!-- Task Delete Button -->
<td>
<form action="/task/{{ $task->id }}" method="POST">
{{ csrf_field() }}
{{ method_field('DELETE') }}

<button type="submit" id="delete-task-{{ $task->id }}" class="btn btn-danger">
<i class="fa fa-btn fa-trash"></i>Delete
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
</div>
</div>
@endsection

此处将创建人物、列表展示以及删除人物均整合到该页面中。
同时,在app.blade.php中删除冗余的侧边栏导航,增加Tasks的导航。
如此,点击侧边栏的Tasks,可看到页面:
Tasks基本页面
其中数据通过seed填充。

基本MVC进阶

在用户登录及管理章节中,我们使用了Repository。同时,对于删除操作,一般而言,只允许该任务的创建者才拥有删除权限,因此,此处对以上的流程稍作优化。

创建Repository

app/Repositories中创建TaskRepository.php,在其中使用用户权限管理,即为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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();
}
}

使用auth控制删除

首先在app/Policies中创建TaskPlolicy.php,限制destroy方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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;
}
}

然后在app/Providers/AuthServiceProvider.php中添加到$policies中:

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

如此,可在Controller中使用。

改造Controller

在以上Repository和Policy基础上,改造Controller,如下:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

use App\Task;
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()),
]);
}

/**
* Create a new task.
*
* @param Request $request
* @return Response
*/

public function store(Request $request)
{

$this->validate($request, [
'name' => 'required|max:255',
]);

$request->user()->tasks()->create([
'name' => $request->name,
]);

return redirect('/task');
}

/**
* Destroy the given task.
*
* @param Request $request
* @param Task $task
* @return Response
*/

public function destroy(Request $request, Task $task)
{

$this->authorize('destroy', $task);

$task->delete();

return redirect('/task');
}
}

其中通过构造方法注入Repository,并使用$this->authorize(‘destroy’, $task)判断删除操作时候被授权,若未被授权,则该操作终止。

分页

当任务太过,上百甚至上千时,需要使用分页显示。Laravel中自带了分页功能,可通过该功能快速地实现分页,以及下一页等的URL,同时提供了前端显示的基于Bootstrap的demo。
Laravel提供了paginate方法用于分页,可在query builder以及Eloquent query中使用。如以上实例中,需要对分页显示20条数据,则将TaskRepository中的方法改为:

1
2
3
4
5
6
public function forUser(User $user)
{

return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->paginate(20);
}

增加paginate方法。
增加之后,可看到返回的数据中增加了分页信息,如示例所示:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{
"total": 23273,
"per_page": 10,
"current_page": 1,
"last_page": 1164,
"next_page_url": "http://localhost/task?page=2",
"prev_page_url": null,
"from": 1,
"to": 20,
"data": [
{
"id": 3,
"user_id": 1,
"name": "damye test task",
"created_at": "2016-03-18 09:01:07",
"updated_at": "2016-03-18 09:01:07"
},

{
"id": 4,
"user_id": 1,
"name": "test controller",
"created_at": "2016-03-18 10:06:36",
"updated_at": "2016-03-18 10:06:36"
},

{
"id": 5,
"user_id": 1,
"name": "432342",
"created_at": "2016-03-18 10:09:53",
"updated_at": "2016-03-18 10:09:53"
},

{
"id": 6,
"user_id": 1,
"name": "PejSLfbKwV",
"created_at": "2016-03-29 08:04:30",
"updated_at": "2016-03-29 08:04:30"
},

{
"id": 7,
"user_id": 1,
"name": "mAfmaBCsUS",
"created_at": "2016-03-29 08:04:30",
"updated_at": "2016-03-29 08:04:30"
},

{
"id": 17,
"user_id": 1,
"name": "VbVV5J7VvH",
"created_at": "2016-03-29 08:04:31",
"updated_at": "2016-03-29 08:04:31"
},

{
"id": 18,
"user_id": 1,
"name": "XQwXK9BeGE",
"created_at": "2016-03-29 08:04:31",
"updated_at": "2016-03-29 08:04:31"
},

{
"id": 19,
"user_id": 1,
"name": "3JhnyOLMgQ",
"created_at": "2016-03-29 08:04:31",
"updated_at": "2016-03-29 08:04:31"
},

{
"id": 20,
"user_id": 1,
"name": "fGx83BzjU6",
"created_at": "2016-03-29 08:04:31",
"updated_at": "2016-03-29 08:04:31"
},

{
"id": 21,
"user_id": 1,
"name": "9Az4ctR7lb",
"created_at": "2016-03-29 08:04:31",
"updated_at": "2016-03-29 08:04:31"
}

]
}

如此,后台返回的数据已带上分页信息。Laravel同时提供了基于Bootstrap的前端代码,在前端加上:

1
{!! $tasks->links() !!}

即可获得页码信息。
示例如图:
Tasks基本页面

总结

本文的方法中,将任务的创建、列表展示、删除合为一个页面,实际处理的时候可根据需求拆分,使用方法类似。
另外,css和js可根据前端的需求调整。