Skip to content

[Validator] Added GroupSequenceProvider #3199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 9, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG-2.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* made ExecutionContext immutable
* deprecated Constraint methods `setMessage`, `getMessageTemplate` and
`getMessageParameters`
* added support for dynamic group sequences with the GroupSequenceProvider pattern

### Yaml

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator\Constraints;

/**
* Annotation to define a group sequence provider
*
* @Annotation
*/
class GroupSequenceProvider
{

}
10 changes: 8 additions & 2 deletions src/Symfony/Component/Validator/GraphWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Mapping\MemberMetadata;
use Symfony\Component\Validator\GroupSequenceProviderInterface;

/**
* Responsible for walking over and initializing validation on different
Expand Down Expand Up @@ -67,8 +68,13 @@ public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPa
$initializer->initialize($object);
}

if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
$groups = $metadata->getGroupSequence();
if ($group === Constraint::DEFAULT_GROUP && ($metadata->hasGroupSequence() || $metadata->isGroupSequenceProvider())) {
if ($metadata->hasGroupSequence()) {
$groups = $metadata->getGroupSequence();
} else {
$groups = $object->getGroupSequence();
}

foreach ($groups as $group) {
$this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);

Expand Down
26 changes: 26 additions & 0 deletions src/Symfony/Component/Validator/GroupSequenceProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Validator;

/**
* Defines the interface for a group sequence provider.
*/
interface GroupSequenceProviderInterface
{
/**
* Returns which validation groups should be used for a certain state
* of the object.
*
* @return array An array of validation groups
*/
function getGroupSequence();
}
34 changes: 34 additions & 0 deletions src/Symfony/Component/Validator/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ClassMetadata extends ElementMetadata
public $properties = array();
public $getters = array();
public $groupSequence = array();
public $groupSequenceProvider = false;
private $reflClass;

/**
Expand Down Expand Up @@ -57,6 +58,7 @@ public function __sleep()
return array_merge(parent::__sleep(), array(
'getters',
'groupSequence',
'groupSequenceProvider',
'members',
'name',
'properties',
Expand Down Expand Up @@ -247,6 +249,10 @@ public function getConstrainedProperties()
*/
public function setGroupSequence(array $groups)
{
if ($this->isGroupSequenceProvider()) {
throw new GroupDefinitionException('Defining a static group sequence is not allowed with a group sequence provider');
}

if (in_array(Constraint::DEFAULT_GROUP, $groups, true)) {
throw new GroupDefinitionException(sprintf('The group "%s" is not allowed in group sequences', Constraint::DEFAULT_GROUP));
}
Expand Down Expand Up @@ -293,4 +299,32 @@ public function getReflectionClass()

return $this->reflClass;
}

/**
* Sets whether a group sequence provider should be used
*
* @param boolean $active
*/
public function setGroupSequenceProvider($active)
{
if ($this->hasGroupSequence()) {
throw new GroupDefinitionException('Defining a group sequence provider is not allowed with a static group sequence');
}

if (!$this->getReflectionClass()->implementsInterface('Symfony\Component\Validator\GroupSequenceProviderInterface')) {
throw new GroupDefinitionException(sprintf('Class "%s" must implement GroupSequenceProviderInterface', $this->name));
}

$this->groupSequenceProvider = $active;
}

/**
* Returns whether the class is a group sequence provider.
*
* @return boolean
*/
public function isGroupSequenceProvider()
{
return $this->groupSequenceProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ public function getClassMetadata($class)

// Include constraints from all implemented interfaces
foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) {
if ('Symfony\Component\Validator\GroupSequenceProviderInterface' === $interface->getName()) {
continue;
}
$metadata->mergeConstraints($this->getClassMetadata($interface->getName()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Validator\Exception\MappingException;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\GroupSequence;
use Symfony\Component\Validator\Constraints\GroupSequenceProvider;
use Symfony\Component\Validator\Constraint;

class AnnotationLoader implements LoaderInterface
Expand All @@ -38,6 +39,8 @@ public function loadClassMetadata(ClassMetadata $metadata)
foreach ($this->reader->getClassAnnotations($reflClass) as $constraint) {
if ($constraint instanceof GroupSequence) {
$metadata->setGroupSequence($constraint->groups);
} elseif ($constraint instanceof GroupSequenceProvider) {
$metadata->setGroupSequenceProvider(true);
} elseif ($constraint instanceof Constraint) {
$metadata->addConstraint($constraint);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public function loadClassMetadata(ClassMetadata $metadata)
if (isset($this->classes[$metadata->getClassName()])) {
$xml = $this->classes[$metadata->getClassName()];

foreach ($xml->{'group-sequence-provider'} as $provider) {
$metadata->setGroupSequenceProvider(true);
}

foreach ($this->parseConstraints($xml->constraint) as $constraint) {
$metadata->addConstraint($constraint);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public function loadClassMetadata(ClassMetadata $metadata)
if (isset($this->classes[$metadata->getClassName()])) {
$yaml = $this->classes[$metadata->getClassName()];

if (isset($yaml['group_sequence_provider'])) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I use array_key_exists here? This would allow the user to set:

MyClass:
    dynamic_constraint_provider: ~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think isset is fine.

MyClass:
    dynamic_constraint_provider: true

$metadata->setGroupSequenceProvider((bool)$yaml['group_sequence_provider']);
}

if (isset($yaml['constraints'])) {
foreach ($this->parseNodes($yaml['constraints']) as $constraint) {
$metadata->addConstraint($constraint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,22 @@
]]></xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="group-sequence-provider" type="group-sequence-provider" minOccurs="0" maxOccurs="1" />
<xsd:element name="constraint" type="constraint" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="property" type="property" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="getter" type="getter" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>

<xsd:complexType name="group-sequence-provider">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the name of the group sequence provider for a class.
]]></xsd:documentation>
</xsd:annotation>
</xsd:complexType>

<xsd:complexType name="property">
<xsd:annotation>
<xsd:documentation><![CDATA[
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Symfony\Tests\Component\Validator\Fixtures;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\GroupSequenceProviderInterface;

/**
* @Assert\GroupSequenceProvider
*/
class GroupSequenceProviderEntity implements GroupSequenceProviderInterface
{
public $firstName;
public $lastName;

protected $groups = array();

public function setGroups($groups)
{
$this->groups = $groups;
}

public function getGroupSequence()
{
return $this->groups;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Exception\GroupDefinitionException;
use Symfony\Tests\Component\Validator\Fixtures\Entity;
use Symfony\Tests\Component\Validator\Fixtures\ConstraintA;
use Symfony\Tests\Component\Validator\Fixtures\ConstraintB;
use Symfony\Tests\Component\Validator\Fixtures\PropertyConstraint;
use Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProvider;

require_once __DIR__.'/../Fixtures/Entity.php';
require_once __DIR__.'/../Fixtures/ConstraintA.php';
Expand All @@ -28,6 +30,7 @@ class ClassMetadataTest extends \PHPUnit_Framework_TestCase
{
const CLASSNAME = 'Symfony\Tests\Component\Validator\Fixtures\Entity';
const PARENTCLASS = 'Symfony\Tests\Component\Validator\Fixtures\EntityParent';
const PROVIDERCLASS = 'Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity';

protected $metadata;

Expand Down Expand Up @@ -189,5 +192,40 @@ public function testGroupSequencesFailIfContainingDefault()

$this->metadata->setGroupSequence(array('Foo', $this->metadata->getDefaultGroup(), Constraint::DEFAULT_GROUP));
}
}

/**
* @expectedException Symfony\Component\Validator\Exception\GroupDefinitionException
*/
public function testGroupSequenceFailsIfGroupSequenceProviderIsSet()
{
$metadata = new ClassMetadata(self::PROVIDERCLASS);
$metadata->setGroupSequenceProvider(true);
$metadata->setGroupSequence(array('GroupSequenceProviderEntity', 'Foo'));
}

/**
* @expectedException Symfony\Component\Validator\Exception\GroupDefinitionException
*/
public function testGroupSequenceProviderFailsIfGroupSequenceIsSet()
{
$metadata = new ClassMetadata(self::PROVIDERCLASS);
$metadata->setGroupSequence(array('GroupSequenceProviderEntity', 'Foo'));
$metadata->setGroupSequenceProvider(true);
}

/**
* @expectedException Symfony\Component\Validator\Exception\GroupDefinitionException
*/
public function testGroupSequenceProviderFailsIfDomainClassIsInvalid()
{
$metadata = new ClassMetadata('stdClass');
$metadata->setGroupSequenceProvider(true);
}

public function testGroupSequenceProvider()
{
$metadata = new ClassMetadata(self::PROVIDERCLASS);
$metadata->setGroupSequenceProvider(true);
$this->assertTrue($metadata->isGroupSequenceProvider());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,17 @@ public function testLoadClassMetadataAndMerge()
$this->assertEquals($expected, $metadata);
}

public function testLoadGroupSequenceProviderAnnotation()
{
$loader = new AnnotationLoader(new AnnotationReader());

$metadata = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');
$loader->loadClassMetadata($metadata);

$expected = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');
$expected->setGroupSequenceProvider(true);
$expected->getReflectionClass();

$this->assertEquals($expected, $metadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,17 @@ public function testLoadClassMetadata()

$this->assertEquals($expected, $metadata);
}

public function testLoadGroupSequenceProvider()
{
$loader = new XmlFileLoader(__DIR__.'/constraint-mapping.xml');
$metadata = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');

$loader->loadClassMetadata($metadata);

$expected = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');
$expected->setGroupSequenceProvider(true);

$this->assertEquals($expected, $metadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,17 @@ public function testLoadClassMetadata()

$this->assertEquals($expected, $metadata);
}

public function testLoadGroupSequenceProvider()
{
$loader = new YamlFileLoader(__DIR__.'/constraint-mapping.yml');
$metadata = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');

$loader->loadClassMetadata($metadata);

$expected = new ClassMetadata('Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity');
$expected->setGroupSequenceProvider(true);

$this->assertEquals($expected, $metadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,11 @@
<constraint name="NotNull" />
</getter>
</class>

<class name="Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity">

<!-- GROUP SEQUENCE PROVIDER -->
<group-sequence-provider />

</class>
</constraint-mapping>
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ Symfony\Tests\Component\Validator\Fixtures\Entity:
getters:
lastName:
- NotNull: ~

Symfony\Tests\Component\Validator\Fixtures\GroupSequenceProviderEntity:
group_sequence_provider: true
Loading