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

时间:2016-05-26 17:04:51

标签: php validation symfony

我意识到也许我想要使用Symfony中的Validator组件的方式是不可能的。这是想法。

我有一个名为Package的类,它现在只有一个名为namespace的属性。通常我会在我的Package类中包含ClassMetadata和我想要验证的任何约束对象。但是,我的想法是,我宁愿保持我的主题清洁,只负责它必须负责的事情。

下面是我写的一个类,并称之为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 : [];
   }

  }

尽管我不确定我的约束的用法,因为大多数参考使用注释,但我不能忽略那部分。我也意识到我的测试由于这个事实而失败。但是,我的问题在于我的设计,因为我没有添加Validation对象用于构建验证的静态函数。而不是我的方法映射,其中约束驻留在实际对象中,它位于一个单独的类中。

我们的想法是在我的对象上强制执行关注点分离单一责任。下面的图表描绘了我想要实现的目标:

enter image description here

我写了我的测试,如下所示:

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

上面我传入了一个数组,而不是一个字符串,这会使我的测试失败,因为永远不会有一个数组形式的命名空间 - 至少不是我想要实现的。

问题在于我的getViolations方法在PackageValidator对象中,因为我没有将我的主题传递到我的验证过程的上下文之外,该过程定义了主题本身内的主题元数据,然后获得具有对主题的refence的验证器对象元数据得到验证错误。

总而言之,Package没有loadMetadata方法,而是PackageValidator。如何在不污染我想用元数据功能验证的每个对象的情况下实现这一目标?

以下是我从PHPUnit获得的内容:

  

SimplexTest \验证\包\ PackageValidatorTest :: testIfValidatorInterfaceWorks   Symfony \ Component \ Validator \ Exception \ RuntimeException:不能   自动验证“NULL”类型的值。请提供一个   约束

2 个答案:

答案 0 :(得分:1)

您可以使用yml或xml配置向对象添加约束。

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

您可以通过在Bundle配置目录中创建名为validation.yml的文件来完成此操作。添加以下内容以验证您的对象:

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

这是保持你不认为对象的责任的一种方法。它还消除了对您创建的每个对象的自定义验证器类的需要。您可以简单地使用框架已经提供的验证器服务。

修改

好吧,我想我找到了你可能想要的东西:你可以创建一个MetadataFactory来按你想要的方式加载Metadata。这里有几个例子:https://github.com/symfony/validator/tree/master/Mapping/Factory

它基本上归结为一个Factory类,它返回一个MetadataInterface实例,您可以在其中附加约束。这意味着您可以让Factory读取任何内容的元数据。例如,您可以这样做:

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

然后在您的PackageValidator中,您所要做的就是:

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 : [];
    }

}

希望这更符合您的需求。

答案 1 :(得分:0)

我已经按照您的建议进行了操作。我唯一需要改变的是PackageMetadataFactory中的hasMetadaFor方法实现。以下是我如何检查财产存在。

public function hasMetadataFor( $value ){
 return property_exists(Package::class, $value);
}

你建议的其他一切都很完美。以下是我的测试功能。

$validator = new PackageValidator(new OrderPackage(125787618));
$this->assertSame(true, $validator->validates());

测试失败,因为命名空间不能是数字。通过执行OrderPackage :: class传递OrderPackage的完全限定类名验证对象。

非常感谢你的建议。