While experimenting with building an API based upon the FOSRestBundle, I hit a spot of bother trying to bind a simple request to a form.
The form had three fields, username, email and password, matching the properties of its data class. I was testing it using the Postman extension for Chrome, sending POST requests to a /users URI (the Restful way of creating a new user). Here’s the request:
username => myusernameemail => firstname.lastname@example.org => 12345
In the controller, I bound the request to the form as usual:
After which $form->isValid() promptly failed with an empty $form->getErrors().
I spent a good chunk of time hunting down validation problems in the User entity annotations, experimenting with the CSRF token requirement, disabling the JMSSerializerBundle exclusion policies for the User entity and praying to the Gods of the Source. To no avail. I could only get the form to work by manually binding each field:
$form->bind(array('username' => $this->getRequest()->get('username'),'email' => $this->getRequest()->get('email'),'password' => $this->getRequest()->get('password'),));
By then I was pretty sure this was deep facepalm country. I find the best way to handle these kinds of situations is to take a break, so I carefully closed the laptop, called it a night and walked away.
Sure enough, this morning the solution hit me as soon as I looked at the code. Quite simply, Symfony expects my POST request to have a top-level namespace matching the form’s name. Given the form had the name ‘user’, it was a simple matter of tweaking the request:
user[username] => myusernameuser[email] => email@example.com[password] => 12345
Done and done.
It’s hard to draw a lesson here. On one hand, this wouldn’t have happened if I was using a form rendered by Symfony rather than crafting my request by hand. Since the manual assumes you’ll be doing just that, it doesn’t bother too much with explaining how things work under the veneer of the helpers and that makes life difficult when building an API that’s not going to be consumed by something built with Symfony. On the other hand, maybe I could have started my debugging by having symfony build a valid form and looking at the request it produced. Then again, hindsight is 20/20.
I guess the only sure conclusion is that, yet again, walking away from the code and coming back well rested works wonders.