Description
If I construct two objects Foo and Bar, such that:
class Foo
{
/** @var Bar */
public $bar;
}
class Bar
{
}
whereby Foo hasOne Bar, I would expect that a form constructed with a data_class of Foo would validate all properties of Foo which have constraints regardless of whether or not a default value for $bar is set. Instead, the form passes validation even if other fields in Foo which have constraints should fail, provided that I have set a default value for $foo->bar.
It's easier to demonstrate than explain:
https://github.com/jmcclell/recursive-validator-bug-test-case
In this repo, there is a single test class at src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php
with two test cases. One hits a route where I do not define a default value for Foo::bar and one where I do. Both test cases provide an empty request to the route. I am expecting a 400 response in both cases, but as you can see, where I set the default I get a 200 response, even though Foo::Baz does not have its constraints met.
This means that if I were to place business logic inside the Form::isVaid() conditional which depends on the entities being validated, I am going to get entities that I cannot trust to be valid if the client sends a malformed (in this case, empty) request.
Curiously, if I run the object through the Validator manually, it performs as expected and returns the proper list of validation errors. It appears to be specific to form validation.
I ran into this bug with a specific setup of mine, so you'll notice a few things:
- CSRF protection is disabled (my project is a RESTful API which is AJAX only)
- Forms have no names (my client does not wrap its request in a namespace)
- I am using submit() vs handleRequest() because in my specific case I need to strip out extra fields because forms do not support extra fields yet
- I am using annotated constraints, in the test case you can find them in the
src/Acme/DemoBundle/Entity
folder for both theFoo
andBar
entites