Reputation: 16034
I want to use custom validators on various query parameters in my controller. The docs give this example:
// validate a query parameter (a hash in this case)
$incomingHashConstraint = new CustomAssert\IncomingHash();
// use the validator to validate the value
// If you're using the new 2.5 validation API (you probably are!)
$errorList = $this->get('validator')->validate(
$incomingHash,
$incomingHashConstraint
);
if (0 === count($errorList)) {
// ... this IS a valid hash
} else {
// this is *not* a valid hash
$errorMessage = $errorList[0]->getMessage();
// ... do something with the error
throw $this->createNotFoundException('Not a valid hash ID ' . $incomingHash);
}
It is pretty clunky to use this in a lot of controllers. Ideally I'd be able to use a custom validator as a requirement in the route, but that doesn't seem to be an option. Should these validators be a service? Ideally I'd want something like
if(!isValid($incomingHash, IncomingHashConstraint)) {
throw \Exception(); }
Any suggestions on the best way to organize this? Thanks!
Upvotes: 1
Views: 2397
Reputation: 12720
There is a very easy and clean way of doing it.
I'll give a working example but if you want a full example then it is here. If you apply it, you'll have a very very thin controller. Literally no more than 15 lines. This way you'll have what exactly what @SergioIvanuzzo said above.
INSTALL jms/serializer-bundle Doc
composer require jms/serializer-bundle
// in AppKernel::registerBundles()
$bundles = array(
// ...
new JMS\SerializerBundle\JMSSerializerBundle(),
// ...
);
CUSTOM MODEL CLASS
namespace Application\FrontendBundle\Model;
use Application\FrontendBundle\Validator\Constraint as PersonAssert;
use JMS\Serializer\Annotation as Serializer;
/**
* @PersonAssert\Person
*/
class Person
{
/**
* @var int
* @Serializer\Type("integer")
*/
public $id;
/**
* @var string
* @Serializer\Type("string")
*/
public $name;
/**
* @var string
* @Serializer\Type("string")
*/
public $dob;
/**
* @var string
* @Serializer\Type("string")
*/
public $whatever;
}
YOUR CUSTOM VALIDATOR CLASSES
namespace Application\FrontendBundle\Validator\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class Person extends Constraint
{
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
public function validatedBy()
{
return get_class($this).'Validator';
}
}
namespace Application\FrontendBundle\Validator\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class PersonValidator extends ConstraintValidator
{
public function validate($person, Constraint $constraint)
{
if (!is_numeric($person->id)) {
$this->context->buildViolation('Id must be a numeric value.')->addViolation();
}
if ($person->name == 'Acyra') {
$this->context->buildViolation('You name is weird.')->addViolation();
}
if ($person->dob == '28/11/2014') {
$this->context->buildViolation('You are too young.')->addViolation();
}
// I'm not interested in validating $whatever property of Person model!
}
}
CONTROLLER
If you don't use your controller as a service then you can access validator
and serializer
services directly with $this->get('put_the_name_here')
like you did above.
...
use JMS\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
....
/**
* @Route("person", service="application_frontend.controller.bank")
*/
class PersonController extends Controller
{
private $validator;
private $serializer;
public function __construct(
ValidatorInterface $validator,
SerializerInterface $serializer
) {
$this->validator = $validator;
$this->serializer = $serializer;
}
/**
* @param Request $request
*
* @Route("/person")
* @Method({"POST"})
*
* @return Response
*/
public function personAction(Request $request)
{
$person = $this->validatePayload(
$request->getContent(),
'Application\FrontendBundle\Model\Person'
);
if ($person instanceof Response) {
return $person;
}
print_r($person);
// Now you can carry on doing things in your service class
}
private function validatePayload($payload, $model, $format = 'json')
{
$payload = $this->serializer->deserialize($payload, $model, $format);
$errors = $this->validator->validate($payload);
if (count($errors)) {
return new Response('Some errors', 400);
}
return $payload;
}
}
EXAMPLES
Request 1
{
"id": 66,
"name": "Acyraaaaa",
"dob": "11/11/1111",
"whatever": "test"
}
Response 1
Application\FrontendBundle\Model\Person Object
(
[id] => 66
[name] => Acyraaaaa
[dob] => 11/11/1111
[whatever] => test
)
Request 2
{
"id": "Hello",
"name": "Acyra",
"dob": "28/11/2014"
}
Response 2
400 Bad request
Some errors
If you go to link I gave you above and apply the rest then you would actually get proper error messages like:
{
"errors": {
"id": "Id must be a numeric value.",
"name": "You name is weird.",
"dob": "You are too young."
}
}
Upvotes: 4