Laravel 搭建商城之学习笔记(第三章)

前端用户认证指令:guest

# @auth 和 @guest 指令用来快速认证当前用户:
@auth
    // 通过认证
@endauth

@guest
    // 未通过认证
@endguest

必要的话,可以在 @auth 和 @guest 指令中指定 认证看守器(Guard):

@auth('admin')
    // 通过认证
@endauth

@guest('admin')
    // 未通过认证
@endguest

数据库字段类型转换:casts

$casts 属性提供了一个便利的方法来将数据库字段值转换为常见的数据类型,$casts 属性应是一个数组,且数组的键是那些需要被转换的字段名,值则是你希望转换的数据类型。

支持转换的数据类型有: integer,real,float,double,string,boolean,object,array,collection,date,datetime 和 timestamp。

App\Models\Test.php

protected $fillable = [
        'field'
    ];

    protected $casts = [
        'field' => 'integer',
    ];

中间件的简单应用

如果我们想在进入某个路由的时候做一些验证,那么就可以使用中间件

  • 创建中间件
php artisan make:middleware CheckAge
  • 注册中间件
    假设你想为指定的路由分配中间件,首先应该在 app/Http/Kernel.php 文件内为该中间件分配一个键 。默认情况下, Kernel 类的 $routeMiddleware 属性下包含了Laravel内置的中间件。若要加入自定义的中间件,只需把它附加到列表后并为其分配一个自定义键 即可。例如:
// 在 App\Http\Kernel 类中
protected $routeMiddleware = [
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    'check_age' => \App\Http\Middleware\CheckAge::class,
];
  • 使用中间件
    一旦在 Kernel 类中定义好了中间件,就可以通过 middleware 方法将为路由分配中间件(更为详细的使用方法请查看官方文档):
route::get('admin/profile', function () {
    //
})->middleware('check_age');

消息通知

使用情景:比如邮箱验证,在用户注册后给用户发送验证邮件

除了 发送邮件 Laravel 还支持通过多种频道发送通知,包括邮件、短信 (通过 Nexmo), 以及 Slack。通知还能存到数据库,这样就能在网页界面上显示了。

  • 创建消息通知
php artisan make:notification EmailVerificationNotification
  • 配置:消息通知对于 email 频道为开箱即用,以下为 email 简单配置(以下为用户邮箱验证情景,且需要引入相应类,比如 Str)
    app\Notifications\EmailVerificationNotification.php
<?php
    public function toMail($notifiable)
    {
        // 使用 Laravel 内置的 Str 类生成随机字符串的函数,参数就是要生成的字符串长度
        $token = Str::random(16);
        // 往缓存中写入这个随机字符串,有效时间为 30 分钟。
        Cache::set('email_verification_' . $notifiable->email, $token, 30);
        $url = route('email_verification.verify', ['email' => $notifiable->email, 'token' => $token]);
        return (new MailMessage)
                    ->greeting($notifiable->name . '您好:')
                    ->subject('注册成功,请验证您的邮箱')
                    ->line('请点击下方链接验证您的邮箱')
                    ->action('验证', $url);
    }
  • 使用:配置完成后,需要在控制器中引入使用
<?php
namespace App\Http\Controllers;
use Exception;
use App\Models\User;
use Cache;
use Illuminate\Http\Request;
class EmailVerificationController extends Controller
{
    public function verify(Request $request)
    {
        // 从 url 中获取 `email` 和 `token` 两个参数
        $email = $request->input('email');
        $token = $request->input('token');
        // 如果有一个为空说明不是一个合法的验证链接,直接抛出异常。
        if (!$email || !$token) {
            throw new Exception('验证链接不正确');
        }
        // 从缓存中读取数据,我们把从 url 中获取的 `token` 与缓存中的值做对比
        // 如果缓存不存在或者返回的值与 url 中的 `token` 不一致就抛出异常。
        if ($token != Cache::get('email_verification_'.$email)) {
            throw new Exception('验证链接不正确或已过期');
        }

        // 根据邮箱从数据库中获取对应的用户
        // 通常来说能通过 token 校验的情况下不可能出现用户不存在
        // 但是为了代码的健壮性我们还是需要做这个判断
        if (!$user = User::where('email', $email)->first()) {
            throw new Exception('用户不存在');
        }
        // 将指定的 key 从缓存中删除,由于已经完成了验证,这个缓存就没有必要继续保留。
        Cache::forget('email_verification_'.$email);
        // 最关键的,要把对应用户的 `email_verified` 字段改为 `true`。
        $user->update(['email_verified' => true]);

        // 最后告知用户邮箱验证成功。
        return view('pages.success', ['msg' => '邮箱验证成功']);
    }
}
  • 邮件配置:.env
MAIL_DRIVER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_FROM_ADDRESS=null // 和 MAIL_USERNAME 相同配置,忽略可能会报错
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null

提示:MailHog 是 Homestead 自带的一个组件,可以很方便地调试发送邮件。Laragon 中也有邮箱测试组件,位置在:菜单》PHP》邮件接收|邮件发送

注册事件和监听器:注册时发送激活邮件

使用情景:需要特定事件触发的任务,比如注册用户时发送邮箱验证。

  • 创建事件的监听器
php artisan make:listener RegisteredListener
  • 配置监听器
    app\Listeners\RegisteredListener.php
<?php
namespace App\Listeners;
use App\Notifications\EmailVerificationNotification; // 需要触发的事件
use Illuminate\Auth\Events\Registered;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
// implements ShouldQueue 让这个监听器异步执行
class RegisteredListener implements ShouldQueue
{
    // 当事件被触发时,对应该事件的监听器的 handle() 方法就会被调用
    public function handle(Registered $event)
    {
        // 获取到刚刚注册的用户
        $user = $event->user;
        // 调用 notify 发送通知
        $user->notify(new EmailVerificationNotification());
    }
}
  • 将注册事件和监听器关联
    App/Providers/EventServiceProvider.php
use App\Listeners\RegisteredListener;
use Illuminate\Auth\Events\Registered;
.
.
.
    protected $listen = [
        Registered::class => [ // 输出 Illuminate\Auth\Events\Registered
            RegisteredListener::class,
        ],
    ];

异常处理

未经过处理的网站异常页面对用户并不友好,错误信息也不准确,因此我们需要优雅的处理一下网站异常,使之能够友好并且明确的展示出异常信息

  • 创建异常处理类
php artisan make:exception InvalidRequestException
  • 配置异常处理
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Http\Request;
class InvalidRequestException extends Exception
{
    public function __construct(string $message = "", int $code = 400)
    {
        parent::__construct($message, $code);
    }

    public function render(Request $request)
    {
        if ($request->expectsJson()) {
            // json() 方法第二个参数就是 Http 返回码
            return response()->json(['msg' => $this->message], $this->code);
        }

        return view('pages.error', ['msg' => $this->message]);
    }
}
// 一下为 pages/error.blade.php 内容
@extends('layouts.app')
@section('title', '错误')
@section('content')
<div class="panel panel-default">
    <div class="panel-heading">错误</div>
    <div class="panel-body text-center">
        <h1>{{ $msg }}</h1>
        <a class="btn btn-primary" href="{{ route('root') }}">返回首页</a>
    </div>
</div>
@endsection
  • 简单使用
use App\Exceptions\InvalidRequestException;
.
.
.
    public function test()
    {
        throw new InvalidRequestException('错误信息');
    }

表单验证

  • 创建一个 Request 基类
php artisan make:request Request
  • 配置
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class Request extends FormRequest
{
    // 判断用户是否有权限进行此请求
    public function authorize()
    {
        return true;
    }
}
  • 创建 指定表单验证 类并继承 Request 基类
php artisan make:request TestRequest
  • 配置验证规则
    app/Http/Requests/TestRequest.php
<?php
namespace App\Http\Requests;
class TestRequest extends Request
{
    public function rules()
    {
        return [
            'field1' => 'required',
            'field2' => 'required',
            'field3' => 'required',
        ];
    }
}
  • 应用
    app/Http/Controllers/TestController.php
use App\Http\Requests\TestRequest;
.
.
.
    public function store(TestRequest $request)
    {
        $request->user()->addresses()->create($request->only([
            'field1',
            'field2',
            'field3',
        ]));

        return redirect()->route('test.index');
    }
.
.
.
  • 快速验证,不需要生成和调用 TestRequest
    App\Http\Controllers\Test.php
/**
 * 保存一篇新的博客文章。
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // 文章内容是符合规则的,存入数据库...
}

中文语言包

  • 安装中文语言包
composer require overtrue/laravel-lang
  • 配置
    config/app.php
'locale' => 'zh-CN',
'fallback_locale' => 'zh-CN',
  • 指定表单验证字段信息
    app/Http/Requests/TestRequest.php
.
.
.
public function attributes()
{
    return [
    'field1' => '字段1',
    'field2' => '字段2',
    'field3' => '字段3',
    ];
}

检查用户修改权限:授权策略类

比如用来限制用户只能个修改自己创建的文章

  • 创建授权策略类
php artisan make:policy TestPolicy
  • 配置
    app/Policies/TestPolicy.php
use App\Models\Test;
.
.
.
    public function own(User $user, Test $test)
    {
        return $test->user_id == $user->id;
    }
  • 注册这个授权策略
    app/Providers/AuthServiceProvider.php
use App\Models\Test;
use App\Policies\TestPolicy;
.
.
.
    protected $policies = [
        Test::class => TestPolicy::class,
    ];
.
.
.
  • 应用
    app/Http/Controllers/TestController.php
.
.
.
    public function edit(Test $test)
    {
        $this->authorize('own', $test);

        return view('test.create_and_edit', ['test' => $test]);
    }

606 total views, 2 views today

Revisions

There are no revisions for this post.

No comments yet.

发表评论