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":{}}

If I do

$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, если вы не настроили его изначально.


person Ravioli87    schedule 29.11.2015    source источник
comment
Бьюсь об заклад (но я еще не уверен), что вам не нужны все эти пакеты. Вы объяснили, что хотите получать данные формы извне, но не сказали нам, в каком методе запроса (собственно метод POST) и в каком формате данных (только заголовки POST или JSON или XML)   -  person Frank B    schedule 29.11.2015


Ответы (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 '';
}

Or

$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;
}
person Isabek Tashiev    schedule 29.11.2015
comment
Все эти предложения в точку! Функция getFormErrors() очень полезна. Основная проблема заключалась в том, что комбинация CSRF и имени формы не передавалась как пустая. - person Ravioli87; 29.11.2015