简体   繁体   中英

Symfony2 FOSRestBundle PUT Action FORM returns empty results

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.

Try changing the following line in putAction:

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

to:

$form = $this->createForm(new ContainerType(), $entity, array('method' => 'PUT'));

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

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);
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM