laravel6开发论坛-个人页面

定义路由

资源路由

css 复制代码
Route::resource('users', 'UsersController', ['only' => ['show', 'update', 'edit']]);

创建控制器

go 复制代码
php artisan make:controller UsersController

进入app/Http/Controllers/UsersController.php

scala 复制代码
use App\Models\User;

class UsersController extends Controller
{
    // 个人主页
    public function show(User $user)
    {
        return view('users.show', compact('user'));
    }
}

创建视图

来新建一个用户个人页面。

resources/views/users/show.blade.php

xml 复制代码
@extends('layouts.app')
@section('title', $user->name . ' 的个人中心')
@section('content')
  <div class="row">
    <div class="col-lg-3 col-md-3 hidden-sm hidden-xs user-info">
      <div class="card ">
        <img class="card-img-top" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F89e47fdc-a132-4ce7-a46d-86a0777eb6af%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1712478362&t=0d57949efcef42ff9ba4d755af700353">
 <div class="card-body">
        <h5><strong>个人简介</strong></h5>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. </p>
        <hr>
        <h5><strong>注册于</strong></h5>
        <p>January 01 1901</p>
      </div>
    </div>
  </div>
  <div class="col-lg-9 col-md-9 col-sm-12 col-xs-12">
    <div class="card ">
      <div class="card-body">
        <h1 class="mb-0" style="font-size:22px;">{{ $user->name }} <small>{{ $user->email }}</small></h1>
      </div>
    </div>
    <hr>
    {{-- 用户发布的内容 --}}
    <div class="card ">
      <div class="card-body">
        暂无数据 ~_~
      </div>
    </div>
  </div>
  </div>
@stop

这时候我们再访问用户个人页面,便能够看到基本的数据展示:

编辑个人资料

作为个人中心页面 ,查看用户表相关的迁移文件: database/migrations/2014_10_12_000000_create_users_table.php,我们需要增加『头像』和『个人简介』字段

scss 复制代码
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}

新建迁移文件

ini 复制代码
php artisan make:migration add_avatar_and_introduction_to_users_table --table=users

现在我们来为新增的迁移文件加上这两个字段:

database/migrations/[timestamp]_add_avatar_and_introduction_to_users_table.php

php 复制代码
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddAvatarAndIntroductionToUsersTable extends Migration
{
    /**
     * 执行迁移
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('avatar')->nullable();
            $table->string('introduction')->nullable();
        });
    }

    /**
     * 回滚迁移
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('avatar');
            $table->dropColumn('introduction');
        });
    }
}

运行迁移

复制代码
php artisan migrate

打开数据库查看工具,即可看到我们新添加的两个字段:

增加入口

接下来我们需要增加一个页面链接入口,让登录用户可以很方便地进入到自己的『资料编辑页面』: resources/views/layouts/_header.blade.php

ini 复制代码
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
  <a class="dropdown-item" href="{{ route('users.show', Auth::id()) }}">个人中心</a>
  <a class="dropdown-item" href="{{ route('users.edit', Auth::id()) }}">编辑资料</a>
  <div class="dropdown-divider"></div>
  <a class="dropdown-item" id="logout" href="#">
    <form action="{{ route('logout') }}" method="POST">
      @csrf
      <button class="btn btn-block btn-danger" type="submit" name="button">退出</button>
    </form>
  </a>
</div>

编辑页面

我们需要前往 UsersController 控制器里创建 edit() 方法:

php 复制代码
// 编辑页面
public function edit(User $user)
{
    return view('users.edit', compact('user'));
}

来创建编辑视图文件: resources/views/users/edit.blade.php

ini 复制代码
@extends('layouts.app')
@section('content')
  <div class="container">
    <div class="col-md-8 offset-md-2">
      <div class="card">
        <div class="card-header">
          <h4>
            <i class="glyphicon glyphicon-edit"></i> 编辑个人资料
          </h4>
        </div>
        <div class="card-body">
          <form action="{{ route('users.update', $user->id) }}" method="POST" accept-charset="UTF-8">
            <input type="hidden" name="_method" value="PUT">
            <input type="hidden" name="_token" value="{{ csrf_token() }}">

            <div class="form-group">
              <label for="name-field">用户名</label>
              <input class="form-control" type="text" name="name" id="name-field"
                     value="{{ old('name', $user->name) }}"/>
            </div>
            <div class="form-group">
              <label for="email-field">邮 箱</label>
              <input class="form-control" type="text" name="email" id="email-field"
                     value="{{ old('email', $user->email) }}"/>
            </div>
            <div class="form-group">
              <label for="introduction-field">个人简介</label>
              <textarea name="introduction" id="introduction-field" class="form-control"
                        rows="3">{{ old('introduction', $user->introduction) }}</textarea>
            </div>

            <div class="form-group mb-4">
              <label for="" class="avatar-label">用户头像</label>
              <input type="file" name="avatar" class="form-control-file">

            </div>
            <div class="well well-sm">
              <button type="submit" class="btn btn-primary">保存</button>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
@endsection

表单请求验证

我们创建 UserRequest ,使用以下命令:

go 复制代码
php artisan make:request UserRequest

执行成功后会生成以下文件:

app/Http/Requests/UserRequest.php

php 复制代码
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;

class UserRequest extends FormRequest
{
    /**
     * 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|between:3,25|regex:/^[A-Za-z0-9-_]+$/|unique:users,name,' . Auth::id(),
             'email' => 'required|email',
             'introduction' => 'max:80',
         ];
    }
}

更新用户信息

未编写处理表单提交的方法,接下来我们先创建此方法:

app/Http/Controllers/UsersController.php

php 复制代码
use App\Http\Requests\UserRequest;

// 执行编辑
public function update(UserRequest $request, User $user)
{
    $user->update($request->all());
    return redirect()->route('users.show', $user->id)->with('success', '个人资料更新成功!');
}

接下来我们创建 error.blade.php 文件来渲染错误提示:

resources/views/shared/_error.blade.php

less 复制代码
@if (count($errors) > 0)
  <div class="alert alert-danger">
    <div class="mt-2"><b>有错误发生:</b></div>
    <ul class="mt-2 mb-2">
      @foreach ($errors->all() as $error)
        <li><i class="glyphicon glyphicon-remove"></i> {{ $error }}</li>
      @endforeach
    </ul>
  </div>
@endif

我们需要自定义表单的提示信息,修改 UserRequest,新增方法 messages() :

app/Http/Requests/UserRequest.php

dart 复制代码
public function messages()
{
    return [
        'name.unique' => '用户名已被占用,请重新填写',
        'name.regex' => '用户名只支持英文、数字、横杠和下划线。',
        'name.between' => '用户名必须介于 3 - 25 个字符之间。',
        'name.required' => '用户名不能为空。',
    ];
}

来到App/Models/User,添加过滤字段introduction

ini 复制代码
protected $fillable = [
    'name', 'email', 'password', 'introduction',
];

效果

再次测试下编辑资料功能,请在『个人简介』里填入测试内容 ------ php,是世界最好的语言. ,点击保存按钮:

结果:

显示个人资料

在个人中心里显示出来:

resources/views/users/show.blade.php

xml 复制代码
<div class="card-body">
  <h5><strong>个人简介</strong></h5>
  <p>{{ $user->introduction }}</p>
  <hr>
  <h5><strong>注册于</strong></h5>
  <p>{{ $user->created_at->diffForHumans() }}</p>
</div>

效果:

上传头像

模型文件修改

首先我们需在 User 模型里将 avatar 字段加入到允许修改的白名单 $fillable 中:

ini 复制代码
protected $fillable = [
    'name', 'email', 'password', 'introduction', 'avatar',
];

编辑页面

接下来我们在 的『个人简介』编辑框下面,增加头像上传的选项:

resources/views/users/edit.blade.php

ini 复制代码
<form action="{{ route('users.update', $user->id) }}" method="POST" accept-charset="UTF-8"
      enctype="multipart/form-data">

存储用户上传图片

接下来是对图片进行存储,本项目中,我们不止上传头像需要用到『图片上传功能』,在后面发布帖子功能中,我们也将会允许用户上传图片,所以此处我们需要预先设计一下图片上传相关的逻辑,我们可以将『图片上传』核心操作做成一个工具类:

app/Handlers/ImageUploadHandler.php

php 复制代码
<?php
namespace App\Handlers;
use Illuminate\Support\Str;
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' => "/$folder_name/$filename"
        ];
    }
}

还可以通过前端对上传的文件后缀进行控制

python 复制代码
<input type="file" name="avatar" class="form-control-file" accept="image/png,image/gif,image/jpeg,image/jpeg">

我们将使用 app/Handlers 文件夹来存放本项目的工具类,『工具类(utility class)』是指一些跟业务逻辑相关性不强的类, Handlers 意为 处理器 ,ImageUploadHandler 意为图片上传处理器,简单易懂。

接下来我们需要在 UsersController 里调用(注意顶部 use 引入):

app/Http/Controllers/UsersController.php

php 复制代码
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use App\Http\Requests\UserRequest;
use App\Handlers\ImageUploadHandler;

class UsersController extends Controller
{
  

    // 执行编辑 UserRequest为表单验证
    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', '个人资料更新成功');
    }
}

效果

提交成功,打开项目文件夹,一步步寻找下去,找到我们刚刚上传的图片:

显示头像

目前我们有两个地方用到用户头像,第一个是个人空间,第二个是顶部导航栏。

修改个人空间,将头像的 src 属性修改为 {{ $user->avatar }} :

resources/views/users/show.blade.php

ini 复制代码
<div class="card ">
  <img class="card-img-top" src="{{ $user->avatar }}" alt="{{ $user->name }}">

  <div class="card-body">

效果如下:

接下来修改顶部导航:

resources/views/layouts/_header.blade.php

ini 复制代码
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"
   aria-haspopup="true" aria-expanded="false">
  <img
    src="{{ Auth::user()->avatar }}"
    class="img-responsive img-circle" width="30px" height="30px">
  {{ Auth::user()->name }}
</a>

效果:

限制头像分辨率

图片验证

当用户上传分辨率太小的图片时,会影响网站的美观,所以我们需要对图片的分辨率大小加以限制。在 UserRequest 中增加图片验证规则即可:

app/Http/Requests/UserRequest.php

php 复制代码
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;

class UserRequest extends FormRequest
{
    /**
     * 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|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' => '用户名不能为空。',
        ];
    }
}

测试:

上传一个208PX以下的照片

如上图可以看到我们自定义的错误消息提示。

裁剪头像

裁剪图片 我们还有一个地方要优化,用户有时会上传分辨率较大的图片,并且图片太大会拖慢页面的加载速度,所以接下来我们将对此进行优化。

我们将使用备受欢迎的 intervention/image 扩展包来处理图片裁切的逻辑,接下来我们需要先安装此扩展包;

安装扩展包

arduino 复制代码
composer require intervention/image

配置信息

执行以下命令获取配置信息:

ini 复制代码
php artisan vendor:publish --provider="Intervention\Image\ImageServiceProviderLaravelRecent"

结果如下:

打开 config/image.php 文件可以看到只有一个驱动器的选项,支持的值有 GD库 和 imagick:

注:此处我们使用默认的 gd 即可,如果将要开发的项目需要较专业的图片,请考虑 ImageMagic

开始裁剪

我们将裁切的逻辑写在 ImageUploadHandler 中,请将以下代码替换:

app/Handlers/ImageUploadHandler.php

php 复制代码
<?php
namespace App\Handlers;
use Illuminate\Support\Str;
use Intervention\Image\Facades\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' => "/$folder_name/$filename"
//            '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();
    }
}

以上的 save() 方法中,我们新增了 $max_width 参数,用来指定最大图片宽度,我们修改 UsersController 的update() 方法中的调用,修改为:

ini 复制代码
$result = $uploader->save($request->avatar, 'avatars', $user->id, 416);

开始测试 进入资料编辑页面 ,选择一张较大的图片,然后点击保存提交表单:

可以看到更新成功的提示:

至此图片上传功能开发完毕。

相关推荐
追逐时光者20 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_20 小时前
敏捷开发流程-精简版
前端·后端
苏打水com21 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧1 天前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧1 天前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧1 天前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧1 天前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧1 天前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng1 天前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印6011 天前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring