Symfony2 REST API - Partial update - php

I'm building a REST API with FOSRestBundle in Symfony2. I'm using forms to create and update entities with Doctrine2. Everything works fine if I send all form fields. Example:
{"first_name":"Pi","last_name":"Wi"}
The person is inserted fine but now I want to update only the last name.
{"last_name":"Wi"}
The problem is that the first name is empty after the update because the form updates the entity with an "null" value (because it isn't given). Is it possible to just update the last name and ignore the first name?

Sure, it's possible.
First, in terms of RESTful that would be a PATCH request, so if you're using the ClassResourceInterface based controller approach, you'll have to add a patchAction method in your controller.
Then, when processing a submitted form, you'll need to pass a false $clearMissing option to your form's submit method call in the controller, like this:
<?php
// in your controller's patchAction:
/** #var \Symfony\Component\Form\FormInterface $form */
/** #var \Symfony\Component\HttpFoundation\Request $request */
$form->submit($request, false);
This will tell the form component to only update the fields passed from the form, without clearing the missing fields (as the parameter's name says). See the source code for reference.
Note though, that passing a Request to a FormInterface::submit() method will be deprecated as of Symfony 3.0, so this answer is for Symfony 2.x.

Related

Symfony2 Validating submitted data against Model class

I'm trying to validate submitted data against existing Model/Entity/POPO, however I can't get it to work in any simple way.
All of this is takes place inside a controller action.
So, I can do like this:
$constraints = new Assert\Collection([
'username' => [new Assert\NotBlank()],
'email' => [new Assert\Email()],
]);
$violationList = $this->get('validator')->validate($request->request->all(), $constraints);
However to do that in every action makes no sense, as having all constraints in a class would be a lot better. So, Validation component allows to do like this:
// All constraints are defined inside Customer class
$customer = new Customer();
$violationList = $this->get('validator')->validate($customer);
Violation list is full of errors now, as $customer is an empty object, but the problem is I can't find a way to use data from POST AND validate it against constraints that are defined in the class.
It is possible to write extra component/helper that would take POST data and then will call bunch of ->setUsername(), ->setEmail(), etc., but that doesn't seem right considering you can easily map Model to POST data, if:
Form component is involved;
OR using ConstraintsCollection manually;
Am I missing something obvious here or there is no out-of-the-box possibility? Thanks!
AFAIK the form component is the one responsible for mapping post data to your entity. So you have two choices
Use a form, like that you will have your data mapped and your model validated
Skip the form but then you have to map request params to your entity manually. then validate your model with $this->get('validator')->validate($customer);
Edit :
The form role is to map data coming from request ( html form , api .... ) to a model. Validation could be done with from or without it as its the validator component who does the job , it should be noted that the validation is done on the model and not the form.
If you want to skip the form check this question: Populate entity from data array without form/request although the form component is very useful specially if you are using the same logic in many places ( create / edit .. )

Using custom ParamConverter for POST Request in Symfony 2

I'm using Symfony 2.6 and the FOS Rest Bundle.
Param converters for PATCH , DELETE and GET requests work nicely and reduce the code in the controller actions. However for POST requests I have a problem. The default \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter gets called every time. This results in an exception:
Unable to guess how to get a Doctrine instance from the request information.
I checked the \Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener and saw that it's always including the Doctrine param converter in the onKernelController method. From the documentation it seems that the doctrine param converter is automatically applied for all type hinted controller actions unless you set it to off:
sensio_framework_extra:
request:
converters: true
auto_convert: false
I found a kind of hacky way to prevent this. The array of param converters to be applied will be indexed by the name of the type hinted argument in the controller method (which symfony gets by reflection). If I just name my param converter the same as this type hint then the default doctrine param converter will not be added to the list of param converters. For example:
...
* #ParamConverter(
* "MyEntity",
* class="Foo\Bar\MyEntity",
* converter="my_custom_converter"
* )
*
* #param MyEntity $myEntity
* #return MyEntity
*/
public function postMyEntityAction(MyEntity $myEntity)
{
I sort of wrote this question as I was digging deeper into the code and I'm not even really sure what my question is anymore. I guess it's "Is it logical to apply multiple param converters?" or would also like to know if it's possible to turn off param converters for certain actions. Maybe my logic is completely wrong here and this isn't what param converters were intended for.
I'd appreciate any advice.
Alright, I realized where I was going wrong. It was a simple case of not returning true from my custom paramConverter apply method. If it does return true then the doctrine param converter won't be applied.

How can I get the form data from the FOSUserBundle registration form?

I am using the FOSUserBundle and have overwritten the RegistrationController. When the form is submitted and valid, I want to get the email address the user entered in the registration form.
But I don't see any way to get it. As taken from the Symfony2 forms documentation, you can get form data like this:
$this->get('request')->request->get('name');
But the RegistrationController does not know the get() method (as it's not inherited from the Symfony2 controller entity). So I could go like this:
// Note the ...->container->...
$this->container->get('request')->request->get('name');
But this returns NULL. Now I try to get it from the $form.
// Does contain a lot of stuff, but not the entered email address
$form->get('email');
// Does also contain a lot of stuff, but not the desired content
$request->get('email');
$request->request('email');
// Throws error message: No method getData()
$request->getData();
Any idea?
It's really, really simple. You create a form with related entity. In FOSUserBundle you should have a RegistrationFormHandler, and in process method you've got:
$user = $this->createUser();
$this->form->setData($user);
if ('POST' === $this->request->getMethod()) {
$this->form->bind($this->request);
if ($this->form->isValid()) /**(...)**/
After the line $this->form->bind($this->request) every value in $user object is overwritten by data from form. So you can use $user->getEmail().
On the other hand you are able to get data directly from request, but not by the property name, but by form name. In FOSUserBundle registration form it is called fos_user_registration - you can find it in FOS/UserBundle/Form/Type/RegistrationFormType.php in getName method.
You get it by:
$registrationArray = $request->get('fos_user_registration');
$email = $registrationArray['email'];
If you were to use Controller as a Service (which should work with this), you could pass the RequestStack (sf >=2.4) in the constructor and do $this->request_stack->getCurrentRequest()->get();
My guess is that you are trying to get the POST data. You are trying to put the data in a form object I presume. I would recommend you to take a look at: http://symfony.com/doc/current/book/forms.html if you have a custom form.
As regarding to your question, the form is probably containing a name. If you want to have direct access to it instead of doing things in your form, you need to fetch it multilevel if directly via the true at $deep, get('registration_form_name[email]', null, true); You can also do $email = $request->get('registration_form_name')['email']; (if you have php 5.4+)

How to populate a form with a populated Entity in symfony

I have an EntityType with an embedded EntityType collection. When I create a form and pass an empty instance of the related object everything works as expected.
However if I populate an object using Doctrine and pass this to the form I get a viewData error.
I have put alot more detail in a previous question and the answer given to this put me on the right path - almost.
It seems using #parameter annotation will fill the blanks in for me.
I would like to understand how I can do this myself in code e.g sudo code:
populatedEntity = Doctrine->getEntity
form = (entityType,populatedEntity)
Doing it this way throws an viewData error. I have set the class_data in the EntityType to that of the Entity
This works:
emptyEntity = new Entity
form = (entityType,emptyEntity)

removing the form name from post field in symfony2

I'm trying to use symfony2 to create a web service. I'd like the webservice to be structured and listen for:
POST to /teams/list with params key1=value and key2=value2
For validation purposes, i've created a TeamForm object and a TeamFormModel to validate the data against (using annotations). The problem i'm having is that the form is looking for team[key1] and team[key2] instead of just key1 and key2 to bind to the TeamFormModel.
Is there a way to configure the form to not use the team[*]?
If you are using the 2.1 branch, it's easy you can simply create a form with an empty name.
$form = $this->get('form.factory')->createNamed(
'', // the name
new TeamType(), // the type
$team // the data
);
$form->bindRequest($request);
So it will work as you are expecting.
But if you are using the 2.0 branch, from what I know, it's not supported and you have to do the binding manually:
$form = $this->createForm(new TeamType(), $team);
$from->bind($request->request->all());
You can validate entity without creating form. You can create entity object from POST data and pass it to validator. See validation section of the cookbook.
If you don't like to create entity object from request parameters every time then you can post data in json or xml format and then de-serialize into entity object using JMSSerializerBundle.

Categories