最近写Laravel,发现很多工具用起来的时候都得现查,现在在这备份一下省得之后还得查。

Eloquent ORM数据库迁移模型

利用php脚本建立数据表

  1. 创建迁移模型

    $ php artisan make:migration create_表名
  2. 将主键设置为UUID

    $table->uuid('id');
  3. 完整迁移脚本样例

    <?php
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    class CreateUsersTable extends Migration
    {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->uuid('id');
            $table->string('user_name');
            $table->string('user_email');
            $table->string('user_password');
            $table->string('user_type');
            $table->string('user_status');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
    }
  4. 启动迁移

    $ php artisan migrate
  5. 数据库创建后的结构(以上面的user脚本为例)

    字段名 类型 长度 约束 备注
    id varchar 36 非空 36位长uuid
    user_name varchar 255 非空 用户名
    user_email varchar 255 非空 用户邮箱
    user_password varchar 255 非空 用户密码(使用随机盐哈希加密)
    user_type varchar 255 非空 用户类型
    user_status varchar 255 非空 用户状态
    created_at timestamp 0 创建时间(由laravel自动生成并管理)
    updated_at timestamp 0 修改时间(由laravel自动生成并管理)
  6. 数据填充seeder
    a. 创建seeder类

    $ php artisan make:seeder

    b. 将数据脚本写入run

    public function run()
    {
        DB::beginTransaction();
        try {
            $user = new User();
            ...
            $user->save();
    
            $user_profile = new UserProfile();
            ...
            $user_profile->save();
        }catch (\Exception $e){
            DB::rollBack();
            echo $e;
        }
        DB::commit();
    }

    c. 将seeder加入DatabaseSeeder.php

    public function run()
    {
        $this->call([
            AddAdminUserSeeder::class,
        ]);
    }

    d. 执行迁移

    $ php artisan db:seed

数据库与持久层

  1. 数据库事务

    ...
    //需要引入DB门面
    use Illuminate\Support\Facades\DB;
    ...
    class xxx extend Controller{
    public function xxx(Request $request){
        //事务的开启
         DB::beginTransaction();
         ...
         //事务的提交
         DB::commit();
         ...
         //事务的回滚
         DB::rollBack();
    }
    }
  2. 数据对象ORM
    a. 通过脚手架创建数据对象(Model)

    $ php artisan make:model ModelName 

    b. 数据对象结构

    <?php
    namespace App\Models;
    use Illuminate\Contracts\Auth\MustVerifyEmail;
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;
    use Laravel\Sanctum\HasApiTokens;
    class User extends Authenticatable
    {
    use HasApiTokens, HasFactory, Notifiable;
    
    //批量赋值时的白名单
    protected $fillable = [
        'user_name',
        'user_email',
        'user_password',
    ];
    
    //查询返回的json中不出现的字段
    protected $hidden = [
        'user_password',
    ];
    //表名
    protected $table="users";
    //是否自增(当主键为uuid时为否)
    public $incrementing = false;
    //主键类型(当主键为uuid时为string)
    protected $keyType = 'string';
    //自动管理时间戳(如果使用unix时间戳或不添加created_at与updated_at字段为false)
    public $timestamps = false;
    }
  3. 数据对象查询(以User.php为例)

    //通过持久层拼接Where语句,返回为数据对象数组
    $user = User::query()
                ->where("user_name", $username)
                ->orWhere("email", $username)
                ->get();
    //假定只有一条数据,获取其用户名,多条foreach循环即可
    $name = $user[0]->user_name;
  4. 新增或编辑数据对象(以User.php为例)

    //事务开启
    DB::beginTransaction();
    //新增为new对象,编辑则直接修改从数据库中返回的对象即可
    $user = new User();
    //uuid使用见下节
    $user->id = Uuid::generate();
    //随机盐哈希加密见下节
    $user->user_password = password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
    //...其他省略
    //异常捕获
    try {
    $user->save();
    } catch (\Exception $e) {
    //如果异常则回滚
    DB::rollBack();
    throw $e;
    }
    DB::commit();

随机盐的哈希加密

  1. 获取加密后的密码
    $password_hash = password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
  2. 验证原密码与加密后的密码是否匹配,返回布尔
    $verify=password_verify('password_string', $password_hash)

UUID插件

  1. 安装Uuid拓展
    $ composer require webpatser/laravel-uuid
  2. 添加app别名注册(config/app.php)
    'aliases' => [
    ...
    'Uuid' => Webpatser\Uuid\Uuid::class,
    ],
  3. uuid使用
    //引入
    use Webpatser\Uuid\Uuid;
    //生成
    $uuid=Uuid::generate();

Redis插件安装与使用

  1. Redis安装
    Laravel自带Redis引擎,但是用不惯,我习惯切换成predis

    $ composer require predis/predis
  2. Redis配置
    修改redis配置文件,在config/database.php文件中

    'redis' => [
        //引擎配置,默认为phpredis,可以改为predis
        'client' => env('REDIS_CLIENT', 'phpredis'),
        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            //此处为在redis中key的前缀,为.env配置文件中的name,可自行修改
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],
        //默认配置,无需修改
        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],
        //自定义设置,在.env中配置即可
        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],
    ],
  3. Redis使用
    引入门面

    use Illuminate\Support\Facades\Redis;

    代码中使用

    //设置字符串值
    Redis::set("key","value")
    //设置带有过期时间的值(单位为秒)
    Redis::setex("key",$expiredTime,"value");
    //获取字符串值
    Redis::get("key")
    //读取后通过json格式解码(用于value是php array格式的情况)
    $value=json_decode(Redis::get("key"),true);

缓存

  1. 缓存Cache默认使用文件驱动,可改为Redis驱动数据库驱动或其他,我一般用文件驱动来代替当Redis不存在的情况
    //添加缓存,过期时间可为空,为过期时间点,并非时长
    Cache::put("key" , "value" ,$expiredAt);
    //获取缓存
    Cache::get("key");

mews/captcha验证码

  1. 安装
    a. 通过composer安装

    $ composer require mews/captcha

    b. 配置config/app.php

    'providers' => [
    //新增
    Mews\Captcha\CaptchaServiceProvider::class,
    ],
    'aliases' => [
    //新增
    'Captcha' => Mews\Captcha\Facades\Captcha::class,
    ],

    c. 通过脚手架生成配置文件(config/captcha.php)

    $ php artisan vendor:publish
  2. 使用(接口方式)
    由于原方法使用的是hash加解方法,也就是说可以通过同一个hash串重复请求,所以需要在本地缓存。(这里我重写了验证方法,因为我下完包之后死活也验证不了hash正确性,一直都是false)
    a. 创建自定义工具类:CustomCaptcha.php

    <?php
    namespace App\Http\Utils;
    use Mews\Captcha\Captcha;
    class CustomCaptcha extends \Mews\Captcha\Captcha
    {
    /**
     * @param array $code
     */
     //构造方法,传入建好的验证码对象
        public function __construct(Captcha $captcha)
        {
            parent::__construct($captcha->files, $captcha->config, $captcha->imageManager, $captcha->session, $captcha->hasher, $captcha->str);
            parent::create('flat', true);
        }
    
        //获取验证码的真实值
        public function getCode()
        {
            return implode($this->text);
        }
    
        //获取BASE64格式图片
        public function getImg()
        {
            return $this->image->encode('data-url')->encoded;
        }
    }

    b. 在控制器中使用:

    //获取验证码的方法(接口控制器)
    function getCaptcha(Request $request, Captcha $captchaBuilder)
    {
        //获取laravel的session token,这里的思想是通过缓存token与验证码值来验证以避免重复提交同一hash问题
        $key = $request->cookie(app()->getNamespace() . "session");
        //创建自定义验证码对象,需要将构建器传入
        $captcha = new CustomCaptcha($captchaBuilder);
        //设置过期时间。我设置了两分钟
        $expiredAt = now()->addMinute(2);
        //将验证码值,session token放入缓存并设置过期时间
        Cache::put($key, ['captcha' => $captcha->getCode()], $expiredAt);
        //构建返回数组,包括有效期截止时间和BASE64格式图片
        $result = [
            'expired_at' => $expiredAt->toDateTimeString(),
            'captcha_img' => $captcha->getImg()
        ];
        //返回201 Created
        return response()->json($result)->setStatusCode(201);
    }
    
    //验证用户提交的验证码,返回值bool(私有方法)
    private function check_captcha($captcha,$session):bool{
        //通过传入的session获取缓存中的验证码对象,不存在则返回验证失败
        $captchaData = Cache::get($session);
        Cache::forget($session);
        if ($captchaData == null) {
            return false;
        }
        //判断传入的验证码与缓存是否相等
        if ($captcha == $captchaData['captcha']){
            return true;
        }else{
            return false;
        }
    }
    
    //具体使用验证码的方法,接口控制器
    function check_captcha(Request $request)
    {
        //通过request获取到session token后通过上面的方法验证是否正确
        if (!$this->check_captcha($request->get("captcha"), $request->cookie(app()->getNamespace() . "session"))) {
            //这里的R类是我自定义的返回类
            return R::error(HTTP_CODE::UNAUTHORIZED_CAPTCHA);
        }
        return R::ok();
    }

自定义返回类

  1. 用于返回json格式接口R.php
    <?php
    namespace App\Http\Controllers;
    use Illuminate\Foundation\Bus\DispatchesJobs;
    use Illuminate\Routing\Controller as BaseController;
    use Illuminate\Foundation\Validation\ValidatesRequests;
    use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
    class R extends BaseController {
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
    //返回成功的方法
    static public function ok($data = []) {
        return response()->json([
            'status' => true,
            //这里只支持200,201等其他代码可以用error方法
            'code' => 200,
            //这里用了翻译文件来返回,如果不需要翻译可以直接加一个参数赋在这里
            'message' => trans('http_codes.code')[200],
            'data' => $data,
        ]);
    }
    //返回错误的方法
    static public function error($code, $data = []) {
        return response()->json([
            'status' => false,
            'code' => $code,
            'message' => trans('http_codes.code')[(int)$code],
            'data' => $data,
        ]);
    }
    }

路由

  1. 路由位置
    页面路由在routes/web.php中,访问路径为host/xxx
    接口路由在routes/api.php中,接口路由默认路径为host/api/xxx

  2. 添加路由

    //路由单例,/index为地址,view('index')为返回index.blade.php模板生成的页面
    //访问路径为域名host/index
    Route::get("/index",function (){
    return view('index');
    });
    //路由群组。/test为群组访问uri
    //三个链接分别是 host/test/1,host/test/2,host/test/3
    Route::group(['prefix'=>'/test'],function (){
    Route::get("/1",function (){
        return view('index');
    });
    Route::get("/2",function (){
        return view('login');
    });
    Route::get("/3",function (){
        return view('register');
    });
    });
    //中间件,在参数中添加middleware,指向为添加在中间件配置中的别名,见下节
    Route::group(['prefix'=>'/','middleware'=>MiddleWares::Alias],function (){
    Route::get("/test",function (){
        return view('test');
    });
    });
    //接口控制器
    //当路由为接口时,可以返回控制器
    //为一个数组,前面为控制器文件,后面为执行的控制器方法
    //该接口链接为host/api/user/login
    Route::post('/user/login', [LoginController::class,"login"]);

中间件Middleware

可用于登录验证,非法请求拦截,xss filter等

  1. 利用脚手架生成中间件

    $ php artisan make:middleware TestMiddleware
  2. 注册中间件别名
    将中间件别名加入config/app.php

    ...
    'aliases' => [
    ...
    "xxxMiddleware"=>XxxMiddleware::class,
    ];
  3. 中间件实例

    <?php
    namespace App\Http\Middleware\System;
    ...引入包
    class AuthMiddleware
    {
    //中间件句柄
    public function handle(Request $request, Closure $next) {
        ...中间件处理方法
        //直接跳转至某页面
        if(xxx){
            return redirect('/login');
        }
        //将数据通过header向下传递
        $request->attributes->add(["key":"vaule"]);
        //向下继续执行
        return $next($request);
    }
    }

控制器Controller

  1. 通过脚手架生成控制器

    $ php artisan make:controller TestController
  2. 控制器实例

    <?php
    namespace App\Http\Controllers\User;
    ...引入包
    class LoginController extends Controller {
    //控制器方法,可在路由中调用
    function login(Request $request) {
       ...业务代码
        return R::ok();
    }
    
    function logout(Request $request) {
         ...业务代码
        return R::error(xxx);
    }
    }

cookie

laravel的cookie都是以jwt形式加密的,使用前需要将之cookie加密中间件添加进内核,否则cookie无法生效

//app/Http/Kernel.php
...
protected $middlewareGroups = [
...
    "web"=>[
        ...
        \App\Http\Middleware\EncryptCookies::class,
    ]
    ...
    "api"=>[
        ...
        \App\Http\Middleware\EncryptCookies::class,
    ]
    ...
],
...

卸载自带认证器

$ composer remove laravel/sanctum
$ composer remove laravel/passport

自动代码补全

  1. 安装Laravel Plugin插件
  2. 安装IDE助手
    # 补全工具
    $ composer require --dev barryvdh/laravel-ide-helper
    # 模型注释工具
    $ composer require doctrine/dbal
    # 复制配置文件,进入config/ide-helper.php中将include_fluent改为true来支持自动链式操作
    $ php artisan vendor:publish --provider="Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider" --tag=config
    # 清除bootstrap/compiled.php
    $ php artisan clear-compiled
    # Facades 注释
    $ php artisan ide-helper:generate
    # 数据模型注释
    $ php artisan ide-helper:models
    # 工厂模式
    $ php artisan ide-helper:meta

本篇内容为原创内容,采用CC BY-NC-SA 4.0协议许可
2021-08-31 16:47
UtopiaXC
于北京


尽管如此,世界依旧美丽