定制 jackai/symfony-validator 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

jackai/symfony-validator

Composer 安装命令:

composer require jackai/symfony-validator

包简介

Validate request on symfony controller annotation

README 文档

README

CircleCI

Validate request on symfony controller annotation.

Installation

1.Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:

composer require jackai/symfony-validator

2.Open config/services.yaml and add this config:

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 }

Useage

驗證器參數說明

  • 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
    }
}

Create custom validation

After creating a validator using the example on the official Symfony website (https://symfony.com/doc/current/validation/custom_constraint.html), add the following line in the annotation.

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
    }
}

Create advanced custom validation

  1. Use Jackai\Validator\Constraint
  2. You can get rawValues and doctrine in constraint
  3. Don't forget to add alias: "My": "App\\Validator"
  4. And you can use "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
    }
}

jackai/symfony-validator 适用场景与选型建议

jackai/symfony-validator 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 38 次下载、GitHub Stars 达 0, 最近一次更新时间为 2020 年 08 月 29 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「symfony」 「annotation」 「validate」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 jackai/symfony-validator 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 jackai/symfony-validator 我们能提供哪些服务?
定制开发 / 二次开发

基于 jackai/symfony-validator 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 38
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 10
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2020-08-29