tp8 验证之unique验证字段值唯一性(详解)

  • 原创
  • 作者:程序员三丰
  • 发布时间:2024-03-27 23:49
  • 浏览量:1081
本文主要介绍在tp8中如何使用unique验证规则,在写入数据时进行单个字段或者多个字段的唯一性验证。规则本身很简单易用,只是用法间隔一段时间就会遗忘,导致经常使用不当都不能生效,都要耗费时间阅读源码,所以本文分析整理以便快速查阅。

本文实例代码是基于当下最新的tp版本(8.0.3),应该也适用于tp5 和 tp6,如有需要自行按照本文思路进行实践验证即可。
ThinkPHP 官网地址:https://www.thinkphp.cn/
ThinkPHP8 官方手册:https://doc.thinkphp.cn/v8_0/preface.html
ThinkPHP6 官方手册:https://doc.thinkphp.cn/v6_1/default.html
ThinkPHP5.1 官方手册:https://doc.thinkphp.cn/v5_1/default.html
ThinkPHP5.0 官方手册:https://doc.thinkphp.cn/v5_0/default.html

下图是截取自tp8官网手册的介绍:
图片.png
本文只介绍如下规则:

  • ‘name’ => ‘unique:user’
  • ‘name’ => ‘unique:user,account’
  • ‘name’ => ‘unique:user,status^account’

上面截图中的其他规则目前还没有应用场景,暂不做分享。

语法解析

‘field’ => ‘unique:tableName,field,except,pk’

  • => 左侧
    • field:指的是待验证的数据数组(一般是请求的数据)的键名,不一定是数据表中存在字段名,但一定是对应数据表中的字段名。一般=>左右两侧的field同名。
  • => 右侧
    • unique:指定为unique验证规则
    • tableName: 表名(不含前缀)]
    • field:[数据库字段名]^[组合验证数据库字段名],如果不设置此处的field则验证数据表中与=>左侧field同名的字段,如果设置了则验证设置的字段,也就是说此处的field是数据表中必须存在的字段,示例:
      • ‘username’ => ‘unique:users’ 验证数据表中username是否唯一(验证数据字段名与数据库表中待验证表字段名一致)
      • ‘username’ => ‘unique:users,account’ 验证数据表中account是否唯一(验证数据字段名与数据库表中待验证表字段名不一致)
      • ‘username’ => ‘unique:users,username^login_status’ 验证数据表中account与login_status组合(等同于复合唯一索引)是否唯一(组合验证场景下,必须指定要验证的数据库表字段名)

        注意:unique:tableName,field,except,pk 之间不能存在任何空格,尤其是逗号(,)后面一定不能有空格!!!

数据准备

以数据迁移 migrate 的方式创建数据表,以及通过 seed插入测试数据。具体操作步骤本站另外一篇文章《tp8 数据迁移migrate和seed插入测试数据的完整示例》已做详细介绍,本文不做赘述。

创建验证器 & 设置验证规则

在 tp 项目根目录下,运行php think make:model Users便可成功创建 Users 验证器。打开验证器文件并修改内容如下:

<?php
declare (strict_types=1);

namespace app\validate;

use think\Validate;

class Users extends Validate
{
  protected $failException = true;

  /**
     * 定义验证规则
     * 格式:'字段名' =>  ['规则1','规则2'...]
     *
     * @var array
     */
  protected $rule = [
      // 验证规则一:下面这个规则约束users表中username必须唯一
      // 'username' => 'unique:users,username'

      // 验证规则二:下面这个规则约束users表中username与login_status组合唯一,
      // 即:如果表中存在username=James且login_status=1,
      //         那么[username=James, login_status=1]则验证通不过
      //         而[username=James, login_status=0]则验证可通过
      'username' => 'unique:users,username^login_status'
  ];

  /**
     * 定义错误信息
     * 格式:'字段名.规则名' =>  '错误信息'
     *
     * @var array
     */
  protected $message = [];
}

注意:验证器中必须设置这个属性 $failException 为 true,否则验证不通过时不会抛出异常,而是直接通过 return 的方式返回验证结果。

通过指令测试验证效果

个人觉得通过创建指令测试无需打开浏览器简单方便,当然你可以通过控制器编写测试代码然后再浏览器执行也可以的。
在 tp 项目根目录下,运行php think make:command Ttys便可成功创建 ttys 指令。打开指令文件并修改内容如下:

<?php
declare (strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\validate\Users as UsersValidate;
use think\exception\ValidateException;

class Ttys extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName('ttys')
            ->setDescription('控制台调试工具命令');
    }

    protected function execute(Input $input, Output $output)
    {
        try {
            // 新增时验证
            validate(UsersValidate::class)
                ->check(['username' => 'test', 'login_status' => '1']);

            // 编辑时验证
            // validate(UsersValidate::class)
            //    ->check(['username' => 'test', 'login_status' => '1', 'id' => 22]);

            $output->writeln('validate pass');
        } catch (ValidateException $e) {
            $output->error($e->getMessage());
        }
    }
}

指定文件编辑保存后,还需要编辑 config/console.php 文件,配置上面自定义的指定 Ttys,具体配置如下:

<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
    // 指令定义
    'commands' => [
        // .... 其他自定义指令
        \app\command\Ttys::class
    ],
];

最后在 tp 项目根目录下,运行php think ttys便可运行自定义的 ttys 指令,查看验证结果。

unique 验证规则的源码

源代码位置:vendor/topthink/framework/src/think/Validate.php,在文件内搜索 unique() 方法


    /**
     * 验证是否唯一
     * @access public
     * @param mixed  $value 字段值
     * @param mixed  $rule  验证规则 格式:数据表,字段名,排除ID,主键名
     * @param array  $data  数据
     * @param string $field 验证字段名
     * @return bool
     */
    public function unique($value, $rule, array $data = [], string $field = ''): bool
    {
        if (is_string($rule)) {
            $rule = explode(',', $rule);
        }

        if (str_contains($rule[0], '\\')) {
            // 指定模型类
            $db = new $rule[0];
        } else {
            $db = $this->db->name($rule[0]);
        }

        $key = $rule[1] ?? $field;
        $map = [];

        if (str_contains($key, '^')) {
            // 支持多个字段验证
            $fields = explode('^', $key);
            foreach ($fields as $key) {
                if (isset($data[$key])) {
                    $map[] = [$key, '=', $data[$key]];
                }
            }
        } elseif (isset($data[$field])) {
            $map[] = [$key, '=', $data[$field]];
        } else {
            $map = [];
        }

        $pk = !empty($rule[3]) ? $rule[3] : $db->getPk();

        if (is_string($pk)) {
            if (isset($rule[2])) {
                $map[] = [$pk, '<>', $rule[2]];
            } elseif (isset($data[$pk])) {
                $map[] = [$pk, '<>', $data[$pk]];
            }
        }

        if ($db->where($map)->field($pk)->find()) {
            return false;
        }

        return true;
    }
声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/60.html

文章归档

推荐文章

buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas