Symfony2: Сохранить запись из представления формы Ajax

Я полностью потерян. Есть только так много документации, которую можно прочитать, прежде чем все станет бессмысленным.

Я хочу иметь возможность сохранять данные формы, переданные извне моего приложения Symfony. Я уже установил FOSRestBundle, JMSSerializerBundle, NelmioCorsBundle и т. Д.

Во-первых, у меня есть FormType, который выглядит следующим образом:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('title')
        ->add('requestDate')
        ->add('deliverDate')
        ->add('returnDate')
        ->add('created')
        ->add('updated')
        ->add('contentChangedBy')
    ;
}

Затем у меня есть REST-контроллер, содержащий метод POST, который должен хранить новую запись:

class AvRequestController extends Controller
{
    ...
    public function postAvrequestAction(Request $request){
        $entity = new AvRequest();


        $form = $this->createForm(new AvRequestType(), $entity);
        $form->handleRequest($request);

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

            return new \Symfony\Component\HttpFoundation\JsonResponse($entity, Codes::HTTP_CREATED);
        }

        return new \Symfony\Component\HttpFoundation\JsonResponse($request, 400); 
    }
}

Вот тест с данными фиктивной формы ajax:

$('#postform').submit(function(event){
    event.preventDefault();
    console.log("submitted");

    ajaxObject = {
        url: $("#postform").attr("action"),
        type: 'POST', // Can be GET, PUT, POST or DELETE only
        dataType: 'json',
        xhrFields: {
            withCredentials: true
        },
        crossDomain: true,
        contentType: "application/json; charset=UTF-8",
        data: JSON.stringify({"id":2, "title":"billabong", "requestDate":"2000-01-01 11:11:11", "deliverDate": "2000-01-01 11:11:11", "returnDate": "2000-01-01 11:11:11", "created": "2000-01-01 11:11:11", "updated": "2000-01-01 11:11:11", "content_changed_by":"cpuzzuol"})
    };

    // ... Add callbacks depending on requests

    $.ajax(ajaxObject)
        .done(function(data,status,xhr) {
            console.log( two );
        })
        .fail(function(data,status,xhr) {
            console.log( status );
        })
        .always(function(data,status,xhr) {
            console.log( data );
        });


    console.log("END");
});

Когда я отправляю форму, в моем методе POST срабатывает 400 Bad Request. Хуже, моя сумка $request всегда пуста:

{"attributes":{},"request":{},"query":{},"server":{},"files":{},"cookies":{},"headers":{}}

Если я сделаю

$request->getContent()

Я получаю свои строковые данные:

"{\u0022id\u0022:2,\u0022title\u0022:\u0022billabong\u0022,\u0022requestDate\u0022:\u00222000-01-01 11:11:11\u0022,\u0022deliverDate\u0022:\u00222000-01-01 11:11:11\u0022,\u0022returnDate\u0022:\u00222000-01-01 11:11:11\u0022,\u0022created\u0022:\u00222000-01-01 11:11:11\u0022,\u0022updated\u0022:\u00222000-01-01 11:11:11\u0022,\u0022content_changed_by\u0022:\u0022cpuzzuol\u0022}"

Я читал, что это может иметь какое-то отношение к "слушателю тела" FOSRestBundle, но я уже включил это:

body_listener: true 

ОБНОВИТЬ

body_listener, кажется, не играет роли вообще. Как указано в ответе ниже, вы должны создать форму с пустым именем, поскольку форма, которую вы отправляете из-за пределов системы, не будет иметь имени, которое она обычно имела бы, если бы она была сделана внутри Symfony. Кроме того, обязательно отключите функцию CSRF, если у вас ее нет.

1 ответ

Решение

Форма isValid проверяет также на CSRF token validation, Вы можете выключить csrf token validation в AvRequestType,

//...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\AvRequest',
        'csrf_protection' => false
    ));
}
//...

Кроме того, я предлагаю, чтобы ваша форма name, isValid также проверяет ваш form name,

// form without name
public function getName()
{
    return '';
}

Или же

$form = $this->get('form.factory')->createNamed('', new AvRequestType(), $avRequest);

Если вы хотите создать сущность, вы должны отправить data без идентификатора (от JS).

Я использовал "сериализатор JMS" для сериализации моей сущности в json. // контроллер

public function postAvRequestAction(Request $request)
{
    $avRequest = new AvRequest();

    $form = $this->createForm(new AvRequestType(), $avRequest);
    $form->handleRequest($request);

    $form = $this->get('form.factory')->createNamed('', new AvRequestType(), $avRequest);

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

        $serializer = $this->get('serializer');
        $serialized = $serializer->serialize($avRequest, 'json');
        return new Response($serialized);
    }

    return new JsonResponse(array(
        'errors' => $this->getFormErrors($form)
    ));
}

protected function getFormErrors(Form $form)
{
    $errors = array();

    foreach ($form->getErrors() as $error) {
        $errors['global'][] = $error->getMessage();
    }

    foreach ($form as $field) {
        if (!$field->isValid()) {
            foreach ($field->getErrors() as $error) {
                $errors['fields'][$field->getName()] = $error->getMessage();
            }
        }
    }

    return $errors;
}
Другие вопросы по тегам