UtopiaXC Blog

Laravel常用开发功能记录

技术笔记 ·

最近写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
于北京

# 笔记, Laravel