Laravel常用代码合集

用Laravel也有不短的时间了,也用过不少版本了,以下代码是在日常项目中收集,作为笔记,也分享出来,希望对你有点用处。
注:版本没标注,若有不兼容的问题,微调即可。

验证

不太习惯单独弄个Request验证类,比较习惯下面的写法:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

    $inputData = $request->only(['name', 'address', 'mobile', 'draw_id']);
    $messages = [
        'required'=>':attribute为必填项',
         'int'=>':attribute参数类型错误',
         'max'=>':attribute长度不得超过 :size',
    ];
    $validator = Validator::make($inputData, [
        'draw_id' => 'required|int',
        'name' => 'required',
        'mobile' => 'required',
        'address' => 'required',
    ], $messages,[
        'name'=>'收货人姓名',
        'mobile'=>'手机号码',
        'address'=>'收货地址',
    ]);

    if ($validator->fails()) {
        return self::response([], current($validator->errors()->all()), 2);
    }

自定义验证

比如常用的手机号验证:

php artisan make:rule Mobile

然后改一下:


    /**
     * Mobile主要代码
     * 验证是否通过
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        //
        return preg_match('/^1\d{10}$/', $value);
    }

    /**
     * 错误信息
     *
     * @return string
     */
    public function message()
    {
        return '手机号格式不正确';
    }

然后这么用起来:

$columns = [
    'college' => 'required|max:32',
    'mobile' => ['required', new Mobile()],
    'qq' => 'required',
];

ORM

关联查询

  • 一对一

    // Model定义,关联外键
    class User extends Model
    {
      ...
    
      public function userIntegral()
      {
          return $this->hasOne('App\Models\UserIntegral', 'user_id', 'id');
      }
    }
    // 使用with查询
    (new User())->with('userIntegral')->orderBy('id', 'desc')->paginate($limit);
  • 一对多

    //Model
    namespace App\Models;
    use Illuminate\Database\Eloquent\Model;
    class Hotel extends Model
    {
      public function orders()
      {
          return $this->hasMany('App\Models\Order');
      }
    }
    //使用,比如查询某个Hotel下status=30的Order
    $hotel = Hotel::with(['orders' => function ($query) {
          $query->where('status', 30);
      }])->find(4);

统一异常处理

这个可以参见之前的文章Laravel 统一错误处理为 JSON

队列

失败队列入库

  • 生成表
    生成failed_jobs表

    php artisan queue:failed-table
    php artisan migrate
  • 单独处理

可以在Job中单独处理失败,Job失败也会写入上面生成的failed_jobs表

/**
* 任务失败的处理过程
*
* @param  Exception  $exception
* [@return](https://learnku.com/users/31554) void
*/
public function failed(Exception $exception)
{
    // 处理
}

重试队列

有时候代码有漏洞可能会有队列执行失败的状况,这时候我们就需要重试。

  • 查看所有失败
    php artisan queue:failed
  • 重试所有失败
    php artisan queue:retry all
  • 重试单个失败
    php artisan queue:retry 13
  • 清空失败(重要的队列数据万不可这么操作)
    php artisan queue:flush

另外,手动去操作确实不太方便,你可以设置个cron,定时重试所有失败,但务必要注意消息提醒,以免队列一直重试一直失败,往复运行,影响了正常的队列性能。

其他常用代码

文件上传OSS

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Controller;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use OSS\OssClient;
use OSS\Core\OssException;

class UploadController extends Controller
{
    public function index(Request $request)
    {
        $file = $request->file('file');
        if ($file->isValid()) {
            $ext = $file->getClientOriginalExtension();
            $realPath = $file->getRealPath();
            $filepath = config('app.env').'/' . md5(uniqid('', true));

            $result = $this->uploadOss($realPath, $filepath.".".$ext);

            if ($result['code']) {
                return response(['code' => 2, 'msg' => $result['msg']]);
            } else {
                return response(['code' => 0, 'msg' => '上传成功', 'data' => [
                    'filepath' => $result['data']['url'],
                    'data' => $request->all()
                ]]);
            }
        }
    }

    /**
     * 上传oss
     * @param $filePath  当前路径
     * @param $object   预定义文件名,可含文件夹
     * [@return](https://learnku.com/users/31554) array
     */
    public function uploadOss($filePath, $object)
    {
        $accessKeyId = config('filesystems.disks')[config('filesystems.default')]['access_key'];
        $accessKeySecret = config('filesystems.disks')[config('filesystems.default')]['secret_key'];
        $endpoint = config('filesystems.disks')[config('filesystems.default')]['endpoint'];
        $bucket= config('filesystems.disks')[config('filesystems.default')]['bucket'];
        $url = config('filesystems.disks')[config('filesystems.default')]['host'];

        try{
            $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
            $ossClient->uploadFile($bucket, $object, $filePath);
            return [
                'code' => 0,
                'data' => [
                    'url' => $url.'/'.$object
                ]
            ];
        } catch(OssException $e) {
            return [
                'code' => 1,
                'msg' => $e->getMessage()
            ];
        }
    }
}


// -------
// 配置
'oss' => [
  'driver' => 'oss',
  'root' => '',
  'access_key' => env('OSS_ACCESS_KEY'),
  'secret_key' => env('OSS_SECRET_KEY'),
  'endpoint' => env('OSS_ENDPOINT'), // 使用 ssl 这里设置如: https://oss-cn-beijing.aliyuncs.com
  'bucket' => env('OSS_BUCKET'),
  'isCName' => env('OSS_IS_CNAME', false), // 如果 isCname 为 false,endpoint 应配置 oss 提供的域名如:`oss-cn-beijing.aliyuncs.com`,否则为自定义域名,,cname 或 cdn 请自行到阿里 oss 后台配置并绑定 bucket
  'host' => env('OSS_HOST', '')
],

json输出

protected static $code = 0;
protected static $msg = 'ok';

public function response($data = [], $msg = '', $code = 0)
{
    if (is_null($data)) {
        $data = new \stdClass();
    }
    return response()->json([
        'code' => $code? $code : self::$code,
        'msg' => $msg? $msg : self::$msg,
        'data' => $data,
    ], 200);
}

进程锁

  • 普通版本

    // $autoDel字段删除,$ttl 过期时间,秒
    public function  processLock($key, $autoDel = true, $ttl = 60)
    {
      $key = 'processLock:'.$key;
      // 不同版本或redis扩展,会有略微不同,自行调整下代码即可
      if (Redis::Command('set', [$key, 1, 'EX', $ttl, 'NX'])) {
          if ($autoDel) {
              register_shutdown_function(function () use ($key) {
                  Redis::del($key);
              });
          }
    
          return true;
      }
      return false;
    }
  • lua版本

    public function getScript()
    {
        return <<<LUA
        local ret = redis.call("setnx", KEYS[1], ARGV[1])
        if ret == 1 then
            return redis.call("expire", KEYS[1], ARGV[2])
        else
            return 0
        end
LUA;
    }


    public function processLock($key, $autoDel = true, $ttl = 60)
    {
        if (Redis::eval($this->getScript(), 1, $key, 1, $ttl)) {
            if ($autoDel) {
                register_shutdown_function(function () use ($key) {
                    Redis::del($key);
                });
            }

            return true;
        }
        return false;
    }

说明:Redis::eval行第一个1表示key的数量,是为了区分KEYS和ARGV。

JWT

Laravel 配合 jwt 使用

系统通知到钉钉

我们可以使用队列,把一些重要的通知投到钉钉,主要代码如下:

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use GuzzleHttp\Client;


class SystemNotify implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $title;

    private $content;

    private $type;

    private $robot;

    const DD_URL = 'https://oapi.dingtalk.com';

    /**
     * Create a new job instance.
     *
     * @param $title
     * @param string $content
     * @param string $type text, markdown
     * @param int $robot
     */
    public function __construct($title, $content = '', $type = 'markdown', $robot = 1)
    {
        // 单独使用SystemNotify队列
        $this->queue = 'SystemNotify';
        $this->title = $title;
        $this->content = $content;
        $this->type = $type;
        $this->robot = $robot;

    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // 可以不使用关键字,建议钉钉机器人使用IP段设置,更为安全
        switch ($this->type){
            case 'markdown':
                $params = [
                    'msgtype' => $this->type,
                    $this->type => [
                        'title' => $this->title.'[关键字]',
                        'text' => $this->content
                    ]
                ];
                break;
            default:
                $params = [
                    'msgtype' => $this->type,
                    $this->type => [
                        'content' => $this->content.'[关键字]',
                    ]
                ];
                break;

        }
        $params = json_encode($params, JSON_UNESCAPED_UNICODE);


        $uri = self::URL_MAPPING[$this->robot];
        $this->getClient()->request('POST', $uri, [
            'headers' => [
                'Content-Type' => 'application/json;charset=utf-8'
            ],
            'body' => $params
        ]);
    }

    // 对应不同的钉钉群通知,修改access_token参数即可
    const URL_MAPPING = [
        1 => '/robot/send?access_token=@1',
        2 => '/robot/send?access_token=@2'
    ];

    public function getClient()
    {
        return new Client([
            'base_uri' => 'https://oapi.dingtalk.com',
            'timeout'  => 30,
            'verify' => false
        ]);
    }
}

说明:通知内容可以自定义,添加智能机器人操作比较简单就不赘述了

后台操作日志

利用 Laravel 中间件给后台加个操作日志

Excel

Laravel6 配合 Maatwebsite\Excel 实现 Excel 导入

Laravel6 配合 Maatwebsite\Excel 实现 Excel 导出

陆续补充中…

本作品采用《CC 协议》,转载必须注明作者和本文链接
分享开发知识,欢迎交流。公众号:开源到
本帖由系统于 3年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4
北冥

我验证一般喜欢这样:

$this->validate($request, [
        'supplier' => 'required',
        'prt_id' => 'required',
        'number' => 'required|numeric|integer|min:0',
        'dpt_id' => 'required',
    ],[
        'prt_id.required' => trans('messages.select_product'),
    ],[
        'supplier' => trans('labels.supplier'),
        'number' => trans('labels.number'),
        'dpt_id' => trans('labels.application_department'),
    ]
);
3年前 评论
JerryBool 3年前
已下线 (楼主) 3年前
sunny123456 3年前

进程锁,那部分 用原子锁不香吗

3年前 评论
已下线 (楼主) 3年前

大佬我问个问题:一对多哪里

$category = Category::with(['goods' => function ($query) {
      $query->where('is_sale', true);
  }])->find($categoryId);

goods是引入模型我知道 Category 要怎么引入啊

3年前 评论
已下线 (楼主) 3年前
it_cwc (作者) 3年前
已下线 (楼主) 3年前
xuri

推荐一个功能丰富、兼容性好、高性能的 Excel 文档基础库:github.com/xuri/excelize

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!