使用 Laravel 构建内容管理框架(六)

完成Entrust的配置

安装zizaco/entrust Package


zizaco/entrust是Laravel下基于角色的权限管理方案。

1.修改文件composer.json

1
2
3
4
5
6
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.1.*",
"gregwar/captcha": "1.*",
"zizaco/entrust": "dev-laravel-5"
},

2.在终端运行命令

1
2
3
4
5
composer install
// or
composer update

3.在文件config/app.phpproviders数组和aliase数组分别添加

1
2
3
4
5
// providers
Zizaco\Entrust\EntrustServiceProvider::class
// aliase
'Entrust' => Zizaco\Entrust\EntrustFacade::class,

4.在文件app/Http/Kernel.php$routeMiddleware添加

1
2
3
'role' => Zizaco\Entrust\Middleware\EntrustRole::class,
'permission' => Zizaco\Entrust\Middleware\EntrustPermission::class,
'ability' => Zizaco\Entrust\Middleware\EntrustAbility::class,

5.Entrust会根据文件config/auth.phpmodel来选择用户模型,如下:

1
'model' => App\Models\User::class

执行以下命令生成配置文件config/entrust.php

1
php artisan vendor:publish

修改配置文件entrust.php如下:

1
2
'role'=>'App\Models\Role'
'permission'=>'App\Models\Permission'

6.生成数据库迁移文件并执行

1
2
php artisan entrust:migration
php artisan migrate

7.生成自动加载

1
composer dump-autoload

管理模型


为了使用Entrust,我们需要新建两个模型,在终端运行以下命令:

1
2
3
4
5
// 角色模型
php artisan make:model Models/Role
// 权限模型
php artisan make:model Models/Permission

Role.php

1
2
3
4
5
6
7
8
9
<?php
namespace App\Models;
use Zizaco\Entrust\EntrustRole;
class Role extends EntrustRole
{
protected $guarded = [];
}

Permission.php

1
2
3
4
5
6
7
8
9
<?php
namespace App\Models;
use Zizaco\Entrust\EntrustPermission;
class Permission extends EntrustPermission
{
protected $guarded = [];
}

为了配合Entrust的使用,需要修改User.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
<?php
namespace App\Models;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Zizaco\Entrust\Traits\EntrustUserTrait;
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword, EntrustUserTrait{
Authorizable::can insteadof EntrustUserTrait;
EntrustUserTrait::can as hasPermission;
}
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = ['password', 'remember_token'];
}

完成上述所有操作之后,Entrust的配置就完成了。

使用 Laravel 构建内容管理框架(五)

新增菜单管理模块功能,完成侧边栏菜单视图显示

新增迁移文件与数据填充


在终端执行以下命令,在文件夹database/migrations/新增迁移文件

1
php artisan make:migration --table=menus create_menus_table

在文件夹database/migrations/下打开迁移文件create_menus_table.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
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->smallInteger('parent_id')->default(0);
$table->string('ico', 50);
$table->string('url', 50);
$table->string('name', 50);
$table->string('description', 50);
$table->tinyInteger('sort')->default(0);
$table->tinyInteger('is_hide')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menus');
}
}

打开文件database/seeds/DatabaseSeeder.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
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Menu;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
$this->call(UserTableSeeder::class);
$this->call(MenuTableSeeder::class);
Model::reguard();
}
}
class UserTableSeeder extends Seeder
{
public function run()
{
DB::table('users')->delete();
User::create(['name' => 'Ann', 'email' => 'ann@qq.com', 'password' => bcrypt(123456)]);
User::create(['name' => 'Luis', 'email' => 'luis@qq.com', 'password' => bcrypt(123456)]);
User::create(['name' => 'admin', 'email' => 'admin@qq.com', 'password' => bcrypt(123456)]);
}
}
class MenuTableSeeder extends Seeder
{
public function run()
{
DB::table('menus')->delete();
Menu::create(["parent_id" => "0", "name" => "首页管理", "url" => "index.index", 'description' => '展示系统的各项基础数据']);
Menu::create(["parent_id" => "0", "name" => "菜单管理", "url" => "menu.index", 'description' => '管理菜单的新增、编辑、删除']);
Menu::create(["parent_id" => "2", "name" => "菜单列表", "url" => "menu.index", 'description' => '管理菜单的新增、编辑、删除']);
Menu::create(["parent_id" => "2", "name" => "新增菜单", "url" => "menu.create", 'description' => '新增菜单的页面']);
Menu::create(["parent_id" => "2", "name" => "编辑菜单", "url" => "menu.edit", 'description' => '编辑菜单的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "角色管理", "url" => "role.index", 'description' => '管理角色的新增、编辑、删除']);
Menu::create(["parent_id" => "6", "name" => "角色列表", "url" => "role.index", 'description' => '管理角色的新增、编辑、删除']);
Menu::create(["parent_id" => "6", "name" => "新增角色", "url" => "role.create", 'description' => '新增角色的页面']);
Menu::create(["parent_id" => "6", "name" => "编辑角色", "url" => "role.edit", 'description' => '编辑角色的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "6", "name" => "角色赋权", "url" => "role.show", 'description' => '编辑角色的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "权限管理", "url" => "permission.index", 'description' => '管理权限的新增、编辑、删除']);
Menu::create(["parent_id" => "11", "name" => "权限列表", "url" => "permission.index", 'description' => '管理权限的新增、编辑、删除']);
Menu::create(["parent_id" => "11", "name" => "新增权限", "url" => "permission.create", 'description' => '新增权限的页面']);
Menu::create(["parent_id" => "11", "name" => "编辑权限", "url" => "permission.edit", 'description' => '编辑权限的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "用户管理", "url" => "user.index", 'description' => '管理用户的新增、编辑、删除']);
Menu::create(["parent_id" => "15", "name" => "用户列表", "url" => "user.index", 'description' => '管理用户的新增、编辑、删除']);
Menu::create(["parent_id" => "15", "name" => "新增用户", "url" => "user.create", 'description' => '新增用户的页面']);
Menu::create(["parent_id" => "15", "name" => "编辑用户", "url" => "user.edit", 'description' => '编辑用户的页面', 'is_hide' => 1]);
}
}

在终端运行命名如下:

1
2
3
4
5
// 生成数据表
php artisan migrate
// 填充数据表
php artisan db:seed

进行上述操作之后,会在数据库生成menus数据表并往menus数据表里面填充数据

新增模型


在终端执行以下命令,在文件夹app/Models/新建文件Menu.phpTree.php

1
2
php artisan make:model Models/Menu
php artisan make:model Models/Tree

打开文件Menu.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
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
protected $guarded = [];
/**
* 获取侧边栏菜单
* @return string
*/
public static function getSidebar()
{
$tree = Tree::createNodeTree(Menu::all());
$sidebar = self::setSidebar($tree);
return $sidebar;
}
/**
* 设置侧边栏菜单
* @param $tree
*
* @return string
*/
public static function setSidebar($tree)
{
$html = "";
foreach ($tree as $menu) {
if ($menu->is_hide == 0) {
if ($menu->child) {
$html .= '<li class="treeview">'
. '<a><i class="fa fa-bars"></i> <span>' . $menu->name . '</span><i class="fa fa-angle-left pull-right"></i></a>'
. '<ul class="treeview-menu">'
. self::setSidebar($menu->child)
. '</ul>'
. '</li>';
} else {
$html .= '<li><a href="' . route($menu->url) . '"><i class="fa fa-bars"></i><span> ' . $menu->name . '</span></a></li>';
}
}
}
return $html;
}
}

打开文件Tree.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
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class Tree
* 树的创建与操作
*
* @package App\Models
*/
class Tree extends Model
{
/**
* 生成无序层级树
*
* @param $models
* @param int $parent_id
* @param int $level
* @param string $html
*
* @return array
*/
public static function createLevelTree($models, $parent_id = 0, $level = 0, $html = "-")
{
$tree = [];
foreach ($models as $model) {
if ($model->parent_id == $parent_id) {
if ($level != 0) {
$model->html = str_repeat("    ", $level);
$model->html .= '|';
}
$model->html .= str_repeat($html, $level);
$tree[] = $model;
$tree = array_merge($tree, self::createLevelTree($models, $model->id, $level + 1));
}
}
return $tree;
}
/**
* 生成无序节点树
*
* @param $models
* @param int $parent_id
* @param string $node
*
* @return array
*/
public static function createNodeTree($models, $parent_id = 0, $node = 'child')
{
$tree = [];
foreach ($models as $model) {
if ($model->parent_id == $parent_id) {
$model->$node = self::createNodeTree($models, $model->id);
$tree[] = $model;
}
}
return $tree;
}
}

新增验证请求


在终端执行以下命令,在文件夹app/Http/Requests/Form下新增表单验证MenuForm

1
php artisan make:request Form/MenuForm

修改文件MenuForm代码如下:

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\Requests\Form;
use App\Http\Requests\Request;
class MenuForm extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'url' => 'required',
'name' => 'required',
'parent_id' => 'required'
];
}
public function messages()
{
return [
'url.required' => '菜单地址不能为空',
'name.required' => '菜单名称不能为空',
'parent_id.required' => '父级分类不能为空'
];
}
}

新增控制器MenuController


在终端执行以下命令,在文件夹app/Http/Controllers/Backend新增文件MenuController.php

1
php artisan make:controller Backend/MenuController

修改文件MenuController.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php
namespace App\Http\Controllers\Backend;
use App\Models\Menu;
use App\Models\Tree;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\Form\MenuForm;
class MenuController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$menus = Menu::paginate(25);
return view('backend.menu.index', compact('menus'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$tree = Tree::createLevelTree(Menu::all());
return view('backend.menu.create', compact('tree'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function store(MenuForm $request)
{
try {
if (Menu::create($request->all())) {
return redirect()->back()->withSuccess("新增菜单成功");
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()))->withInput();
}
}
/**
* Display the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$menu = Menu::find($id);
$tree = Tree::createLevelTree(Menu::all());
return view('backend.menu.edit', compact('menu', 'tree'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function update(MenuForm $request, $id)
{
$data = $request->all();
unset($data['_method']);
unset($data['_token']);
try {
if (Menu::where('id', $id)->update($data)) {
return redirect()->back()->withSuccess('编辑菜单成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()))->withInput();
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$childMenus = Menu::where('parent_id', '=', $id)->get()->toArray();
if ( ! empty($childMenus)) {
return redirect()->back()->withErrors("请先删除其下级分类");
}
try {
if (Menu::destroy($id)) {
return redirect()->back()->withSuccess('删除菜单成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()));
}
}
}

新增路由


在文件app\Http\routes.php修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
Route::group(['namespace' => 'Backend', 'middleware' => ['auth']], function () {
Route::get('/', ['as' => 'index.index', 'uses' => 'IndexController@index']);
Route::resource('user', 'UserController');
Route::resource('menu', 'MenuController');
});
Route::group(['namespace' => 'Auth'], function () {
Route::get('auth/login', 'AuthController@getLogin');
Route::post('auth/login', 'AuthController@postLogin');
Route::get('auth/logout', 'AuthController@getLogout');
});

新增视图


在文件夹resources/views/backend/新建文件夹menu,并新建以下模板文件:

  • edit.blade.php
  • index.blade.php
  • create.blade.php

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
79
80
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-xs-1">
<div class="small-box">
<a href="{{URL::to('menu/create')}}" class="btn btn-success">新增菜单</a>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">菜单列表</h3>
<div class="box-tools pull-right">
<div class="input-group input-group-sm" style="width: 150px;">
<input type="text" name="table_search" class="form-control pull-right" placeholder="快速查询">
<div class="input-group-btn">
<button type="button" class="btn btn-default disabled">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>菜单编号</th>
<th>菜单名称</th>
<th>菜单地址</th>
<th>操作</th>
</tr>
@forelse($menus as $menu)
<tr>
<td>{{$menu->id}}</td>
<td>{{$menu->name}}</td>
<td>{{$menu->url}}</td>
<td>
<a class="btn btn-info" href="{{URL::to('menu/'.$menu->id.'/edit')}}">
编辑
</a>
<button class="btn btn-danger" data-toggle="modal" data-target="#defalutModal" data-url="{{URL::to('menu/'.$menu->id)}}">
删除
</button>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center">暂无数据</td>
</tr>
@endforelse
</table>
</div>
@if($menus->render() !== "")
<div class="box-footer">
{!! $menus->render() !!}
</div>
@endif
</div>
</div>
</div>
@include('backend.layout.model.default',['model_title'=>'操作提示','model_content'=>'你确定要删除这条菜单吗?'])
@stop
@section('script')
<script type="text/javascript">
$('#defalutModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var url = button.data('url');
var modal = $(this);
modal.find('form').attr('action', url);
})
</script>
@stop

create.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-primary">
<form class="form-horizontal" action="{{URL::to('menu')}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">{{$page_title or "Page Title"}}</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
</div>
<div class="box-body">
<div class="form-group">
<label class="col-sm-3 control-label">父级菜单</label>
<div class="col-sm-9">
<select class="form-control select2" name="parent_id">
<option value="0">/</option>
@foreach($tree as $menu)
<option value="{{$menu->id}}" @if(old('parent_id') == $menu->id) selected @endif >{{$menu->html}}{{$menu->name}}</option>
@endforeach
</select>
@include('backend.layout.message.tips',['field'=>'parent_id'])
</div>
</div>
<div class="form-group">
<label for="name" class="col-sm-3 control-label">菜单名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="菜单名称" value="{{old('name')}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="url" class="col-sm-3 control-label">菜单地址</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="url" name="url" placeholder="Controller.method" value="{{old('url')}}">
@include('backend.layout.message.tips',['field'=>'url'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop

edit.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-info">
<form class="form-horizontal" action="{{URL::to('menu/'.$menu->id)}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">编辑菜单</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
<input type="hidden" name="_method" value="put">
</div>
<div class="box-body">
<div class="form-group">
<label class="col-sm-3 control-label">父级菜单</label>
<div class="col-sm-9">
<select class="form-control select2" name="parent_id">
<option value="0">/</option>
@foreach($tree as $data)
<option value="{{$data->id}}" @if($menu->parent_id == $data->id) selected @endif >{{$data->html}}{{$data->name}}</option>
@endforeach
</select>
@include('backend.layout.message.tips',['field'=>'parent_id'])
</div>
</div>
<div class="form-group">
<label for="name" class="col-sm-3 control-label">菜单名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="菜单名称" value="{{$menu->name}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="url" class="col-sm-3 control-label">菜单地址</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="url" name="url" placeholder="Controller/method" value="{{$menu->url}}">
@include('backend.layout.message.tips',['field'=>'url'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop
@section('script')
<script type="text/javascript">
$(function(){
$(".select2").select2();
});
</script>
@stop

视图共享数据


打开文件resources/views/backend/layout/sidebar.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
<aside class="main-sidebar">
<section class="sidebar">
<div class="user-panel">
<div class="pull-left image">
<img src="{{ asset("/assets/img/user2-160x160.jpg") }}" class="img-circle" alt="User Image"/>
</div>
<div class="pull-left info">
<p>Alexander Pierce</p>
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<form action="#" method="get" class="sidebar-form">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search..."/>
<span class="input-group-btn">
<button type='submit' name='search' id='search-btn' class="btn btn-flat">
<i class="fa fa-search"></i>
</button>
</span>
</div>
</form>
<ul class="sidebar-menu">
@inject('menu','App\Models\Menu')
{!! $menu::getSidebar() !!}
</ul>
</section>
</aside>

完成上述操作后,便可以在每个视图都能看到共享的侧边栏菜单数据

使用 Laravel 构建内容管理框架(八)

新增权限管理模块,管理权限的增删查改。

新增请求


1
php artisan make:request Form/PermissionForm

文件PermissionForm代码如下:

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\Requests\Form;
use App\Http\Requests\Request;
class PermissionForm extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'display_name' => 'required',
'description' => 'required'
];
}
public function messages()
{
return [
'name.required' => '权限标识不能为空',
'display_name.required' => '权限名称不能为空',
'description.required' => '权限描述不能为空'
];
}
}

新增控制器


1
php artisan make:controller Backend/PermissionController

文件PermissionController.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
namespace App\Http\Controllers\Backend;
use App\Models\Permission;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Requests\Form\PermissionForm;
class PermissionController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$permissions = Permission::paginate(25);
return view('backend.permission.index', compact('permissions'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('backend.permission.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function store(PermissionForm $request)
{
try {
if (Permission::create($request->all())) {
return redirect()->route('permission.index')->withSuccess('新增权限成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()])->withInput();
}
}
/**
* Display the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$permission = Permission::find($id);
return view('backend.permission.edit', compact('permission'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function update(PermissionForm $request, $id)
{
$data = $request->all();
unset($data['_token']);
unset($data['_method']);
try {
if (Permission::where('id', '=', $id)->update($data)) {
return redirect()->back()->withSuccess("新增权限成功");
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()])->withInput();
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
try {
if (Permission::destroy($id)) {
return redirect()->back()->withSuccess("删除菜单成功");
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()])->withInput();
}
}
}

新增视图


在文件夹resources/views/permission/下新增文件

  • index.blade.php
  • create.blade.php
  • edit.blade.php

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
79
80
81
82
83
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-xs-1">
<div class="small-box">
<a href="{{URL::to('permission/create')}}" class="btn btn-success">新增权限</a>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">权限列表</h3>
<div class="box-tools pull-right">
<div class="input-group input-group-sm" style="width: 150px;">
<input type="text" name="table_search" class="form-control pull-right" placeholder="快速查询">
<div class="input-group-btn">
<button type="button" class="btn btn-default disabled">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>权限编号</th>
<th>权限标识</th>
<th>权限名称</th>
<th>权限描述</th>
<th>管理操作</th>
</tr>
@forelse($permissions as $permission)
<tr>
<td>{{$permission->id}}</td>
<td>{{$permission->name}}</td>
<td>{{$permission->display_name}}</td>
<td>{{$permission->description}}</td>
<td>
<a class="btn btn-info" href="{{URL::to('permission/'.$permission->id.'/edit')}}">
编辑
</a>
<a class="btn btn-danger" data-toggle="modal" data-target="#defalutModal" data-url="{{URL::to('permission/'.$permission->id)}}">
删除
</a>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center">暂无数据</td>
</tr>
@endforelse
</table>
</div>
@if($permissions->render() !== "")
<div class="box-footer">
{!! $permissions->render() !!}
</div>
@endif
</div>
</div>
</div>
@include('backend.layout.model.default',['model_title'=>'操作提示','model_content'=>'你确定要删除这条权限吗?'])
@stop
@section('script')
<script type="text/javascript">
$('#defalutModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var url = button.data('url');
var modal = $(this);
modal.find('form').attr('action', url);
})
</script>
@stop

edit.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-info">
<form class="form-horizontal" action="{{URL::to('permission/'.$permission->id)}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">编辑菜单</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
<input type="hidden" name="_method" value="put">
</div>
<div class="box-body">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">权限名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="权限名称" value="{{$permission->name}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="display_name" class="col-sm-3 control-label">权限标识</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="display_name" name="display_name" placeholder="权限标识" value="{{$permission->display_name}}">
@include('backend.layout.message.tips',['field'=>'display_name'])
</div>
</div>
<div class="form-group">
<label for="description" class="col-sm-3 control-label">菜单地址</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="description" name="description" placeholder="权限描述" value="{{$permission->description}}">
@include('backend.layout.message.tips',['field'=>'description'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop

create.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-info">
<form class="form-horizontal" action="{{URL::to('permission')}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">{{$page_title or "Page Title"}}</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
</div>
<div class="box-body">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">权限标识</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="权限标识" value="{{old('name')}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="display_name" class="col-sm-3 control-label">权限名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="display_name" name="display_name" placeholder="权限名称" value="{{old('display_name')}}">
@include('backend.layout.message.tips',['field'=>'display_name'])
</div>
</div>
<div class="form-group">
<label for="description" class="col-sm-3 control-label">权限描述</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="description" name="description" placeholder="权限名称" value="{{old('description')}}">
@include('backend.layout.message.tips',['field'=>'description'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop

填充数据


打开文件database/seeds/DatabaseSeeder.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Menu;
use App\Models\Role;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
$this->call(UserTableSeeder::class);
$this->call(MenuTableSeeder::class);
$this->call(RoleTableSeeder::class);
$this->call(PermissionTableSeeder::class);
Model::reguard();
}
}
class UserTableSeeder extends Seeder
{
public function run()
{
DB::table('users')->delete();
User::create(['name' => 'Ann', 'email' => 'ann@qq.com', 'password' => bcrypt(123456)]);
User::create(['name' => 'Luis', 'email' => 'luis@qq.com', 'password' => bcrypt(123456)]);
User::create(['name' => 'admin', 'email' => 'admin@qq.com', 'password' => bcrypt(123456)]);
}
}
class MenuTableSeeder extends Seeder
{
public function run()
{
DB::table('menus')->delete();
Menu::create(["parent_id" => "0", "name" => "首页管理", "url" => "index.index", 'description' => '展示系统的各项基础数据']);
Menu::create(["parent_id" => "0", "name" => "菜单管理", "url" => "menu.index", 'description' => '管理菜单的新增、编辑、删除']);
Menu::create(["parent_id" => "2", "name" => "菜单列表", "url" => "menu.index", 'description' => '管理菜单的新增、编辑、删除']);
Menu::create(["parent_id" => "2", "name" => "新增菜单", "url" => "menu.create", 'description' => '新增菜单的页面']);
Menu::create(["parent_id" => "2", "name" => "编辑菜单", "url" => "menu.edit", 'description' => '编辑菜单的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "角色管理", "url" => "role.index", 'description' => '管理角色的新增、编辑、删除']);
Menu::create(["parent_id" => "6", "name" => "角色列表", "url" => "role.index", 'description' => '管理角色的新增、编辑、删除']);
Menu::create(["parent_id" => "6", "name" => "新增角色", "url" => "role.create", 'description' => '新增角色的页面']);
Menu::create(["parent_id" => "6", "name" => "编辑角色", "url" => "role.edit", 'description' => '编辑角色的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "6", "name" => "角色赋权", "url" => "role.show", 'description' => '编辑角色的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "权限管理", "url" => "permission.index", 'description' => '管理权限的新增、编辑、删除']);
Menu::create(["parent_id" => "11", "name" => "权限列表", "url" => "permission.index", 'description' => '管理权限的新增、编辑、删除']);
Menu::create(["parent_id" => "11", "name" => "新增权限", "url" => "permission.create", 'description' => '新增权限的页面']);
Menu::create(["parent_id" => "11", "name" => "编辑权限", "url" => "permission.edit", 'description' => '编辑权限的页面', 'is_hide' => 1]);
Menu::create(["parent_id" => "0", "name" => "用户管理", "url" => "user.index", 'description' => '管理用户的新增、编辑、删除']);
Menu::create(["parent_id" => "15", "name" => "用户列表", "url" => "user.index", 'description' => '管理用户的新增、编辑、删除']);
Menu::create(["parent_id" => "15", "name" => "新增用户", "url" => "user.create", 'description' => '新增用户的页面']);
Menu::create(["parent_id" => "15", "name" => "编辑用户", "url" => "user.edit", 'description' => '编辑用户的页面', 'is_hide' => 1]);
}
}
class RoleTableSeeder extends Seeder{
public function run()
{
DB::table('roles')->delete();
Role::create(['name' => 'admin', 'display_name' => 'User Administrator', 'description' => 'User is allowed to manage and edit other users']);
Role::create(['name' => 'owner', 'display_name' => 'Project Owner', 'description' => 'User is the owner of a given project']);
}
}
class PermissionTableSeeder extends Seeder
{
public function run()
{
DB::table('permissions')->delete();
Permission::create(["display_name" => "首页管理", "name" => "index.index", 'description' => '展示系统的各项基础数据']);
Permission::create(["display_name" => "菜单列表", "name" => "menu.index", 'description' => '管理菜单的新增、编辑、删除']);
Permission::create(["display_name" => "新增菜单", "name" => "menu.create", 'description' => '新增菜单的页面']);
Permission::create(["display_name" => "编辑菜单", "name" => "menu.edit", 'description' => '编辑菜单的页面']);
Permission::create(["display_name" => "角色列表", "name" => "role.index", 'description' => '管理角色的新增、编辑、删除']);
Permission::create(["display_name" => "新增角色", "name" => "role.create", 'description' => '新增角色的页面']);
Permission::create(["display_name" => "编辑角色", "name" => "role.edit", 'description' => '编辑角色的页面']);
Permission::create(["display_name" => "角色赋权", "name" => "role.show", 'description' => '编辑角色的页面']);
Permission::create(["display_name" => "权限列表", "name" => "permission.index", 'description' => '管理权限的新增、编辑、删除']);
Permission::create(["display_name" => "新增权限", "name" => "permission.create", 'description' => '新增权限的页面']);
Permission::create(["display_name" => "编辑权限", "name" => "permission.edit", 'description' => '编辑权限的页面']);
Permission::create(["display_name" => "用户列表", "name" => "user.index", 'description' => '管理用户的新增、编辑、删除']);
Permission::create(["display_name" => "新增用户", "name" => "user.create", 'description' => '新增用户的页面']);
Permission::create(["display_name" => "编辑用户", "name" => "user.edit", 'description' => '编辑用户的页面']);
}
}

接着在终端执行以下命令回滚并再次执行迁移,填充数据

1
php artisan migrate:refresh --seed

使用 Laravel 构建内容管理框架(四)

新增用户管理模块功能

新增验证请求


在终端执行以下命令,在文件夹app/Http/Requests/Form下新增表单验证UserForm

1
php artisan make:request Form/UserForm

修改文件UserForm代码如下:

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
<?php
namespace App\Http\Requests\Form;
use App\Http\Requests\Request;
class UserForm extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required',
'email' => 'required',
'password' => 'required|confirmed',
'password_confirmation' => 'required'
];
}
public function messages()
{
return [
'name.required' => '用户名称不能为空',
'email.required' => '用户邮箱不能为空',
'password.required' => '用户密码不能为空',
'password.confirmed' => '确认密码不一致',
'password_confirmation.required' => '确认密码不能为空'
];
}
}

新增控制器UserController


在终端执行以下命令,在文件夹app/Http/Controllers/Backend新增文件UserController.php

1
php artisan make:controller Backend/UserController

修改文件UserController.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<?php
namespace App\Http\Controllers\Backend;
use App\Models\User;
use App\Http\Requests\Form\UserForm;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$users = User::paginate(25);
return view('backend.user.index', compact('users'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('backend.user.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Response
*/
public function store(UserForm $request)
{
$data = [
'name' => $request['name'],
'email' => $request['email'],
'password' => bcrypt($request['password']),
];
try {
if (User::create($data)) {
return redirect()->back()->withSuccess('新增用户成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()))->withInput();
}
}
/**
* Display the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$user = User::find($id);
return view('backend.user.edit', compact('user'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function update(UserForm $request, $id)
{
$data = [
'name' => $request['name'],
'email' => $request['email'],
'password' => bcrypt($request['password']),
];
try {
if (User::where('id', $id)->update($data)) {
return redirect()->back()->withSuccess('编辑用户成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()))->withInput();
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
try {
if (User::destroy($id)) {
return redirect()->back()->withSuccess('删除用户成功');
}
} catch (\Exception $e) {
return redirect()->back()->withErrors(array('error' => $e->getMessage()));
}
}
}

新增路由


在文件app\Http\routes.php新增代码如下:

1
2
3
4
Route::group(['namespace' => 'Backend', 'middleware' => ['auth']], function () {
Route::get('/', 'IndexController@index');
Route::resource('user', 'UserController');
});

新增视图


在文件夹resources/views/backend/新建文件夹user,并新建以下模板文件:

  • index.blade.php
  • create.blade.php
  • edit.blade.php

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
79
80
81
82
83
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-1">
<div class="small-box">
<a href="{{URL::to('user/create')}}" class="btn btn-success">新增用户</a>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">用户列表</h3>
<div class="box-tools pull-right">
<div class="input-group input-group-sm" style="width: 150px;">
<input type="text" name="table_search" class="form-control pull-right" placeholder="快速查询">
<div class="input-group-btn">
<button type="button" class="btn btn-default disabled">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</div>
</div>
<div class="box-body table-responsive no-padding">
<table class="table table-hover">
<tr>
<th>用户编号</th>
<th>用户名称</th>
<th>用户邮箱</th>
<th>所属角色</th>
<th>管理操作</th>
</tr>
@forelse($users as $user)
<tr>
<td>{{$user->id}}</td>
<td>{{$user->name}}</td>
<td>{{$user->email}}</td>
<td></td>
<td>
<a class="btn btn-info" href="{{URL::to('user/'.$user->id.'/edit')}}">
编辑
</a>
<button class="btn btn-danger" data-toggle="modal" data-target="#defalutModal" data-url="{{URL::to('user/'.$user->id)}}">
删除
</button>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center">暂无数据</td>
</tr>
@endforelse
</table>
</div>
@if($users->render() !== "")
<div class="box-footer">
{!! $users->render() !!}
</div>
@endif
</div>
</div>
</div>
@include('backend.layout.model.default',['model_title'=>'操作提示','model_content'=>'你确定要删除这名用户吗?'])
@stop
@section('script')
<script type="text/javascript">
$('#defalutModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
var url = button.data('url');
var modal = $(this);
modal.find('form').attr('action', url);
})
</script>
@stop

create.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-info">
<form class="form-horizontal" action="{{URL::to('user')}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">{{$page_title or "page_title"}}</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
</div>
<div class="box-body">
<div class="form-group">
<label class="col-sm-3 control-label">用户角色</label>
<div class="col-sm-9">
<select class="form-control select2" name="role_id">
<option value="0">/</option>
{{--@foreach($roles as $role)--}}
{{--<option value="{{$role->id}}">{{$role->display_name}}</option>--}}
{{--@endforeach--}}
</select>
@include('backend.layout.message.tips',['field'=>'role_id'])
</div>
</div>
<div class="form-group">
<label for="name" class="col-sm-3 control-label">用户名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="用户名称" value="{{old('name')}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-3 control-label">用户邮箱</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="email" name="email" placeholder="用户邮箱" value="{{old('email')}}">
@include('backend.layout.message.tips',['field'=>'email'])
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-3 control-label">用户密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="password" name="password" placeholder="用户密码" value="{{old('password')}}">
@include('backend.layout.message.tips',['field'=>'password'])
</div>
</div>
<div class="form-group">
<label for="password_confirmation" class="col-sm-3 control-label">确认密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="确认密码" value="{{old('password_confirmation')}}">
@include('backend.layout.message.tips',['field'=>'password_confirmation'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop

edit.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
@extends('backend.layout.main')
@section('content')
<div class="row">
<div class="col-md-6">
<div class="box box-info">
<form class="form-horizontal" action="{{URL::to('user/'.$user->id)}}" method="post" enctype="multipart/form-data">
<div class="box-header with-border">
<h3 class="box-title">{{$page_title or "Page_title"}}</h3>
<input type="hidden" name="_token" value="{{csrf_token()}}">
<input type="hidden" name="_method" value="put">
</div>
<div class="box-body">
<div class="form-group">
<label for="name" class="col-sm-3 control-label">用户名称</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="name" name="name" placeholder="用户名称" value="{{$user->name}}">
@include('backend.layout.message.tips',['field'=>'name'])
</div>
</div>
<div class="form-group">
<label for="email" class="col-sm-3 control-label">用户邮箱</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="email" name="email" placeholder="用户邮箱" value="{{$user->email}}">
@include('backend.layout.message.tips',['field'=>'email'])
</div>
</div>
<div class="form-group">
<label for="password" class="col-sm-3 control-label">用户密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="password" name="password" placeholder="用户密码">
@include('backend.layout.message.tips',['field'=>'password'])
</div>
</div>
<div class="form-group">
<label for="password_confirmation" class="col-sm-3 control-label">确认密码</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="password_confirmation" name="password_confirmation" placeholder="确认密码" value="{{$user->password_confirmation}}">
@include('backend.layout.message.tips',['field'=>'password_confirmation'])
</div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default" onclick="javascript:history.back(-1);return false;">
返回
</button>
<button type="submit" class="btn btn-danger pull-right">确 定</button>
</div>
</form>
</div>
</div>
</div>
@stop
@section('script')
<script type="text/javascript">
$(function () {
$(".select2").select2();
});
</script>
@stop

新增视图组件


在文件夹resources/views/layout/新建文件夹model,并新建模板文件default.blade.php,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="modal fade" id="defalutModal" tabindex="-1" role="dialog" aria-labelledby="defaultModalLabel">
<form class="form-horizontal" method="post" enctype="multipart/form-data">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="defaultModalLabel">{{$model_title}}</h4>
</div>
<div class="modal-body">
{{$model_content}}
<input type="hidden" name="_token" value="{{csrf_token()}}">
<input type="hidden" name="_method" value="delete">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="submit" class="btn btn-primary">确认</button>
</div>
</div>
</div>
</form>
</div>

使用 Laravel 构建内容管理框架(三)

完成登录管理页面与登录验证功能

管理用户模型


在文件夹app下新建文件夹Models,将文件夹app下的User.php移动到文件夹app/Models
修改文件User.php的命名空间

1
namespace App\Models;

打开文件config/auth.php,修改代码如下

1
'model' => App\Models\User::class,

新增路由


打开文件app/Http/routes.php,文件代码如下:

1
2
3
4
5
6
7
8
9
10
<?php
Route::group(['namespace' => 'Backend', 'middleware' => ['auth']], function () {
Route::get('/', 'IndexController@index');
});
Route::group(['namespace' => 'Auth'], function () {
Route::get('auth/login', 'AuthController@getLogin');
Route::post('auth/login', 'AuthController@postLogin');
Route::get('auth/logout', 'AuthController@getLogout');
});

新增登录视图


在文件夹resources/views/新建文件夹auth,然后新建视图文件login.blade.php
在文件夹resources/views/backend/layout新建文件夹message,然后新建视图文件如下:

  • tips.blade.php
  • error.blade.php
  • success.blade.php

login.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
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Log in</title>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link href="{{ asset("/assets/css/app.css") }}" rel="stylesheet" type="text/css"/>
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<b>TianMao</b>CMF
</div>
<div class="login-box-body">
<p class="login-box-msg">Happy Coding</p>
<form action="{{URL::to('/auth/login')}}" method="post" enctype="multipart/form-data">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="row form-group has-feedback">
<input type="text" class="form-control" placeholder="账号" name="email">
<span class="fa fa-user form-control-feedback"></span>
@include('backend.layout.message.tips',['field'=>'email'])
</div>
<div class="row form-group has-feedback">
<input type="password" class="form-control" placeholder="密码" name="password">
<span class="fa fa-lock form-control-feedback"></span>
@include('backend.layout.message.tips',['field'=>'password'])
</div>
<div class="row form-group has-feedback">
<input type="text" class="form-control" placeholder="验证码" name="captcha">
<span class="fa fa-image form-control-feedback"></span>
@include('backend.layout.message.tips',['field'=>'captcha'])
</div>
<div class="row form-group has-feedback">
<img src="{{$captcha}}" alt="图片验证码" style="width: 100%;">
</div>
<div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox" name="remember" value="1"> 保持登录
</label>
</div>
</div>
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">登 录</button>
</div>
</div>
</form>
</div>
</div>
<script src="{{ asset ("/assets/js/app.js") }}"></script>
<script>
$(function () {
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
increaseArea: '20%'
});
});
</script>
</body>
</html>

tips.blade.php

1
2
3
4
5
6
7
@if($errors->has($field))
@foreach ($errors->get($field) as $error)
<button class="btn btn-danger col-sm-12 btn-flat" style="width: 100%;">
<i class="fa fa-times-circle-o"></i> {{$error}}
</button>
@endforeach
@endif

error.blade.php

1
2
3
4
5
6
7
8
9
@if(Session::has('errors'))
<div id="errors-message" class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-ban"></i> 错误提示!</h4>
@foreach($errors->all() as $error)
{{$error}}
@endforeach
</div>
@endif

success.blade.php

1
2
3
4
5
6
7
@if(Session::has('success'))
<div id="success-message" class="alert alert-success alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-check"></i> 成功提示!</h4>
{{Session::get('success')}}
</div>
@endif

新增验证码Package


打开composer.json文件,修改代码如下:

1
2
3
4
5
"require": {
"php": ">=5.5.9",
"laravel/framework": "5.1.*",
"gregwar/captcha": "1.*"
},

gregwar/captcha是验证码Package,用来生成验证码,在终端执行以下命令进行安装。

1
sudo composer install

管理登录认证控制器

打开文件app/Http/Auth/AuthController.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
use Illuminate\Support\Facades\Session;
public function getLogin()
{
if (view()->exists('auth.authenticate')) {
return view('auth.authenticate');
}
$builder = new CaptchaBuilder();
$builder->build();
Session::put('phrase', $builder->getPhrase());
return view('auth.login')->with('captcha', $builder->inline());
}
public function postLogin(Request $request)
{
$this->validate($request, [
$this->loginUsername() => 'required',
'password' => 'required',
'captcha' => 'required'
], [
$this->loginUsername() . '.required' => '请输入邮箱或用户名称',
'password.required' => '请输入用户密码',
'captcha.required' => '请输入验证码'
]);
if (Session::get('phrase') !== $request->get('captcha')) {
return redirect()->back()->withErrors(array('captcha' => '验证码不正确'))->withInput();
}
}

使用迁移文件生成数据表


接下来打开终端,在项目下运行以下命令,生成用户表并创建用户

1
2
3
4
5
6
7
8
9
10
11
// 生成数据表
php artisan migrate
// 生成用户数据
php artisan tinker
$user = new App\Models\User;
$user->name = "admin";
$user->email = "admin@qq.com";
$user->password = bcrypt(123456);
$user->save();
exit

管理登录认证操作跳转页面

打开文件app\Http\Controller\Auth\AuthController,新增以下成员属性:

1
2
3
4
5
6
7
8
// 设置成功登录后转向的页面:
protected $redirectPath = '/';
// 设置登录失败后转向的页面:
protected $loginPath = '/auth/login';
// 设置退出登录后转向的页面:
protected $redirectAfterLogout = '/';

打开中间件app\Http\Middleware\RedirectIfAuthenticated.php,找到相应的地方,修改代码如下:

1
2
3
4
5
6
7
8
9
public function handle($request, Closure $next)
{
// 设置登录之后,输入/auth/login的跳转地址
if ($this->auth->check()) {
return redirect('/');
}
return $next($request);
}

使用 Laravel 构建内容管理框架(二)

初步搭建好后台管理功能的界面

新增控制器


在终端执行以下命令,在文件夹app/Http/Controllers/Backend下生成IndexController控制器。

1
php artisan make:controller Backend/IndexController --plain

修改文件IndexController.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
<?php
namespace App\Http\Controllers\Backend;
use App\Http\Requests;
use App\Http\Controllers\Controller;
class IndexController extends Controller
{
public function index()
{
$tasks = [
[
'name' => 'Design New Dashboard',
'progress' => '87',
'color' => 'danger'
],
[
'name' => 'Create Home Page',
'progress' => '76',
'color' => 'warning'
],
[
'name' => 'Some Other Task',
'progress' => '32',
'color' => 'success'
],
[
'name' => 'Start Building Website',
'progress' => '56',
'color' => 'info'
],
[
'name' => 'Develop an Awesome Algorithm',
'progress' => '10',
'color' => 'success'
]
];
return view('backend.index.index',compact('tasks'));
}
}

新增路由


打开文件app\Http\routes.php,修改代码如下:

1
2
3
Route::group(['namespace'=>'Backend'],function(){
Route::get('index','IndexController');
});

新增视图


resources/views/下新建文件夹backend,在文件夹backend下新建文件夹indexlayout

在文件夹layout新建文件如下:

  • header.blade.php
  • footer.blade.php
  • main.blade.php
  • sidebar.blade.php

在文件夹index新建文件如下:

  • index

header.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<header class="main-header">
<a href="index2.html" class="logo"><b>Admin</b>LTE</a>
<nav class="navbar navbar-static-top" role="navigation">
<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button">
<span class="sr-only">Toggle navigation</span>
</a>
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
<li class="dropdown messages-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-envelope-o"></i>
<span class="label label-success">4</span>
</a>
<ul class="dropdown-menu">
<li class="header">You have 4 messages</li>
<li>
<ul class="menu">
<li>
<a href="#">
<div class="pull-left">
<img src="{{ asset("/assets/img/user2-160x160.jpg") }}" class="img-circle" alt="User Image"/>
</div>
<h4>
Support Team
<small><i class="fa fa-clock-o"></i> 5 mins</small>
</h4>
<p>Why not buy a new awesome theme?</p>
</a>
</li>
</ul>
</li>
<li class="footer"><a href="#">See All Messages</a></li>
</ul>
</li>
<li class="dropdown notifications-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-bell-o"></i>
<span class="label label-warning">10</span>
</a>
<ul class="dropdown-menu">
<li class="header">You have 10 notifications</li>
<li>
<ul class="menu">
<li>
<a href="#">
<i class="fa fa-users text-aqua"></i> 5 new members joined today
</a>
</li>
</ul>
</li>
<li class="footer"><a href="#">View all</a></li>
</ul>
</li>
<li class="dropdown tasks-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-flag-o"></i>
<span class="label label-danger">9</span>
</a>
<ul class="dropdown-menu">
<li class="header">You have 9 tasks</li>
<li>
<ul class="menu">
<li>
<a href="#">
<h3>
Design some buttons
<small class="pull-right">20%</small>
</h3>
<div class="progress xs">
<div class="progress-bar progress-bar-aqua" style="width: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100">
<span class="sr-only">20% Complete</span>
</div>
</div>
</a>
</li>
</ul>
</li>
<li class="footer">
<a href="#">View all tasks</a>
</li>
</ul>
</li>
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="{{ asset("/assets/img/user2-160x160.jpg") }}" class="user-image" alt="User Image"/>
<span class="hidden-xs">Alexander Pierce</span>
</a>
<ul class="dropdown-menu">
<li class="user-header">
<img src="{{ asset("/assets/img/user2-160x160.jpg") }}" class="img-circle" alt="User Image"/>
<p>
Alexander Pierce - Web Developer
<small>Member since Nov. 2012</small>
</p>
</li>
<li class="user-body">
<div class="col-xs-4 text-center">
<a href="#">Followers</a>
</div>
<div class="col-xs-4 text-center">
<a href="#">Sales</a>
</div>
<div class="col-xs-4 text-center">
<a href="#">Friends</a>
</div>
</li>
<li class="user-footer">
<div class="pull-left">
<a href="#" class="btn btn-default btn-flat">Profile</a>
</div>
<div class="pull-right">
<a href="#" class="btn btn-default btn-flat">Sign out</a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</header>
1
2
3
4
5
6
<footer class="main-footer">
<div class="pull-right hidden-xs">
Anything you want
</div>
<strong>Copyright © {{date('Y')}} <a href="#">Company</a>.</strong> All rights reserved.
</footer>

main.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
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
<title>{{ $page_title or "AdminLTE Dashboard" }}</title>
<link rel="stylesheet" href="{{asset('/assets/css/app.css')}}">
@yield('style')
</head>
<body class="skin-blue">
<div class="wrapper">
@include('backend.layout.header')
@include('backend.layout.sidebar')
<div class="content-wrapper">
<section class="content-header">
<h1>
{{ $page_title or "Page Title" }}
<small>{{ $page_description or null }}</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li>
<li class="active">Here</li>
</ol>
</section>
<section class="content">
@yield('content')
</section>
</div>
@include('backend.layout.footer')
</div>
<script src="{{ asset ("/assets/js/app.js") }}" type="text/javascript"></script>
@yield('script')
</body>
</html>
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
<aside class="main-sidebar">
<section class="sidebar">
<div class="user-panel">
<div class="pull-left image">
<img src="{{ asset("/assets/img/user2-160x160.jpg") }}" class="img-circle" alt="User Image"/>
</div>
<div class="pull-left info">
<p>Alexander Pierce</p>
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
<form action="#" method="get" class="sidebar-form">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search..."/>
<span class="input-group-btn">
<button type='submit' name='search' id='search-btn' class="btn btn-flat"><i class="fa fa-search"></i>
</button>
</span>
</div>
</form>
<ul class="sidebar-menu">
<li class="header">HEADER</li>
<li class="active"><a href="#"><span>Link</span></a></li>
<li><a href="#"><span>Another Link</span></a></li>
<li class="treeview">
<a href="#"><span>Multilevel</span> <i class="fa fa-angle-left pull-right"></i></a>
<ul class="treeview-menu">
<li><a href="#">Link in level 2</a></li>
<li><a href="#">Link in level 2</a></li>
</ul>
</li>
</ul>
</section>
</aside>

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
@extends("backend.layout.main")
@section("content")
<div class="row">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Randomly Generated Tasks</h3>
<div class="box-tools pull-right">
<button class="btn btn-box-tool" data-widget="collapse" data-toggle="tooltip" title="Collapse">
<i class="fa fa-minus"></i>
</button>
<button class="btn btn-box-tool" data-widget="remove" data-toggle="tooltip" title="Remove">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="box-body">
@foreach($tasks as $task)
<h5>
{{ $task['name'] }}
<small class="label label-{{$task['color']}} pull-right">{{$task['progress']}}%</small>
</h5>
<div class="progress progress-xxs">
<div class="progress-bar progress-bar-{{$task['color']}}" style="width: {{$task['progress']}}%"></div>
</div>
@endforeach
</div>
<div class="box-footer">
<form action="#">
<input type="text" placeholder="New task" class="form-control input-sm"/>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">Second Box</h3>
<div class="box-tools pull-right">
<button class="btn btn-box-tool" data-widget="collapse" data-toggle="tooltip" title="Collapse">
<i class="fa fa-minus"></i>
</button>
<button class="btn btn-box-tool" data-widget="remove" data-toggle="tooltip" title="Remove">
<i class="fa fa-times"></i>
</button>
</div>
</div>
<div class="box-body">
A separate section to add any kind of widget. Feel free
to explore all of AdminLTE widgets by visiting the demo page
on <a href="https://almsaeedstudio.com">Almsaeed Studio</a>.
</div>
</div>
</div>
</div>
@endsection

使用 Sublime Text 3 开发 PHP 的心得

PHPStorm 不卡之日,SublimeText 舍弃之时

环境

  • 系统:OSX Yosemite 10.10.5
  • 版本:Sublime Text 3 Build 3103

安装

使用 Homebrew 安装 Sublime Text 3

1
brew install Caskroom/cask/sublime-text

终端配置

执行以下代码,即可在终端使用 Sublime Text 3

1
2
3
4
5
// 创建程序链接
ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" /usr/local/bin/subl
// 查看使用帮助
subl -h

快捷键

常用

按键 命令
⌘ + C 复制
⌘ + ⇧ + D 复制粘贴
⌘ + X 剪切
⌘ + V 粘贴
⌘ + ⇧ + V 保持缩进粘贴
⌘ + ⌥ + V 打开粘贴面板
⌘ + F 查找
⌘ + / 注释行
⌘ + ⌥ + / 生成注释
⌘ + ⇧ + F 全局查找、替换
⌘ + ↩ 在光标下插入行
⌘ + ⇧ + ↩ 在光标上插入行
⌘ + ⇧ + t 打开关闭的标签页
⌘ + [NUM] 选取对应数字的标签页
⌘ + ⇧ + [ 上一个标签页
⌘ + ⇧ + ] 下一个标签页
⌃ + ⇧ + ↑ 向上扩展光标
⌃ + ⇧ + ↓ 向下扩展光标

书签

按键 命令
⌘ + F2 生成或撤销书签
⇧ + F2 上一个书签
F2 下一个书签

删除

按键 命令
⌃ + ⇧ + K 删除当前行
⌘ + K + ⌫ 删除光标之后的内容
⌘ + K + ⌘ + K 删除光标之后的内容

跳转

按键 命令
⌘ + P , ⌘ + T 跳转到文件
⌘ + R 跳转到方法
⌃ + G 跳转到行数
⌃ + ⇧ + P 切换工作空间
⌘ + ⇧ + P 打开命令面板
⌃ + - 向后跳转至修改处
⌃ + ⇧ + - 向前跳转至修改处

光标移动

按键 命令
⌃ + P 光标向上移动
⌃ + N 光标向下移动
⌃ + B 光标向左移动
⌃ + F 光标向右移动
⌃ + A , ⌘ + ← 光标移动到行最左
⌃ + E , ⌘ + → 光标移动到行最右
⌃ + M 光标在括号里闭合跳转
⌘ + ⌃ + ↑ 向上移动选中行
⌘ + ⌃ + ↓ 向下移动选中行

选取

按键 命令
⌘ + L 选取行
⌘ + D 选取一个相同的文本
⌃ + ⇧ + G 选取所有相同的文本
⌘ + A 选取所有内容
⌘ + ⇧ + ↑ 向上选取所有内容
⌘ + ⇧ + ↓ 向下选取所有内容
⌘ + ⇧ + K 选取当前行HTML标签
⌘ + ⇧ + A 选取HTML标签闭合内容
⌘ + ⇧ + J 选取块闭合内容(可以折叠的内容为块)
⌃ + ⇧ + M 选取括号内容
⌃ + ⇧ + A 选取光标之前内容
⌃ + ⇧ + E 选取光标之后内容

窗口

按键 命令
⌘ + K + ⌘ + B 显示左侧栏
⌃ + ` 显示控制台
⌘ + B 执行编译
⌃ + ⌘ + F 全屏模式
⌃ + ⇧ + ⌘ + F 无干扰全屏模式
⌃ + ⇧ + 2 进行左右分屏
⌃ + ⇧ + 5 进行上下分屏
⌃ + ⇧ + 1 取消所有分屏
⌃ + Num 根据数字跳转至对应分屏

插件

PackageControl 插件

按下 ⌃ + ~ 打开控制台,复制粘贴以下代码并按下

1
import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())

Material Theme 插件

Material Theme 是一款编辑器主题插件

可以使用 Package Control 插件安装 Material-Theme 插件

Material-Theme 插件可提供三种外观样式:

1
2
3
4
5
6
7
8
9
10
11
//default
"theme": "Material-Theme.sublime-theme",
"color_scheme": "Packages/Material Theme/schemes/Material-Theme.tmTheme",
//Darker
"theme": "Material-Theme-Darker.sublime-theme",
"color_scheme": "Packages/Material Theme/schemes/Material-Theme-Darker.tmTheme",
//Lighter
"theme": "Material-Theme-Lighter.sublime-theme",
"color_scheme": "Packages/Material Theme/schemes/Material-Theme-Lighter.tmTheme",

AdvanceNewFile 插件

AdvanceNewFile 是一款快速操作文件的插件

可以使用 Package Control 安装 AdvanceNewFile 插件

安装完毕之后,可以按下 ⌘ + ⇧ + P 调出命令面板,执行以下命令对文件进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//新增
ANF:New File
//删除
ANF:Delete File
//重命名
ANF:Rename File
//复制当前文件
ANF:Copy Current File
//删除当前文件
ANF:Delete Current File

亦可使用快捷键 ⌘ + ⌥ + N 快速新建文件

PHP Getters and Setters 插件

PHP Getters and Setters 是一款根据类文件的成员属性,快速生成 Getter 方法和 Setter 方法的插件

可以使用 Package Control 安装 PHP Getters and Setters 插件

执行以下命令快速生成 Getter 和 Setter 的代码

1
2
3
4
5
6
7
// 生成 Getter 方法
PHP:Generate Getters
// 生成 Setter 方法
PHP:Generate Setters
// 同时生成 Getter 和 Setter
PHP:Generate Getters and Setters

PHP Companion 插件

PHP Companion 插件提供以下功能

  • find_use
    生成所选类的use声明

  • expand_fqcn
    生成所选类的引用路径

  • import_namespace
    生成所选文件的命名空间

  • goto_definition_scope
    寻找所选代码的定义源头

  • insert_php_constructor_property
    生成当前类的构造方法

Preferences -> Key Bindings - User 新增以下快捷键调用上述插件命令

1
2
3
4
5
//PHP Companion
{"keys":["f9"],"command":"find_use"},
{"keys":["f10"],"command":"expand_fqcn"},
{"keys":["f11"],"command":"goto_definition_scope"},
{"keys":["f12"],"command":"insert_php_constructor_property"},

Laravel Artisan 插件

Laravel Artisan 是一款让开发可以不使用终端就能运行 Artisan CLI 的插件

命令与在终端使用 php artisan 基本一致,可以按下 ⌘ + ⇧ + P 打开命令面板,执行以下命令:

1
2
3
4
5
6
7
8
9
10
11
// 生成控制器
Laravel Artisan5:Make:Controller
// 生成请求
Laravel Artisan5:Make:Request
// 生成服务提供者
Laravel Artisan5:Make:Provider
// 生成数据迁移文件
Laravel Artisan5:Make:Mrgiation

DocBlockr 插件

DocBlockr 是一款根据代码自动生成注释的插件

All Autocomplete 插件

All Autocomplete 是一款自动补全的插件,它会查找你打开的所有文件的代码,然后进行代码补全

SublimeLinter-php 插件

SublimeLinter 是一款检查PHP语言错误的插件

使用 Package Control 安装 SublimeLinter 插件
使用 Package Control 安装 SublimeLinter-php 插件

Preferences -> Package Settings -> SublimeLinter -> Settings - User 新增以下代码:

1
"show_errors_on_save": true,

上述操作会让编辑器在保存文件的时候,提示错误信息

代码片段

Sublime Text 可以创建自定义的代码片段

点击菜单栏 Tools -> New Snippet ,创建文件 PHP Public Method.sublime-snippet

1
2
3
4
5
6
7
8
9
10
11
<snippet>
<content><![CDATA[
public function ${1:function_name}(${2:param}){
${3:return result}
}
]]></content>
<!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
<tabTrigger>pubf</tabTrigger>
<!-- Optional: Set a scope to limit where the snippet will trigger -->
<scope>source.php</scope>
</snippet>

当在PHP文件写代码时,输入 pubf 并按下 Tab 键,会生成相对应的代码片段

1
2
3
public function function_name(param){
return true
}

格式化代码

在终端执行以下命令安装 php-cs-fix

1
brew install php-cs-fixer

假如要对某个 PHP 文件进行格式化操作,运行以下命令:

1
2
cd dir
php-cs-fixer fix fileName.php --level="psr2"

使用 Sublime Text 3Build System 格式化代码
点击 Tools -> Build System -> New Bulid System ...,打开代码如下:

1
"shell_cmd":"make"

修改上述代码如下:

1
2
3
{
"shell_cmd": "php-cs-fixer fix $file --level=psr2"
}

保存之后,在 Tools-Build System 选择 PSR-2,按 ⌘ + B对当前文件进行 PSR-2 格式化操作。

觉得格式化提示信息麻烦,可以在Preferences-Settings - User添加

1
"show_panel_on_build":false

使用 Laravel 构建内容管理框架(一)

为使用 Laravel 构建内容管理框架做好环境、代码和脚本的准备

安装Laravel


在终端使用Composer安装Laravel

1
sudo composer create-project laravel/laravel backend --prefer-dist 5.1.x

执行后,会在当前目录下新建一个文件夹,里面就是新建的Laravel项目

安装gulp、bower


在终端使用npm安装gulp

1
2
3
4
5
6
7
sudo npm install -g gulp #全局安装npm
sudo npm install -g bower #全局安装bower
cd backend
sudo npm install gulp #在项目本地安装gulp
sudo npm install bower #在项目本地安装bower
sudp npm install #安装项目Node依赖、Laravel Elixir

运行gulp tdd之后,会自动监控测试单元

使用bower集成前端依赖库


在项目根目录新增文件.bowerrc

1
2
3
{
"directory": "vendor/bower"
}

上述配置告诉bower在vendor/bower存放下载文件
执行命令bower init创建文件bower.json

依赖安装所需前端资源

1
2
3
sudo bower install admin-lte
sudo bower install fontawesome
sudo bower install ionicons

打开文件gulpfile.js,编辑如下:

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
var gulp = require('gulp');
var elixir = require('laravel-elixir');
gulp.task('copy', function () {
// jQuery
gulp.src("vendor/bower/AdminLTE/plugins/jQuery/jQuery-2.1.4.min.js")
.pipe(gulp.dest("resources/assets/js/"));
// bootstarp
gulp.src("vendor/bower/AdminLTE/bootstrap/css/bootstrap.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/AdminLTE/bootstrap/js/bootstrap.min.js")
.pipe(gulp.dest("resources/assets/js/"));
// AdminLTE
gulp.src("vendor/bower/AdminLTE/dist/css/AdminLTE.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/AdminLTE/dist/css/skins/skin-blue.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/AdminLTE/dist/js/app.min.js")
.pipe(gulp.dest("resources/assets/js/"));
gulp.src("vendor/bower/AdminLTE/dist/img/*")
.pipe(gulp.dest("public/assets/img/"));
// Fontawesome
gulp.src("vendor/bower/font-awesome/css/font-awesome.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/font-awesome/fonts/*")
.pipe(gulp.dest("public/assets/fonts/"));
// Ionicons
gulp.src("vendor/bower/Ionicons/css/ionicons.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/Ionicons/fonts/*")
.pipe(gulp.dest("public/assets/fonts/"));
// slimScroll
gulp.src("vendor/bower/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js")
.pipe(gulp.dest("resources/assets/js/"));
// iCheck
gulp.src("vendor/bower/AdminLTE/plugins/iCheck/icheck.min.js")
.pipe(gulp.dest("resources/assets/js/"));
gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue.png")
.pipe(gulp.dest("public/assets/css/"));
gulp.src("vendor/bower/AdminLTE/plugins/iCheck/square/blue@2x.png")
.pipe(gulp.dest("public/assets/css/"));
// select2
gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.full.min.js")
.pipe(gulp.dest("resources/assets/js/"));
gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.min.js")
.pipe(gulp.dest("resources/assets/js/"));
gulp.src("vendor/bower/AdminLTE/plugins/select2/select2.min.css")
.pipe(gulp.dest("resources/assets/css/"));
// pace
gulp.src("vendor/bower/AdminLTE/plugins/pace/pace.min.css")
.pipe(gulp.dest("resources/assets/css/"));
gulp.src("vendor/bower/AdminLTE/plugins/pace/pace.min.js")
.pipe(gulp.dest("resources/assets/js/"));
});
elixir(function (mix) {
// 合并javascript脚本
mix.scripts(
[
'jQuery-2.1.4.min.js',
'bootstrap.min.js',
'app.min.js',
'pace.min.js',
'jquery.slimscroll.min.js',
'icheck.min.js',
'select2.full.min.js',
'select2.min.js'
],
'public/assets/js/app.js',
'resources/assets/js/'
);
// 合并css脚本
mix.styles(
[
'bootstrap.min.css',
'pace.min.css',
'select2.min.css',
'AdminLTE.min.css',
'skin-blue.min.css',
'font-awesome.min.css',
'ionicons.min.css',
'blue.css'
],
'public/assets/css/app.css',
'resources/assets/css/'
);
// 运行单元测试
mix.phpUnit();
});

在终端执行命令如下:

1
2
gulp copy
gulp

上述操作将会在根目录文件夹public/assets/生成所需的前端资源,接下来就是在视图模板中使用。