Laravel 进阶笔记 3

上传图片/文件:

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

view里面一个input, type是file, 记得把form的enctype="multipart/form-data"

在后台, 有两种方法, 都通过Request来获取上传的文件.

$ file = $request -> file('avatar');

$ file = $request -> avatar;

然后起一个类, app\Handlers\ImageHandler.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"
        ];
    }
}

返回的是保存地址跟文件名.

在UserController里面调用这个handler

    public function update(UserRequest $request,ImageUploadHandler $uploader, User $user)
    {
        $data = $request->all();

        //如果上传了头像
        if ($request->avatar) {
            //尝试保存头像并返回保存目录
            $result = $uploader->save($request->avatar, 'avatars', $user->id);
            //成功之后, 写入数组
            if ($result) {
                $data['avatar'] = $result['path'];
            }
        }

        //写库
        $user->update($data);

        //dd($request->avatar);
        //$user->update($request->all());
        return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功!');
    }

引出一个问题, php的函数形参, 不讲求顺序的么?

可以乱来的?

这可能就是这种语言的好处, 函数都是可以变的, 不用编译, 不像c, java这种, 一定要定义好方法的形参, 返回值, 因为一旦编译完, 不可能动态的修改函数的形参, 甚至数据类型都不可以, 但是好处是执行效率更高, 而php的灵活性更好, 甚至function的调用生成都可以是动态的.

裁剪图片

如果用户的图片太大, 为了节省宝贵的CDN流量费用, 用第三方的包进行图片裁剪吧, Intervention/image

$ composer require intervention/image

获取配置信息

创建image的配置文件:

$ php artisan vendor:publish --provider="Intervention\Image\ImageServiceProviderLaravel5"

在ImageUploadHandler里面写好裁剪的逻辑

...
        if ($max_width && $extension != 'gif') {

            // 此类中封装的函数,用于裁剪图片
            $this->reduceSize($upload_path . '/' . $filename, $max_width);
        }
...

  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();
    }

安全性

游客是不可以edit任何人信息的, 现在不是, 所以要加中间件进行控制.

在UsersController里面, 添加auth中间件, 除了show操作之外都要进行auth.

  public function __construct()
    {
        $this->middleware('auth', ['except' => ['show']]);
    }

之所以加在controller里面, 因为controller每次被访问都会被实例化一次, 用Log::debug('')可验证.

认证是一种逻辑, 另一种逻辑是, 用户自己编辑自己的资料, 而不能编辑别人的. 这样就需要UserPolicy, 生成policy可以用make:policy

$ php artisan make:policy UserPolicy

增加update方法,

    public function update(User $currentUser, User $user)
    {
        return $currentUser->id === $user->id;
    }

 

然后再AuthServiceProvider里面注册一下这个update, 不然usercontroller的auth哪知道你定的什么规则.

 protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
        \App\Models\User::class  => \App\Policies\UserPolicy::class,
    ];

然后在UserController里面, 把这个update认证policy用authorize方法限定一下:

  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();

        if ($request->avatar) {
            $result = $uploader->save($request->avatar, 'avatars', $user->id, 416);
            if ($result) {
                $data['avatar'] = $result['path'];
            }
        }

        $user->update($data);
        return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功!');
    }

这事儿是这么个意思吧, 本来是controller需要验证这个user, 如果单独写验证方法, 显得太傻, 无法复用, 于是写个规则, 就是这个叫'update'的policy, 然后将这个policy注册到auth的provider里面, 连同别的规则, 一起都放这里面, 然后你要使用的时候无论你是更新的时候还是update的时候, 都统一用authorize+policy名字的方法, 调用认证规则进行认证, 以后你要在其他controller, 操控其他资源, 也同样可以直接调用authorize这个policy即可, 这样就解耦了, 厉害!

这个就是"提供者provider"的pattern.

 

posted @ 2019-01-03 08:57  Montauk  阅读(160)  评论(0编辑  收藏  举报