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

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

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

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

相关推荐
白总Server37 分钟前
MongoDB解说
开发语言·数据库·后端·mongodb·golang·rust·php
计算机学姐1 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
程序员-珍2 小时前
SpringBoot v2.6.13 整合 swagger
java·spring boot·后端
海里真的有鱼2 小时前
好文推荐-架构
后端
骆晨学长2 小时前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
AskHarries2 小时前
利用反射实现动态代理
java·后端·reflect
Flying_Fish_roe3 小时前
Spring Boot-Session管理问题
java·spring boot·后端
hai405874 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
Adolf_19935 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥5 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap