Hello Stackers,
After readling a lot of documentation, SymfonyCasts and other questions here I still didn't find a cause to my problem. In my opinion, and I could of course be very wrong since the product is so big, I followed the steps as given by the Symfony Docs I still cannot seem to get a Custom Validator Constraint working.
We have a form controller, which I hereby call BasicFormController.php. This is where the actual form submission is sent at. At the end I added an empty response, just to see if it can continue. The problem here is with the the EmailNotFiltered constraint. It get's called here, but after that it just stops.
$constraint = new Assert\Collection([
'firstName' => new Assert\Length(['min' => 1, 'max' => 50]),
'lastName' => new Assert\Length(['min' => 1, 'max' => 50]),
'email' => new ProductAssert\EmailNotFiltered(),
'textWhy' => new Assert\Length(['min' => 20, 'max' => 1000]),
]);
# Validate for all violations.
$violations = $validatorInterface->validate($request->get('data'), $constraint);
# Form contains violations
if (count($violations) >= 1) {
return new JsonResponse(['data' => $violations, 'status' => "error"]);
}
return new JsonResponse([]);
Then the Validator Constraint itself. EmailNotFiltered.php. Nothing weird, just as in the instructions.
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
#[\Attribute]
class EmailNotFiltered extends Constraint
{
public $message = "Invalid email address. Declined by filter.";
public function __construct(array $groups = null, mixed $payload = null)
{
parent::__construct([], $groups, $payload);
}
public function validatedBy()
{
return static::class.'Validator';
}
}
Followed with the Validator, in the same Folder, EmailNotFilteredValidator.php. I've just removed all my own logic to test if I can even get a violation to build.
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
#[\Attribute]
class EmailNotFilteredValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void{
$this->context->buildViolation($constraint->message)
->setParameter('{{ string }}', $value)
->addViolation();
}
}
However, if I throw an exception there, it's not seen. The last place an exception can be thrown and where it will be seen is at the Constraint base file, in the constructor. After that it's done.
I removed all the default logic from the Validator too, just to be sure. But with that, it does not work too. It doesn't really matter, it just doesn't seem to even build up a single violation.
Am I understanding the step-by-step documentation wrong? Or what is the thing I'm doing wrong here?
Related
I am trying to create a custom symfony form validator constraint. I created two class, one constraint and one validator and it works fine. But I need to pass doctrine entitymanager instance to validator class, as I am using them standalone and not framework, I don't have yaml configuration file. I created a constructor in validator class to have $em, and in controller I have:
->add('email', EmailType::class, [
'constraints' => [
new Assert\Email(['message' => 'invalid.email', 'mode' => 'strict', 'normalizer' => 'trim']),
new Assert\EmailExists($em),
],
]);
But I am not getting $em, in my validator class, what should I do? I also tried to have constructor in main constraint class, then in validator I had parent::construct(), still not working.
I did read this too How to configure dependencies on custom validator with Symfony Components? but instead of making the factory class, I used the current factor class and used this:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em);
$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();
$violations = $validator->validate('email address', [
new EmailExists()
]);
if (0 !== count($violations)) {
// there are errors, now you can show them
foreach ($violations as $violation) {
echo $violation->getMessage().'<br>';
}
}
With this code both dependency injection and validation works fine, but is there a trick to have this custom constraint as 'constraint' array argument within form builder rather than validating it manually?
->add('email', EmailType::class, [
'constraints' => [
new Assert\Email(['message' => 'invalid.email', 'mode' => 'strict', 'normalizer' => 'trim']),
new Assert\EmailExists($em),
],
]);
With code above I cannot pass $em to the constructor of my custom Validator. Any trick possible?
EDIT:
In order to inject doctrine EntityManager, in EmailExists class I had:
public function validatedBy()
{
return 'customEmailUniqueEntity';
//return \get_class($this).'Validator';
}
then I had:
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em);
because if I was returning validator class from validatedBy() I could not inject $em to the constructor of validator. With the answer below I used:
->addTag('validator.constraint_validator');
But now I am getting customEmailUniqueEntity class not found error, as if I return validator from validatedBy(), injection will not work, what should I do? I tried to return
public function validatedBy()
{
return 'EmailExists';
//return \get_class($this).'Validator';
}
but this one, of course I am getting initialize() error. Please advise.
EDIT2:
I added addTag to:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em),
->addTag('validator.constraint_validator');
$validatorBuilder = Validation::createValidatorBuilder();
$validatorBuilder->setConstraintValidatorFactory(
new ContainerConstraintValidatorFactory($container)
);
$validator = $validatorBuilder->getValidator();
$violations = $validator->validate('email address', [
new EmailExists()
]);
if (0 !== count($violations)) {
// there are errors, now you can show them
foreach ($violations as $violation) {
echo $violation->getMessage().'<br>';
}
}
and in constructor of EmailExistsValidator I have:
var_dump($em);
and I got $em object in validator, so $em is injected and adding addTag() did not cause any error. If I remove validatedBy() of EmailExists contsraint, injection will not be done. In that method I am doing
return `customEmailUniqueEntity;`
because if I return EmailExistsValidator, injection will not be done.
Now how to use validator.constraint_validator or EmailExists() as constraints array param of the form? if I use new EmailExists() I will get Two Few Aguments for validator class as $em wll not be injected this way. What to do?
Constraints are not validators.
Symfony will take a constraint and search for its validator by attaching Validator to the classname.
So in symfony you register your constraint by EmailExists but the class/service which actually does validation is EmailExistsValidator.
And this is also the place to inject EntityManagerInterface into it.
All information can be found here: Symfony - How to Create a custom Validation Constraint
Your customEmailUniqueEntity service will never be taken into account by the ContainerConstraintValidatorFactory when it determines the actual validator to be used for the EmailExists constraint.
In order to let the factory know which services are constraint validators you need to tag it with the validator.constraint_validator tag like this:
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
$container = new ContainerBuilder();
$container
->register('customEmailUniqueEntity', 'EmailExistsValidator')
->addArgument($em)
->addTag('validator.constraint_validator');
$container->addCompilerPass(new AddConstraintValidatorPass());
$container->compile();
I'm trying to write some tests for my forms in order to confirm the validators retrieve the expected errors when required.
The form only has 3 fields: name, discount and expiration and the validator looks like this:
$this->validate($request, [
'name' => 'required',
'discount' => 'required|numeric|between:1,100',
'expiration' => 'required|date_format:d/m/Y',
]);
That works fine both when submitting the form and when running the tests with phpunit using the following code:
/**
* Discount must be numeric check
*/
$response = $this->post(route('offer.create'), [
'name' => $faker->sentence(4),
'discount' => 'asdasd',
'expiration' => $faker->dateTimeBetween('+1 days', '+5 months')
]);
// Check errors returned
$response->assertSessionHasErrors(['discount']);
Since discount is not numeric it throws the expected error and everybody is happy.
Now, if I want to add a new rule to make sure that the expiration is equal or greater to today I add the after:yesterdayrule leaving the validator like:
$this->validate($request, [
'name' => 'required',
'discount' => 'required|numeric|between:1,100',
'expiration' => 'required|date_format:d/m/Y|after:yesterday',
]);
That works fine when submitting the form. I get the error saying the discount is not numeric, but when testing with phpunit it doesn't get the error as expected:
1) Tests\Feature\CreateSpecialOfferTest::testCreateSpecialOffer
Session missing error: expiration
Failed asserting that false is true.
Why adding this new validation rule to expirationgenerates a false validation in discount? Is this a bug in the validator or am I missing something?
Also:
1 - is there a better way to test form validators?
2 - is there an assert that is the opposite of assertSessionHasErrors() to check a certain error is NOT been thrown?
If you see this kind of errors in PHPUnit: Failed asserting that false is true., you can add the 'disableExceptionHandling' function to tests/TestCase.php:
<?php
namespace Tests;
use Exception;
use App\Exceptions\Handler;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function disableExceptionHandling()
{
// Disable Laravel's default exception handling
// and allow exceptions to bubble up the stack
$this->app->instance(ExceptionHandler::class, new class extends Handler {
public function __construct() {}
public function report(Exception $exception) {}
public function render($request, Exception $exception)
{
throw $exception;
}
});
}
}
In your test you call it like this:
<?php
/** #test */
public function your_test_function()
{
$this->disableExceptionHandling();
}
Now, the full output of the error and stacktrace will be shown in the PHPUnit console.
The symfony validator throws an exception when I attempt to validate a scalar using a Collection constraint. I would expect it to return a violation instead.
Example code:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Collection;
$validator = Validation::createValidator();
$input = 'testtesttest';
$constraints = [
new Collection([
'fields' => [
'one' => new Length(array('min' => 10))
]
])
];
$violationList = $validator->validate($input, $constraints);
throws
PHP Fatal error: Uncaught Symfony\Component\Validator\Exception\UnexpectedTypeException: Expected argument of type "array or Traversable and ArrayAccess", "string" given in vendor/symfony/validator/Constraints/CollectionValidator.php:37
Am I doing something wrong here?
For other Constraint classes (e.g. NotBlank, Type) the validator adds to the violation list when it encounters something invalid. To have it throw an exception instead in the case of a Collection seems bizarre to me. Am I doing something obviously wrong?
Sorry for responding a year after but I was stuck with the same issue.
The solution for me was to create a custom Validation Constraint.
Firstly you have to create a custom constraint: CustomCollection which will contain the following code (note that my class is extending the Collection constraint and not the default Constraint class):
<?php
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraints\Collection;
class CustomCollection extends Collection
{
public $message = 'You must provide an array.';
}
Then you have to implement your custom constraint's logic (in this case validate that your value is a valid array) :
<?php
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\CollectionValidator;
class CustomCollectionValidator extends CollectionValidator
{
public function validate($value, Constraint $constraint)
{
if (!\is_array($value)) {
$this->context->buildViolation($constraint->message)
->addViolation();
return;
}
parent::validate($value, $constraint);
}
}
Now if I take your code as example, you have to change the constraint from Collection to CustomCollection in order to get a violation :
$input = 'testtesttest';
$constraints = [
new CustomCollection([
'fields' => [
'one' => new Length(array('min' => 10))
]
])
];
$violationList = $validator->validate($input, $constraints);
You are misusing the Collection constraint. This constraint is meant for validating collections (e.g. array, a traversable object).
You should pass an array of constraints to the validate method.
E.g.:
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\Length;
$validator = Validation::createValidator();
$input = 'testtesttest';
$constraints = [
new Length(array('min' => 10)),
// ... And other constraints
];
$violationList = $validator->validate($input, $constraints);
You can see more info about the validator here: https://symfony.com/doc/current/components/validator.html#usage
I am trying to unit test various custom FormRequest inputs. I found solutions that:
Suggest using the $this->call(…) method and assert the response with the expected value (link to answer). This is overkill, because it creates a direct dependency on Routing and Controllers.
Taylor’s test, from the Laravel Framework found in tests/Foundation/FoundationFormRequestTest.php. There is a lot of mocking and overhead done there.
I am looking for a solution where I can unit test individual field inputs against the rules (independent of other fields in the same request).
Sample FormRequest:
public function rules()
{
return [
'first_name' => 'required|between:2,50|alpha',
'last_name' => 'required|between:2,50|alpha',
'email' => 'required|email|unique:users,email',
'username' => 'required|between:6,50|alpha_num|unique:users,username',
'password' => 'required|between:8,50|alpha_num|confirmed',
];
}
Desired Test:
public function testFirstNameField()
{
// assertFalse, required
// ...
// assertTrue, required
// ...
// assertFalse, between
// ...
}
public function testLastNameField()
{
// ...
}
How can I unit test (assert) each validation rule of every field in isolation and individually?
I found a good solution on Laracast and added some customization to the mix.
The Code
/**
* Test first_name validation rules
*
* #return void
*/
public function test_valid_first_name()
{
$this->assertTrue($this->validateField('first_name', 'jon'));
$this->assertTrue($this->validateField('first_name', 'jo'));
$this->assertFalse($this->validateField('first_name', 'j'));
$this->assertFalse($this->validateField('first_name', ''));
$this->assertFalse($this->validateField('first_name', '1'));
$this->assertFalse($this->validateField('first_name', 'jon1'));
}
/**
* Check a field and value against validation rule
*
* #param string $field
* #param mixed $value
* #return bool
*/
protected function validateField(string $field, $value): bool
{
return $this->validator->make(
[$field => $value],
[$field => $this->rules[$field]]
)->passes();
}
/**
* Set up operations
*
* #return void
*/
public function setUp(): void
{
parent::setUp();
$this->rules = (new UserStoreRequest())->rules();
$this->validator = $this->app['validator'];
}
Update
There is an e2e approach to the same problem. You can POST the data to be checked to the route in question and then see if the response contains session errors.
$response = $this->json('POST',
'/route_in_question',
['first_name' => 'S']
);
$response->assertSessionHasErrors(['first_name']);
I see this question has a lot of views and misconceptions, so I will add my grain of sand to help anyone who still has doubts.
First of all, remember to never test the framework, if you end up doing something similar to the other answers (building or binding a framework core's mock (disregard Facades), then you are doing something wrong related to testing).
So, if you want to test a controller, the always way to go is: Feature test it. NEVER unit test it, not only is cumbersome to unit test it (create a request with data, maybe special requirements) but also instantiate the controller (sometimes it is not new HomeController and done...).
They way to solve the author's problem is to feature test like this (remember, is an example, there are plenty of ways):
Let's say we have this rules:
public function rules()
{
return [
'name' => ['required', 'min:3'],
'username' => ['required', 'min:3', 'unique:users'],
];
}
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class HomeControllerTest extends TestCase
{
use RefreshDatabase;
/*
* #dataProvider invalid_fields
*/
public function test_fields_rules($field, $value, $error)
{
// Create fake user already existing for 'unique' rule
User::factory()->create(['username' => 'known_username']);
$response = $this->post('/test', [$field => $value]);
$response->assertSessionHasErrors([$field => $error]);
}
public function invalid_fields()
{
return [
'Null name' => ['name', null, 'The name field is required.'],
'Empty name' => ['name', '', 'The name field is required.'],
'Short name' => ['name', 'ab', 'The name must be at least 3 characters.'],
'Null username' => ['username', null, 'The username field is required.'],
'Empty username' => ['username', '', 'The username field is required.'],
'Short username' => ['username', 'ab', 'The username must be at least 3 characters.'],
'Unique username' => ['username', 'known_username', 'The username has already been taken.'],
];
}
}
And that's it... that is the way of doing this sort of tests... No need to instantiate/mock and bind any framework (Illuminate namespace) class.
I am taking advantage of PHPUnit too, I am using data providers so I don't need to copy paste a test or create a protected/private method that a test will call to "setup" anything... I reuse the test, I just change the input (field, value and expected error).
If you need to test if a view is being displayed, just do $response->assertViewIs('whatever.your.view');, you can also pass a second attribute (but use assertViewHas) to test if the view has a variable in it (and a desired value). Again, no need to instantiate/mock any core class...
Have in consideration this is just a simple example, it can be done a little better (avoid copy pasting some errors messages).
One last important thing: If you unit test this type of things, then, if you change how this is done in the back, you will have to change your unit test (if you have mocked/instantiated core classes). For example, maybe you are now using a FormRequest, but later you switch to other validation method, like a Validator directly, or an API call to other service, so you are not even validating directly in your code. If you do a Feature Test, you will not have to change your unit test code, as it will still receive the same input and give the same output, but if it is a Unit Test, then you are going to change how it works... That is the NO-NO part I am saying about this...
Always look at test as:
Setup minimum stuff (context) for it to begin with:
What is your context to begin with so it has logic ?
Should a user with X username already exist ?
Should I have 3 models created ?
Etc.
Call/execute your desired code:
Send data to your URL (POST/PUT/PATCH/DELETE)
Access a URL (GET)
Execute your Artisan Command
If it is a Unit Test, instantiate your class, and call the desired method.
Assert the result:
Assert the database for changes if you expected them
Assert if the returned value matches what you expected/wanted
Assert if a file changed in any desired way (deletion, update, etc)
Assert whatever you expected to happen
So, you should see tests as a black box. Input -> Output, no need to replicate the middle of it... You could setup some fakes, but not fake everything or the core of it... You could mock it, but I hope you understood what I meant to say, at this point...
Friends, please, make the unit-test properly, after all, it is not only rules you are testing here, the validationData and withValidator functions may be there too.
This is how it should be done:
<?php
namespace Tests\Unit;
use App\Http\Requests\AddressesRequest;
use App\Models\Country;
use Faker\Factory as FakerFactory;
use Illuminate\Routing\Redirector;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;
use function app;
use function str_random;
class AddressesRequestTest extends TestCase
{
public function test_AddressesRequest_empty()
{
try {
//app(AddressesRequest::class);
$request = new AddressesRequest([]);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
} catch (ValidationException $ex) {
}
//\Log::debug(print_r($ex->errors(), true));
$this->assertTrue(isset($ex));
$this->assertTrue(array_key_exists('the_address', $ex->errors()));
$this->assertTrue(array_key_exists('the_address.billing', $ex->errors()));
}
public function test_AddressesRequest_success_billing_only()
{
$faker = FakerFactory::create();
$param = [
'the_address' => [
'billing' => [
'zip' => $faker->postcode,
'phone' => $faker->phoneNumber,
'country_id' => $faker->numberBetween(1, Country::count()),
'state' => $faker->state,
'state_code' => str_random(2),
'city' => $faker->city,
'address' => $faker->buildingNumber . ' ' . $faker->streetName,
'suite' => $faker->secondaryAddress,
]
]
];
try {
//app(AddressesRequest::class);
$request = new AddressesRequest($param);
$request
->setContainer(app())
->setRedirector(app(Redirector::class))
->validateResolved();
} catch (ValidationException $ex) {
}
$this->assertFalse(isset($ex));
}
}
(using Zend Framework 2.2.4)
My validator factory, doesn't seem to "exist" at validation time. If I attempt to instantiate the validator from the controller in which the form is housed, it conversely works fine:
This works...
$mycustomvalidator = $this->getServiceLocator()
->get('ValidatorManager')
->get('LDP_PinAvailable');
Here's how things are set up otherwise in the code, I can't seem to find the problem, and was hopeful to avoid opening up ZF2 source to understand. By way of documentation, it seems right.
Module Config
public function getValidatorConfig()
{
return array(
'abstract_factories' => array(
'\LDP\Form\Validator\ValidatorAbstractFactory',
),
);
}
Factory Class
namespace LDP\Form\Validator;
use Zend\ServiceManager\AbstractFactoryInterface,
Zend\ServiceManager\ServiceLocatorInterface;
class ValidatorAbstractFactory implements AbstractFactoryInterface
{
public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
return stristr($requestedName, 'LDP_PinAvailable') !== false;
}
public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName)
{
// baked in for sake of conversation
$validator = new \LDP\Form\Validator\PinAvailable();
if( $validator instanceof DatabaseFormValidatorInterface )
$validator->setDatabase( $locator->get('mysql_slave') );
return $validator;
}
}
Custom Validator
namespace LDP\Form\Validator;
class PinAvailable extends \Zend\Validator\AbstractValidator implements DatabaseFormValidatorInterface
{
/**
* #var \Zend\Db\Sql\Sql
*/
private $database;
public function setDatabase( \Zend\Db\Sql\Sql $db )
{
$this->database = $db;
}
public function isValid( $value )
{
$DBA = $this->database->getAdapter();
// do the mixed database stuff here
return true;
}
}
Lastly, the form field validator config part of the array:
'pin' => array(
'required' => true,
'filters' => array(
array('name' => 'alnum'),
array('name' => 'stringtrim'),
),
'validators' => array(
array( 'name' => 'LDP_PinAvailable' )
),
),
),
Piecing it all together, the form loads, and when submitted, it does with the stack trace below:
2013-10-28T17:09:35-04:00 ERR (3): Exception:
1: Zend\Validator\ValidatorPluginManager::get was unable to fetch or create an instance for LDP_PinAvailable
Trace:
#0 /Users/Saeven/Documents/workspace/Application/vendor/zendframework/zendframework/library/Zend/ServiceManager/AbstractPluginManager.php(103): Zend\ServiceManager\ServiceManager->get('LDP_PinAvailabl...', true)
#1 /Users/Saeven/Documents/workspace/Application/vendor/zendframework/zendframework/library/Zend/Validator/ValidatorChain.php(82): Zend\ServiceManager\AbstractPluginManager->get('LDP_PinAvailabl...', Array)
The ValidatorPluginManager extends the Zend\ServiceManager\AbstractPluginManager. The AbstractPluginManager has a feature called "autoAddInvokableClass", which is enabled by default.
Basically, what this means, is that if the service name requested can't be resolved by the ValidatorPluginManager, it will then check if the name is a valid class name. If so, it will simply add it as an invokable class right there, on-demand, which of course means that it will never fall back to your abstract factory.
To circumvent this behavior, the easiest method is to simply make your abstract factory respond to service names that do not actually resolve to the actual class names.
See: AbstractPluginManager.php#L98-L100
Digging some more, I've found the problem. It distilled to these lines in Zend\Validator\ValidatorChain circa line 80:
public function plugin($name, array $options = null)
{
$plugins = $this->getPluginManager();
return $plugins->get($name, $options);
}
There was no plugin manager available in context.
It took about three seconds of Googling to find that I had to do this when I prepared the form in the controller:
$validators = $this->getServiceLocator()->get('ValidatorManager');
$chain = new ValidatorChain();
$chain->setPluginManager( $validators );
$form->getFormFactory()->getInputFilterFactory()->setDefaultValidatorChain( $chain );
Hopefully this helps someone else. You are able to use regular old classnames when setting it up this way, no need to warp the classnames.
In ZF3/Laminas, if a validator is registered as an invokable, you can call the validator in the getInputFilterSpecification() of your form, and no problem. If a validator is instantiated using a factory, you get into trouble. If I understand correctly, even if your form is registered like this
'form_elements' => [
'factories' => [
SomeForm::class => SomeFormFactory::class,
]
]
and your validator:
'validators' => [
'factories' => [
SomeValidator::class => SomeValidatorFactory::class,
]
]
you won't be instantiating the validator via factory. The reason is that the form factory (the one you get like $form->getFormFactory()) has an input filter factory and in there sits default validator chain. And this validator chain has no ValidatorManager attached. And without the ValidatorManager, the default chain cannot map the validator name to the validator factory.
To solve all this headache, in your controller factory do this:
$form->('FormElementManager')->get(SomeForm::class);
$form->getFormFactory()->getInputFilterFactory()
->getDefaultValidatorChain()->setPluginManager($container->get('ValidatorManager'));
and your troubles are over.