-
-
Save maarekj/6b61bf1d5183a804259ba1dc2464304f to your computer and use it in GitHub Desktop.
Example using of SameAs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Validator; | |
use Symfony\Component\Validator\Constraint; | |
/** | |
* Constraint for the Unique Entity validator. | |
* | |
* @Annotation | |
* @Target({"PROPERTY", "ANNOTATION"}) | |
*/ | |
class SameAs extends Constraint | |
{ | |
/** @var string */ | |
public $class; | |
/** @var string|null */ | |
public $property; | |
/** @var array|null */ | |
public $targetGroups; | |
/** {@inheritdoc} */ | |
public function getDefaultOption() | |
{ | |
return 'class'; | |
} | |
/** {@inheritdoc} */ | |
public function getRequiredOptions() | |
{ | |
return ['class']; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Validator; | |
use Illuminate\Support\Arr; | |
use Symfony\Component\Validator\Constraint; | |
use Symfony\Component\Validator\ConstraintValidator; | |
use Symfony\Component\Validator\Exception\UnexpectedTypeException; | |
use Symfony\Component\Validator\Mapping\ClassMetadataInterface; | |
use Symfony\Component\Validator\Mapping\PropertyMetadata; | |
final class SameAsValidator extends ConstraintValidator | |
{ | |
/** {@inheritdoc} */ | |
public function validate($value, Constraint $constraint) | |
{ | |
if (!$constraint instanceof SameAs) { | |
throw new UnexpectedTypeException($constraint, SameAs::class); | |
} | |
$class = $constraint->class; | |
$property = null === $constraint->property ? $this->context->getPropertyName() : $constraint->property; | |
if (null === $property) { | |
throw new UnexpectedTypeException($property, 'string'); | |
} | |
$targetGroups = (array) (null === $constraint->targetGroups ? $this->context->getGroup() : $constraint->targetGroups); | |
$classMetadata = $this->context->getValidator()->getMetadataFor($class); | |
if (!$classMetadata instanceof ClassMetadataInterface) { | |
throw new UnexpectedTypeException($classMetadata, ClassMetadataInterface::class); | |
} | |
$propertyMetadatas = $classMetadata->getPropertyMetadata($property); | |
$constraints = Arr::flatten(\array_map(function (PropertyMetadata $propertyMetadata) { | |
return $propertyMetadata->getConstraints(); | |
}, $propertyMetadatas), 1); | |
$this->context->getValidator()->inContext($this->context)->validate($value, $constraints, $targetGroups); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use App\Validator\SameAs; | |
final class EditPost { | |
/** @var Post */ | |
public $post; | |
/** | |
* @var string|null | |
* @SameAs(class=Post::class, property="slug") | |
*/ | |
public $slug; | |
/** | |
* @var string|null | |
* @SameAs(class=Post::class, property="content") | |
*/ | |
public $content; | |
/** | |
* @var Category|null | |
* @SameAs(class=Post::class, property="category") | |
*/ | |
public $category; | |
public function __construct(Post $post) { | |
$this->post = $post; | |
$this->slug = $post->getSlug(); | |
$this->content = $post->getContent(); | |
$this->category = $post->getCategory(); | |
} | |
} |
Hi!
Would you care to share the code for your SameAs validator? Seems like a neat way to handle DTOs!
Thanks!
I just added the SameAs.php
and SameAsValidator.php
in revision 3 of this gist.
Well thank you my good sir!
I've noticed that the SameAs validator doesn't work on Constraints that are cascading.
I ended up using the cached annotation reader, here's my full code for the SameAsValidator.php
class
<?php
declare(strict_types=1);
namespace App\Validator;
use App\Utils\Utils;
use Doctrine\Common\Annotations\Reader;
use ReflectionProperty;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Mapping\CascadingStrategy;
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
use Symfony\Component\Validator\Mapping\PropertyMetadata;
/**
* Validator handling the SameAs constraint
* Class SameAsValidator
* @package App\Validator
*/
final class SameAsValidator extends ConstraintValidator
{
/**
* @var Reader
*/
private $docReader;
public function __construct(Reader $docReader)
{
$this->docReader = $docReader;
}
/** {@inheritdoc} */
public function validate($value, Constraint $constraint)
{
// Check against constraint type
if (!$constraint instanceof SameAs) {
throw new UnexpectedTypeException($constraint, SameAs::class);
}
$class = $constraint->class;
// Getting the property of the class
$property = null === $constraint->property ? $this->context->getPropertyName() : $constraint->property;
if (null === $property) {
throw new UnexpectedTypeException($property, 'string');
}
// Getting target group
$targetGroups = (array)(null === $constraint->targetGroups ? $this->context->getGroup() : $constraint->targetGroups);
// Metadata
$classMetadata = $this->context->getValidator()->getMetadataFor($class);
if (!$classMetadata instanceof ClassMetadataInterface) {
throw new UnexpectedTypeException($classMetadata, ClassMetadataInterface::class);
}
// Metadata of targeted property
$propertyMetaDatas = $classMetadata->getPropertyMetadata($property);
// Retrieving all constraints from targeted class property
$constraints = Utils::array_flatten(array_map(function (PropertyMetadata $propertyMetadata) use ($class, $property) {
if(empty($propertyMetadata->getConstraints()) && $propertyMetadata->getCascadingStrategy() === CascadingStrategy::CASCADE) {
$reflectionProperty = new ReflectionProperty($class, $property);
$propertyAnnotations = $this->docReader->getPropertyAnnotations($reflectionProperty);
$cascadedConstraints = [];
foreach($propertyAnnotations as $annotation) {
if($annotation instanceof Constraint) {
$cascadedConstraints[] = $annotation;
}
}
return $cascadedConstraints;
}
return $propertyMetadata->getConstraints();
}, $propertyMetaDatas));
// Apply all constraints in this validation context
$this->context->getValidator()->inContext($this->context)->validate($value, $constraints, $targetGroups);
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi!
Would you care to share the code for your SameAs validator? Seems like a neat way to handle DTOs!
Thanks!