路由:
会员列表页面:/admin/member/index get类型
会员的添加页面:/admin/member/add any类型
异步头像上传地址:/admin/uploader/webuploader post类型
异步四级联动数据获取:/admin/member/getAreaById get类型
添加路由
Route::group(['prefix'=>'admin'],function () { Route::get('public/login', 'Admin\PublicController@login')->name('login'); Route::post('public/check', 'Admin\PublicController@check'); Route::get('public/logout', 'Admin\PublicController@logout'); }); Route::group(['prefix'=>'admin','middleware'=>['auth:admin','checkrbac']],function () { Route::get('index/index','Admin\IndexController@index'); Route::get('index/welcome','Admin\IndexController@welcome'); Route::get('manager/index','Admin\Managercontroller@index'); Route::get('auth/index','Admin\AuthController@index'); Route::any('auth/add','Admin\AuthController@add'); Route::get('role/index','Admin\RoleController@index'); Route::any('role/assign','Admin\RoleController@assign'); Route::any('member/index','Admin\MemberController@index'); Route::any('member/add','Admin\MemberController@add'); Route::any('member/getAreaById','Admin\MemberController@getAreaById'); Route::any('uploader/webuploader','Admin\UploaderController@webuploader'); });
控制器文件:
添加控制器 php artisan make:controller Admin/MemberController
模型文件:
创建命令: php artisan make:model Admin/Member
定义模型规范
protected $table = 'member'; //绑定模型关联的表 member
①创建index方法,获取数据,展示视图
public function index(){ $data = Member::get(); return view('admin.member.index',compact('data')); }
②创建需要的视图文件 目录 resources\views\admin\member
文件名 index.blade.php
模板调用
@foreach($data as $val) <tr class="text-c"> <td><input type="checkbox" value="1" name=""></td> <td>{{$val->id}}</td> <td>{{$val->username}}</td> <td><img src="{{$val->avatar}}" width="60" /></td> <td>@if($val->gender=='1')男@elseif($val->gender=='2')女@else保密@endif</td> <td>{{$val->mobile}}</td> <td >{{$val->email}}</td> <td>{{$val->created_at}}</td> <td class="td-status"> @if($val->status=='1') <span class="label label-danger radius">已停用</span></td> @else <span class="label label-success radius">已启用</span></td> @endif <td class="td-status">@if($val->type==1)学生@else 老师 @endif</th> <td class="td-manage"><a style="text-decoration:none" onClick="member_stop(this,'10001')" href="javascript:;" title="停用"><i class="Hui-iconfont"></i></a> <a title="编辑" href="javascript:;" onclick="member_edit('编辑','member-add.html','4','','510')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont"></i></a> <a style="text-decoration:none" class="ml-5" onClick="change_password('修改密码','change-password.html','10001','600','270')" href="javascript:;" title="修改密码"><i class="Hui-iconfont"></i></a> <a title="删除" href="javascript:;" onclick="member_del(this,'1')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont"></i></a></td> </tr> @endforeach
调用其他数据表内容
$country=DB::table('area')->where('pid',0)->get();
post数据
if(Input::method() =='POST'){ $result = Member::insert([ 'username' => Input::get('username'), 'password' => bcrypt('password'), 'gender' => Input::get('gender'), 'mobile' => Input::get('mobile'), 'email' => Input::get('email'), 'avatar' => '/statics/avatar.jpg', 'country_id' => Input::get('country_id'), 'province_id' => Input::get('province_id'), 'city_id' => Input::get('city_id'), 'county_id' => Input::get('county_id'), 'type' => Input::get('type'), 'status' => Input::get('status'), 'created_at' => date('Y-m-d H:i:s') ]); //返回输出 return $result ? '1' : '0';
知识点
{{ route('users.edit', Auth::id()) }} //模型文件调用资源路由示范 /users/1/edit
{{ route('users.show', Auth::id()) }} //模型文件调用资源路由示范 /users/1
路由文件
Route::resource('users', 'UsersController', ['only' => ['show', 'update', 'edit']]);
数据表中添加单独的字段
php artisan make:migration add_avatar_and_introduction_to_users_table --table=users
public function up() { Schema::table('users', function (Blueprint $table) { $table->string('avatar')->nullable(); $table->string('introduction')->nullable(); }); }
php artisan migrate
知识点
public function show(User $user) { return view('users.show', compact('user')); }
User 是User模型
Laravel 会自动解析定义在控制器方法(变量名匹配路由片段)中的 Eloquent 模型类型声明。在上面代码中,由于 show() 方法传参时声明了类型 —— Eloquent 模型 User ,对应的变量名 $user 会匹配路由片段中的 {user} ,这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。 此功能称为 ,是『约定优于配置』设计范式的体现,同时满足以下两种情况,此功能即会自动启 用:
1). 路由声明时必须使用 Eloquent 模型的单数小写格式来作为 路由片段参数,User 对应 {user} :
Route::get('/users/{user}', 'UsersController@show')->name('users.show');
上面路由部分讲过,在使用资源路由 Route::resource('users', 'UsersController'); 时,默认已经包含了上面的声 明。
2). 控制器方法传参中必须包含对应的 Eloquent 模型类型 提示,并且是有序的:
public function show(User $user) { return view('users.show', compact('user')); }
当请求 http://larabbs.test/users/1 并且满足以上两个条件时,Laravel 将会自动查找 ID 为 1 的用户并赋值到变量 $user 中,如果数据库中找不到对应的模型实例,会自动生成 HTTP 404 响应,例如此刻我们只注册了 Summer 和 Monkey 用 户,数据库里只有两条数据,当我们访问 http://larabbs.test/users/3 ID 为 3 的用户时:
则会返回404
3. 继续看 show() 方法里的代码:
return view('users.show', compact('user'));
我们将用户对象变量 $user 通过 方法转化为一个关联数组,并作为第二个参数传递给 view 方法,将 变量数据传递到视图中。 show 方法添加完成之后,在视图中,我们即可直接使用 $user 变量来获取 view 方法传递给视图的用户数据。
重点
php artisan make:request UserRequest
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class UserRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ // ]; } }
表单请求验证(FormRequest)的工作机制,是利用 Laravel 提供的依赖注入功能,在控制器方法,如上面我们的 update() 方法声明中,传参 UserRequest。这将触发表单请求类的自动验证机制,验证发生在 UserRequest 中,并使 用此文件中方法 rules() 定制的规则,只有当验证通过时,才会执行 控制器 update() 方法中的代码。否则抛出异 常,并重定向至上一个页面,附带验证失败的信息。 authorize() 方法是表单验证自带的另一个功能 —— 权限验证,本课程中我们不会使用此功能,关于用户授权,我们 将会在后面章节中使用更具扩展性的方案,此处我们 return true; ,意味所有权限都通过即可。 接下来我们需要定制 rules() 方法,如下:
app/Http/Requests/UserRequest.php
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Auth; class UserRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'name' => 'required|between:3,25|regex:/^[A-Za-z0-9\-\_]+$/|unique:users,name,' . Auth::id(), 'email' => 'required|email', 'introduction' => 'max:80', ]; } }
/5、头像上传
<div class="form-group mb-4"> <label for="" class="avatar-label">用户头像</label> <input type="file" name="avatar" class="form-control-file"> @if($user->avatar) <br> <img class="thumbnail img-responsive" src="{{ $user->avatar }}" width="200" /> @endif </div>
在 Laravel 中,我们可直接通过请求对象 (Request) 来获取用户上传的文件,如以下两种方法:
$file = $request->file('avatar'); // 第二种方法,可读性更高 $file = $request->avatar;
在控制器中 测试
dd($request->avatar);
打印出来是图片文件名称的字符串,与我们的预期不符,图片上传后通过 $request 取到的应是图片对象。 经过一番仔细检查后,发现是因为我们忘记为表单添加 enctype="multipart/form-data" 声明了。请记住,在图片或 者文件上传时,为表单添加此句声明是必须的。
6、 存储用户上传图片
我们可以将『图片上传』核心操作做成一个工具类:
app/Handlers/ImageUploadHandler.php
<?php namespace App\Handlers; class ImageUploadHandler { // 只允许以下后缀名的图片文件上传 protected $allowed_ext = ["png", "jpg", "gif", 'jpeg']; public function save($file, $folder, $file_prefix) { // 构建存储的文件夹规则,值如:uploads/images/avatars/201709/21/ // 文件夹切割能让查找效率更高。 $folder_name = "uploads/images/$folder/" . date("Ym/d", time()); // 文件具体存储的物理路径,`public_path()` 获取的是 `public` 文件夹的物理路径。 // 值如:/home/vagrant/Code/larabbs/public/uploads/images/avatars/201709/21/ $upload_path = public_path() . '/' . $folder_name; // 获取文件的后缀名,因图片从剪贴板里黏贴时后缀名为空,所以此处确保后缀一直存在 $extension = strtolower($file->getClientOriginalExtension()) ?: 'png'; // 拼接文件名,加前缀是为了增加辨析度,前缀可以是相关数据模型的 ID // 值如:1_1493521050_7BVc9v9ujP.png $filename = $file_prefix . '_' . time() . '_' . str_random(10) . '.' . $extension; // 如果上传的不是图片将终止操作 if ( ! in_array($extension, $this->allowed_ext)) { return false; }
$file->move($upload_path, $filename);
return [
'path' => config('app.url') . "/$folder_name/$filename"
];
}
}
我们将使用 app/Handlers 文件夹来存放本项目的工具类,『工具类(utility class)』是指一些跟业务逻辑相关性不强 的类, Handlers 意为 处理器 ,ImageUploadHandler 意为图片上传处理器,简单易懂。 接下来我们需要在 UsersController 里调用(注意顶部 use 引入):
app/Http/Controllers/UsersController.php
use App\Handlers\ImageUploadHandler;
public function update(UserRequest $request, ImageUploadHandler $uploader, User $user){ $data = $request->all(); //dd($data); if($request->avatar){ $result = $uploader->save($request->avatar, 'avatars', $user->id); if($result){ $data['avatar'] = $result['path']; }else{ //上传有错误 withErrors可以携带回错误信 return back()->withErrors(['上传图片格式只支持png, jpg, gif, jpeg这四种格式']); } } $user->update($data); return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功'); }
1. 因为我们使用了命名空间,所以需要在顶部加载 use App\Handlers\ImageUploadHandler; ; 2. $data = $request->all(); 赋值 $data 变量,以便对更新数据的操作; 3. 以下代码处理了图片上传的逻辑,注意 if ($result) 的判断是因为 ImageUploadHandler 对文件后缀名做 了限定,不允许的情况下将返回 false : if ($request->avatar) { $result = $uploader->save($request->avatar, 'avatars', $user->id); if ($result) { $data['avatar'] = $result['path']; } }
7、 图片验证
app/Http/Requests/UserRequest.php
public function rules() { return [ 'name' => 'required|between:3,25|regex:/^[A-Za-z0-9\-\_]+$/|unique:users,name,' . Auth::id(), 'email' => 'required|email', 'introduction' => 'max:80', 'avatar' => 'mimes:jpeg,bmp,png,gif|dimensions:min_width=208,min_height=208', ]; }
public function messages() { return [ 'avatar.mimes' =>'头像必须是 jpeg, bmp, png, gif 格式的图片', 'avatar.dimensions' => '图片的清晰度不够,宽和高需要 208px 以上', 'name.unique' => '用户名已被占用,请重新填写', 'name.regex' => '用户名只支持英文、数字、横杆和下划线。', 'name.between' => '用户名必须介于 3 - 25 个字符之间。', 'name.required' => '用户名不能为空。', ]; }
1. rules() 方法中新增了图片比例验证规则 ,仅允许上传宽和高都大于 208px 的图片;
2. messages() 方法中新增了头像出错时的提示信息。
8、裁剪图片
1、
composer require intervention/image
2、配置信息
php artisan vendor:publish --provider=Intervention\Image\ImageServiceProviderLaravelRecent
3. 开始裁剪
我们将裁切的逻辑写在 ImageUploadHandler 中,请将以下代码替换: app/Handlers/ImageUploadHandler.php
<?php namespace App\Handlers; use Image; class ImageUploadHandler { protected $allowed_ext = ["png", "jpg", "gif", 'jpeg']; public function save($file, $folder, $file_prefix, $max_width = false) { // 构建存储的文件夹规则,值如:uploads/images/avatars/201709/21/ // 文件夹切割能让查找效率更高。 $folder_name = "uploads/images/$folder/" . date("Ym/d", time()); // 文件具体存储的物理路径,`public_path()` 获取的是 `public` 文件夹的物理路径。 // 值如:/home/vagrant/Code/larabbs/public/uploads/images/avatars/201709/21/ $upload_path = public_path() . '/' . $folder_name; // 获取文件的后缀名,因图片从剪贴板里黏贴时后缀名为空,所以此处确保后缀一直存在 $extension = strtolower($file->getClientOriginalExtension()) ?: 'png'; // 拼接文件名,加前缀是为了增加辨析度,前缀可以是相关数据模型的 ID // 值如:1_1493521050_7BVc9v9ujP.png $filename = $file_prefix . '_' . time() . '_' . str_random(10) . '.' . $extension; // 如果上传的不是图片将终止操作 if ( ! in_array($extension, $this->allowed_ext)) { return false; } // 将图片移动到我们的目标存储路径中 $file->move($upload_path, $filename); // 如果限制了图片宽度,就进行裁剪 if ($max_width && $extension != 'gif') { // 此类中封装的函数,用于裁剪图片 $this->reduceSize($upload_path . '/' . $filename, $max_width); } return [ 'path' => config('app.url') . "/$folder_name/$filename" ]; } public function reduceSize($file_path, $max_width) { // 先实例化,传参是文件的磁盘物理路径 $image = Image::make($file_path); // 进行大小调整的操作 $image->resize($max_width, null, function ($constraint) { // 设定宽度是 $max_width,高度等比例双方缩放 $constraint->aspectRatio(); // 防止裁图时图片尺寸变大 $constraint->upsize(); }); // 对图片修改后进行保存 $image->save(); } }
此次新增 reduceSize() 方法,以及此方法的调用。
以上的 save() 方法中,我们新增了 $max_width 参数,用来指定最大图片宽度,我们修改 UsersController 的 update() 方法中的调用,修改为:
$result = $uploader->save($request->avatar, 'avatars', $user->id, 416);
9 授权访问
存在两个问题
1、未登录用户可以访问 edit 和 update 动作
2、 登录用户可以更新其它用户的个人信息,登录 Summer 用户然后访问 Monkey 用户的编辑资料页面
限制游客访问
我们使用 Laravel 提供身份验证(Auth)中间件来过滤未登录用户的 edit , update 动作:
app/Http/Controllers/UsersController.php
public function __construct() { $this->middleware('auth', ['except' => ['show']]); }
__construct 是 PHP 的构造器方法,当一个类对象被创建之前该方法将会被调用。我们在 __construct 方法中调 用了 middleware 方法,该方法接收两个参数,第一个为中间件的名称,第二个为要进行过滤的动作。我们通过 except 方法来设定 指定动作 不使用 Auth 中间件进行过滤,意为 —— 除了此处指定的动作以外,所有其他动作都必 须登录用户才能访问,类似于黑名单的过滤机制。相反的还有 only 白名单方法,将只过滤指定动作。我们提倡在控制 器 Auth 中间件使用中,首选 except 方法,这样的话,当你新增一个控制器方法时,默认是安全的,此为最佳实践。 Laravel 提供的 Auth 中间件在过滤指定动作时,如该用户未通过身份验证(未登录用户),将会被重定向到登录页面:
用户只能编辑自己的资料
使用以下命令来生成一个名为 UserPolicy 的授权策略类文件,用于管理用户模型的授权
php artisan make:policy UserPolicy
所有生成的授权策略文件都会被放置在 app/Policies 文件夹下。 让我们为默认生成的用户授权策略添加 update 方法,用于用户更新时的权限验证。
app/Policies/UserPolicy.php
public function update(User $currentUser, User $user) { return $currentUser->id === $user->id; }
update 方法接收两个参数,第一个参数默认为当前登录用户实例,第二个参数则为要进行授权的用户实例。当两个 id 相同时,则代表两个用户是相同用户,用户通过授权,可以接着进行下一个操作。如果 id 不相同的话,将抛出 403 异常 信息来拒绝访问。 使用授权策略需要注意以下两点: 1. 我们并不需要检查 $currentUser 是不是 NULL。未登录用户,框架会自动为其 所有权限 返回 false ; 2. 调用时,默认情况下,我们 不需要 传递当前登录用户至该方法内,因为框架会自动加载当前登录用户(接着看下 去,后面有例子); 接下来我们还需要在 AuthServiceProvider 类中对授权策略进行注册。 AuthServiceProvider 包含了一个 policies 属性,该属性用于将各种模型对应到管理它们的授权策略上。我们需要为用户模型 User 指定授权策略 UserPolicy 。
app/Providers/AuthServiceProvider.php
protected $policies = [ 'App\Model' => 'App\Policies\ModelPolicy', \App\Models\User::class => \App\Policies\UserPolicy::class, ];
授权策略定义完成之后,我们便可以在控制器中使用 authorize 方法来检验用户是否授权。默认的 App\Http\Controllers\Controller 控制器基类包含了 Laravel 的 AuthorizesRequests trait。此 trait 提供了 authorize 方法,它可以被用于快速授权一个指定的行为,当无权限运行该行为时会抛出 HttpException。 authorize 方法接收两个参数,第一个为授权策略的名称,第二个为进行授权验证的数据。 我们需要为 edit 和 update 方法加上这行:
$this->authorize('update', $user);
这里 update 是指授权类里的 update 授权方法, $user 对应传参 update 授权方法的第二个参数。正如 上面定义 update 授权方法时候提起的,调用时,默认情况下,我们 不需要 传递第一个参数,也就是当前登录用 户至该方法内,因为框架会 自动 加载当前登录用户。
书写的位置如下: app/Http/Controllers/UsersController.php
public function edit(User $user) { $this->authorize('update', $user); return view('users.edit', compact('user')); } public function update(UserRequest $request, ImageUploadHandler $uploader, User $user) { $this->authorize('update', $user); $data = $request->all();
10 git相关
laravel数据回滚
php artisan migrate:rollback
还原修改文件到原始状态:
git checkout .
git clean -f -d
命令 git clean 作用是清理项目, -f 是强制清理文件的设置, -d 选项命令连文件夹一并清除。