jackai/symfony-validator

在symfony控制器注解中验证请求

安装: 35

依赖: 0

建议者: 0

安全: 0

星标: 0

关注者: 2

分支: 0

开放问题: 0

类型:symfony-bundle

v1.09 2022-05-24 05:59 UTC

This package is auto-updated.

Last update: 2024-09-24 10:57:18 UTC


README

CircleCI

在symfony控制器注解中验证请求。

安装

1. 打开命令行,进入您的项目目录并执行以下命令以下载此扩展的最新版本

composer require jackai/symfony-validator

2. 打开config/services.yaml并添加以下配置

services:
    Jackai\Validator\RequestAdvancedValidateListener:
        arguments:
          - {
            doctrine: "@doctrine",
            ruleAlias: {
              "Assert": "Symfony\\Component\\Validator\\Constraints",
              "My": "App\\Validator"
            },
            requireFormCode: null,
            requireQueryCode: null,
            throwOnValidateFail: true,
            throwOnMissingValidate: false,
            emptyStringIsUndefined: true,
            shortErrorMsg: false,
          }
        tags:
            - { name: kernel.event_listener, event: kernel.request }

使用说明

验证器参数说明

  • throwOnMissingValidate: 当传入的参数不在验证列表中时,是否抛出异常,默认为 false
  • throwOnValidateFail: 当验证失败时,是否抛出异常,默认为 true
  • emptyStringIsUndefined: 当传入的参数为空字符串时,是否当作未传入参数处理,默认为 true
  • requireQuery: 在query中必填的项
  • requireForm: 在form中必填的项
  • requireQueryCode: 当query中必填项未填写时,要抛出的错误代码,默认为 null
  • requireFormCode: 当form中必填项未填写时,要抛出的错误代码,默认为 null
  • query: 在query中字段验证规则
  • form: 在form中字段验证规则

query及form验证规则说明

query和form参数中的验证规则如下:

  • name: 字段名称,可以用 . 来连接字段名称,例如参数为 config[abc] 就可写为 config.abc
  • dataType: 转换字段类型
  • rule: 验证规则,目前有三种可用
  • ruleOption: 验证规则的参数设置
  • code: 条件验证失败时返回的错误代码,默认为 null
  • msg: 条件验证失败时返回的错误信息,默认为 验证规则提供的错误信息
  • require: 是否为必填字段,默认值为 false
  • default: 当字段未填写时,默认传入 controller 的值

特殊规则的必填字段

"rule" = "require" 时,为验证特殊必填情况,特殊必填情况符合时 name 字段为必填。

特殊必填有以下几种模式:

  • 指定字段的值等于其中一個数值: "mode" = "if", "values" = {"指定字段", "数值1", "数值2",,, "数值N"}
  • 其中一個指定字段有值: "mode" = "with", "values" = {"指定字段A", "指定字段B",,, "指定字段N"}
  • 全部指定字段都有值: "mode" = "withAll", "values" = {"指定字段A", "指定字段B",,, "指定字段N"}
  • 指定的字段其中一個没有值: "mode" = "without", "values" = {"指定字段A", "指定字段B",,, "指定字段N"}
  • 指定的字段全部没有值: "mode" = "withoutAll", "values" = {"指定字段A", "指定字段B",,, "指定字段N"}

使用示例

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Jackai\Validator\AdvancedValidator;

class TestController extends AbstractController
{
     /**
      * @Route("/test")
      * @AdvancedValidator(
      *     throwOnMissingValidate = true,
      *     throwOnValidateFail = true,
      *     emptyStringIsUndefined = true,
      *     requireQuery = {"name"},
      *     requireQueryCode = 998,
      *     query = {
      *         {"name" = "name", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid name"},
      *         {"name" = "price", "rule" = "Assert\GreaterThan", "ruleOption" = "0", "code" = "112", "default" = "99999", "msg" = "Invalid price"},
      *         {"name" = "picture", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "113", "msg" = "Invalid picture"},
      *         {"name" = "picture", "rule" = "require", "ruleOption" = {"mode" = "if", "values" = {"price", "999"}}, "code" = "118", "msg" = "If price is 999, you should private picture."},
      *     },
      *     requireForm = {"postParamName"},
      *     requireFormCode = 999,
      *     form = {
      *         {"name" = "postField", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid post_field"},
      *     }
      * )
      */
    public function test(Request $request)
    {
        // do something
    }
}

创建自定义验证

使用官方Symfony网站上的示例创建验证器后 (https://symfony.com.cn/doc/current/validation/custom_constraint.html),在注解中添加以下行。

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Jackai\Validator\AdvancedValidator;

class TestController extends AbstractController
{
     /**
      * @Route("/test")
      * @AdvancedValidator(
      *     throwOnMissingValidate = true,
      *     throwOnValidateFail = true,
      *     emptyStringIsUndefined = true,
      *     requireQuery = {"name"},
      *     requireQueryCode = 998,
      *     query = {
      *         {"name" = "name", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid name"},
+      *         {"name" = "name", "rule" = "App\Validator\Constraints\ContainsAlphanumeric", "code" = "114", "msg" = "Name should be alphanumeric"},
      *         {"name" = "price", "rule" = "Assert\GreaterThan", "ruleOption" = "0", "code" = "112", "default" = "99999", "msg" = "Invalid price"},
      *         {"name" = "picture", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "113", "msg" = "Invalid picture"},
      *         {"name" = "picture", "rule" = "require", "ruleOption" = {"mode" = "if", "values" = {"price", "999"}}, "code" = "118", "msg" = "If price is 999, you should private picture."},
      *     },
      *     requireForm = {"postParamName"},
      *     requireFormCode = 999,
      *     form = {
      *         {"name" = "postField", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid post_field"},
      *     }
      * )
      */
    public function test(Request $request)
    {
        // do something
    }
}

创建高级自定义验证

  1. 使用Jackai\Validator\Constraint
  2. 您可以在约束中使用rawValues和doctrine
  3. 别忘了添加别名: "My": "App\\Validator"
  4. 然后您可以使用 "rule" = "My\DataUnique"

src\Validator\DataUnique.php

namespace App\Validator;

use Jackai\Validator\Constraint;

class DataUnique extends Constraint
{
    public $message = 'The attribute "{{ path }}" data "{{ string }}" is duplicate.';
    public $options = null;
    public $entity = null;
    public $rule = null;

    public function __construct($options = null)
    {
        $this->options = $options;
        parent::__construct($options);
    }
}

src\Validator\DataUniqueValidator.php

namespace App\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class DataUniqueValidator extends ConstraintValidator
{
   public function validate($value, Constraint $constraint)
   {
       if (!$constraint instanceof DataUnique) {
           throw new UnexpectedTypeException($constraint, DataUnique::class);
       }

       if ($this->checkDataBase($constraint)) {
           $this->context->buildViolation($constraint->message)
               ->setParameter('{{ path }}', $path)
               ->setParameter('{{ string }}', $value)
               ->addViolation();
       }
   }

   private function checkDataBase(DataUnique $constraint)
   {
       $ruleOption = $constraint->options;
       foreach (['entity', 'rule'] as $k => $v) {
           if (!array_key_exists($v, $ruleOption)) {
               throw new \RuntimeException("ruleOption.{$v} is require.");
           }
       }

       $entity = $ruleOption['entity'];
       $rule = $ruleOption['rule'];
       $values = $constraint->rawValues;
       $doctrine = $constraint->doctrine;
       $em = array_key_exists('em', $ruleOption) ? $ruleOption['em'] : 'default';
       $search = [];

       foreach ($rule as $k => $v) {
           if (strncasecmp($v, ":", 1) === 0) {
               $search[$k] = array_key_exists($k, $values) ? $values[$k] : null;
           }

           if (strncasecmp($v, ":", 1) !== 0) {
               $search[$k] = $v;
           }

           $search[$k] = str_replace('\:', ':', $search[$k]);
       }

       $data = $doctrine->getManager($em)->getRepository("App\\Entity\\{$entity}")->findOneBy($search);

       return $data !== null;
   }
}

config/service.yaml

    Jackai\Validator\RequestAdvancedValidateListener:
        arguments:
          - {
            doctrine: "@doctrine",
            ruleAlias: {
              "Assert": "Symfony\\Component\\Validator\\Constraints",
              "My": "App\\Validator"
            },
          }
        tags:
            - { name: kernel.event_listener, event: kernel.request }

src/Controller/TestController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Jackai\Validator\AdvancedValidator;

class TestController extends AbstractController
{
     /**
      * @Route("/test")
      * @AdvancedValidator(
      *     throwOnMissingValidate = true,
      *     throwOnValidateFail = true,
      *     emptyStringIsUndefined = true,
      *     requireQuery = {"name"},
      *     requireQueryCode = 998,
      *     query = {
      *         {"name" = "name", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid name"},
+      *         {"name" = "name", "rule" = "My\DataUnique", "ruleOption" = {"entity" = "Product", "rule" = {"name" = ":name"}}, "errorCode" = "119"},
      *         {"name" = "price", "rule" = "Assert\GreaterThan", "ruleOption" = "0", "code" = "112", "default" = "99999", "msg" = "Invalid price"},
      *         {"name" = "picture", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "113", "msg" = "Invalid picture"},
      *         {"name" = "picture", "rule" = "require", "ruleOption" = {"mode" = "if", "values" = {"price", "999"}}, "code" = "118", "msg" = "If price is 999, you should private picture."},
      *     },
      *     requireForm = {"postParamName"},
      *     requireFormCode = 999,
      *     form = {
      *         {"name" = "postField", "rule" = "Assert\Length", "ruleOption" = {"min" = 1, "max" = 30}, "code" = "111", "msg" = "Invalid post_field"},
      *     }
      * )
      */
    public function test(Request $request)
    {
        // do something
    }
}