Symfony2 FOSRestBundle PUT Action FORM returns empty results
Asked Answered
E

4

8

I am using Symfony 2.2 and the latest version of FOSRestBundle. So I have manage to make most of the actions work but I seem to have an issue with the FormBuilder that I am passing the Request of my PUT call in.

I have checked the request object and it comes from my Backbone.je model as it should (.save()) But after binding to the form the entity comes back with only the id which causes flush() to throw an error since required fields are not filled.

The action in the Controller:

header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS ');
header('Allow GET, POST, PUT, DELETE, OPTIONS ');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, *');

use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Routing\ClassResourceInterface;
use FOS\Rest\Util\Codes;
use Symfony\Component\HttpFoundation\Request;
use Greenthumbed\ApiBundle\Entity\Container;
use Greenthumbed\ApiBundle\Form\ContainerType;

class ContainerController extends FOSRestController implements ClassResourceInterface
{
/**
 * Put action
 * @var Request $request
 * @var integer $id Id of the entity
 * @return View|array
 */
public function putAction(Request $request, $id)
{
    $entity = $this->getEntity($id);
    $form = $this->createForm(new ContainerType(), $entity);
    $form->bind($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();

        return $this->view(null, Codes::HTTP_NO_CONTENT);
    }

    return array(
        'form' => $form,
    );
}

/**
 * Get entity instance
 * @var integer $id Id of the entity
 * @return Container
 */
protected function getEntity($id)
{
    $em = $this->getDoctrine()->getManager();

    $entity = $em->getRepository('GreenthumbedApiBundle:Container')->find($id);

    if (!$entity) {
        throw $this->createNotFoundException('Unable to find Container entity');
    }

    return $entity;
}

The Form that is called:

namespace Greenthumbed\ApiBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class ContainerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('description')
            ->add('isVisible')
            ->add('type')
            ->add('size')
            ->add('creationDate')
            ->add('userId')
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Greenthumbed\ApiBundle\Entity\Container',
            'csrf_protection' => false,
        ));
    }

    public function getName()
    {
        return 'greenthumbed_apibundle_containertype';
    }
}

I have tried everything so far but I am fairly new with Symfony and I cannot understand why the $entity does not contain the values received by the request.

FYI: I have tried doing it manually as in instantiating a Container class with the ID of the request and putting use the setters to input values into it and it works just fine, I just want to do things the right way as Symfony suggests it should be done.

Thank you very much in advance.

Estrade answered 7/6, 2013 at 4:58 Comment(2)
Why are you using a form type within the put action? You would need just a validator there for server side validation. Your client side application should deal with form stuff. However do a var_dump($request) or use the debug toolbar to see if the values are actually send.Rhineland
did you manage to solve it????, i'm having the same issue :OCountervail
V
8

Try changing the following line in putAction:

$form = $this->createForm(new ContainerType(), $entity);

to:

$form = $this->createForm(new ContainerType(), $entity, array('method' => 'PUT'));
Vinni answered 23/7, 2013 at 1:36 Comment(4)
Worked for me with Symfony 2.6 and FosRestBundle 1.5. Where in the documentation is it mentioned?Dialyze
same issue, how you fixed?Bunni
I am using Symfony 2.7 and FosRestBundle 1.7. My form if ($form->isValid()) { was FALSE and $errors = $form->getErrors(); was empty. Changing method type to 'PUT' fixed the problem.Ingunna
+1 I was having problems with PUT requests in Symfony 2.8.2 and FOSRestBundle 1.7 - this fixed it for me.Hyo
F
6

I think you are experiencing the same problem I had: The mistake is in the name of the Form.

In your Form definition the name is "greenthumbed_apibundle_containertype". public function getName() { return 'greenthumbed_apibundle_containertype'; }

So to bind a request to this form the json should have looked like this:

{"greenthumbed_apibundle_containertype": [{"key": "value"}]}

Since Backbone .save() method ship this kind of json

{"key":"value","key2":"value2"}

you have to remove the name from the Form:

public function getName()
{
    return '';
}

In general if you want to post a json with a placeholder like

"something":{"key":"value"}

your form name must be exactly "something"

from my own question here

Fibril answered 14/12, 2013 at 15:19 Comment(0)
H
0

Use the ParamConverter to have your Entity injected as an argument in your method automatically.

use Greenthumbed\ApiBundle\Entity\Container;

// ...

public function putAction(Request $request, Container $container)
{
    $form = $this->createForm(new ContainerType(), $container);
    $form->bind($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();

        // Entity already exists -> no need to persist!
        // $em->persist($entity);   

        $em->flush();

        return $this->view(null, Codes::HTTP_NO_CONTENT);
    }

    return array('form' => $form);
}
Harkey answered 9/6, 2013 at 5:21 Comment(0)
P
0

see http://symfony.com/doc/current/cookbook/routing/method_parameters.html

Unfortunately, life isn't quite this simple, since most browsers do not support sending PUT and DELETE requests. Fortunately Symfony2 provides you with a simple way of working around this limitation. By including a _method parameter in the query string or parameters of an HTTP request, Symfony2 will use this as the method when matching routes. Forms automatically include a hidden field for this parameter if their submission method is not GET or POST. See the related chapter in the forms documentation for more information.

and http://symfony.com/doc/current/book/forms.html#book-forms-changing-action-and-method

If the form's method is not GET or POST, but PUT, PATCH or DELETE, Symfony2 will insert a hidden field with the name "_method" that stores this method. The form will be submitted in a normal POST request, but Symfony2's router is capable of detecting the "_method" parameter and will interpret the request as PUT, PATCH or DELETE request. Read the cookbook chapter "How to use HTTP Methods beyond GET and POST in Routes" for more information.

Princess answered 28/11, 2013 at 19:23 Comment(2)
I fail to see how this presents a solution to the problem.Subsume
this explains why you have to put "method" => "put" as param in the create form, to simulate the "put" behaviour in webbrowsers....Princess

© 2022 - 2024 — McMap. All rights reserved.