dongshai1944 2016-05-26 17:04
浏览 65
已采纳

独立应用程序中的Symfony Validator组件问题

I am realizing that perhaps the way I want to make use of the Validator Component from Symfony is not possible. Here is the idea.

I have a class called Package which for now has only one property named namespace. Usually I would include the ClassMetadata and any constraint object I would like to validate against within my Package class. However, my idea is that instead of doing that I would rather keep my subject clean and only responsible for the things it must be responsible for.

Below is a class I wrote and call it PackageValidater:

<?php
 use Symfony\Component\Validator\Constraints as Assert;
 use Symfony\Component\Validator\Mapping\ClassMetadata;
 use Symfony\Component\Validator\Validation;

 class PackageValidator
 {
   protected $subject;

   public function PackageValidator($subject){
    $this->subject = $subject;
   }

   public static function loadMetadata(){
    $metadata->addPropertyConstraint('namespace', new new Assert\Type(['type' => 'string']));
   }

   public function getViolations(){
    $validator = Validation::createValidatorBuilder()
       ->addMethodMapping('loadMetadata')
       ->getValidator();

    $violations = $validator->validate($this->subject);

    return !empty($violations) ? $violations : [];
   }

  }

Despite of the fact that I am not sure about the usage of my constraint since most reference uses annotations and I do not we can ignore that part. I also am aware of the fact that my test fails due to this fact. However, my issue is with my design because I have not added the static function that the Validation object uses to build the validation. Instead of my method mapping where constraints reside being in the actual object it resides on a separate class.

The idea is to enforce separation of concerns and single responsibility on my objects. Below is a diagram that depicts exactly what I am trying to achieve:

enter image description here

I have written my test as shown below:

$packageValidator = new PackageValidator(new Package([0 => 'test']));
$this->assertTrue(true, empty($packageValidator->getViolations()));

Above I have passed in an array instead of a string which would make my test fail because there can never be a single namespace that is in a form of array - at least not in what I am trying to achieve.

The issue is with my getViolations method inside the PackageValidator object because I am not passing my subject outside the context of my validation process that is define the subject metadata inside the subject itself then when getting the validator object with the refence to the subject's metadata get the validation errors.

All in all Package does not have loadMetadata method but PackageValidator. How can I make this possible without polluting every object I want to validate with the metadata functionality?

Below is what I get from PHPUnit:

SimplexTest\Validate\Package\PackageValidatorTest::testIfValidatorInterfaceWorks Symfony\Component\Validator\Exception\RuntimeException: Cannot validate values of type "NULL" automatically. Please provide a constraint.

  • 写回答

2条回答 默认 最新

  • duandian8110 2016-05-28 18:33
    关注

    You can use yml or xml configuration to add constraints to your object.

    http://symfony.com/doc/current/book/validation.html#the-basics-of-validation

    You do this by creating a file called validation.yml in your Bundle configuration directory. Add the following content to validate your object:

    Some\Name\Space\Package:
        properties:
            name:
                - NotBlank: ~
    

    That's one way to keep things you don't consider a responsibility for your object out of said object. It also removes the need for a custom validator class for every object you create. You can simply make use of the validator service already provided by the framework.

    Edit

    Alright I think I figured something out you might be looking for: you can create a MetadataFactory to load Metadata the way you want. There are a couple of examples here: https://github.com/symfony/validator/tree/master/Mapping/Factory

    It basically boils down to a Factory class that returns an instance of MetadataInterface where you attach your constraints. This means that you can have the Factory read metadata from anything. You could for example do something like this:

    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
    use Your\Package;
    
    class PackageMetadataFactory implements MetadataFactoryInterface
    {
        /**
         * Create a ClassMetaData object for your Package object
         *
         * @param object $value The object that will be validated
         */
        public function getMetadataFor($value)
        {
    
            // Create a class meta data object for your entity
            $metadata = new ClassMetadata(Package::class);
    
            // Add constraints to your metadata
            $metadata->addPropertyConstraint(
                'namespace', new Assert\Type(['type' => 'string']));
    
            // Return the class metadata object
            return $metadata;
        }
    
        /**
         * Test if the value provided is actually of type Package
         *
         * @param object $value The object that will be validated
         */
        public function hasMetadataForValue($value)
        {
            return $value instanceof Package::class;
        }
    }
    

    Then in your PackageValidator all you have to do is:

    use Symfony\Component\Validator\Mapping\ClassMetadata;
    use Symfony\Component\Validator\Validation;
    use Your\PackageMetadataFactory;
    
    class PackageValidator
    {
        protected $subject;
    
        public function PackageValidator($subject) {
            $this->subject = $subject;
        }
    
        public function getViolations() {
            $validator = Validation::createValidatorBuilder()
                ->setMetadataFactory(new PackageMetadataFactory())
                ->getValidator();
    
            $violations = $validator->validate($this->subject);
    
            return !empty($violations) ? $violations : [];
        }
    
    }
    

    Hopefully this is more in line of what you're looking for.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥20 测距传感器数据手册i2c