使用AJAX持久化Symfony 4表单中的数据

使用AJAX持久化Symfony 4表单中的数据,ajax,forms,symfony,json-deserialization,Ajax,Forms,Symfony,Json Deserialization,我正在使用Symfony表单修改我的“上下文”实体,为了修改我的实体,从ajax请求获取数据时遇到了一个问题。我找到了另一种方法来做我需要的事情(如下),但我没有使用HandlerRequest: ContextController: public function share(Request $request, Context $context, UsersRepository $usersRepository) { $users = $usersReposit

我正在使用Symfony表单修改我的“上下文”实体,为了修改我的实体,从ajax请求获取数据时遇到了一个问题。我找到了另一种方法来做我需要的事情(如下),但我没有使用HandlerRequest:

ContextController:

    public function share(Request $request, Context $context, UsersRepository $usersRepository)
    {
        $users = $usersRepository->findAll();
        $form = $this->createForm(ShareContextType::class, $context, ['users' => $users]);

        // Envoie du formulaire de partage de contexte en ajax
        if ($request->isXmlHttpRequest() && $request->isMethod('GET')) {
            $template = $this->render('context/share.html.twig', [
                'form' => $form->createView(),
            ])->getContent();
            $json = json_encode($template);

            return new JsonResponse($json);

        // Requête post avec les id's des utilisateurs pour le partage de context
        } elseif ($request->isXmlHttpRequest() && $request->isMethod('POST')) {
            $reponse = $request->getContent();
            $json = json_decode($reponse);
            $formArray = [];

            // Ajoute chaque utilisateur aux tableaux d'utilisateurs de ce contexte
            foreach ($json as $userId) {
                $user = $usersRepository->find($userId);
                $context->addUser($user);
                $formArray[] = $user;
            }

            // Si l'utilisateur n'est pas présent dans le tableaux de la requête
            // alors supprime l'utilisateur du tableau du contexte
            foreach ($context->getUsers() as $user) {
                if (!in_array($user, $formArray, true)) {
                    $context->removeUser($user);
                }
            }
            $this->getDoctrine()->getManager()->flush();

            return new JsonResponse(['success' => 'Ok']);
        }
    $('#share-context-form').submit(function(e) {
        e.preventDefault();
        formData = $('#share_context_users').val();
        sendShareContext(formData, $('#context-id').val());
    })

function sendShareContext(formData, contextId) {
    $.ajax({
        type: 'POST',
        url: '/contextes/' + contextId + '/share',
        data: JSON.stringify(formData),
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json'
        },
        async: true,

(...)
我的JS:

    public function share(Request $request, Context $context, UsersRepository $usersRepository)
    {
        $users = $usersRepository->findAll();
        $form = $this->createForm(ShareContextType::class, $context, ['users' => $users]);

        // Envoie du formulaire de partage de contexte en ajax
        if ($request->isXmlHttpRequest() && $request->isMethod('GET')) {
            $template = $this->render('context/share.html.twig', [
                'form' => $form->createView(),
            ])->getContent();
            $json = json_encode($template);

            return new JsonResponse($json);

        // Requête post avec les id's des utilisateurs pour le partage de context
        } elseif ($request->isXmlHttpRequest() && $request->isMethod('POST')) {
            $reponse = $request->getContent();
            $json = json_decode($reponse);
            $formArray = [];

            // Ajoute chaque utilisateur aux tableaux d'utilisateurs de ce contexte
            foreach ($json as $userId) {
                $user = $usersRepository->find($userId);
                $context->addUser($user);
                $formArray[] = $user;
            }

            // Si l'utilisateur n'est pas présent dans le tableaux de la requête
            // alors supprime l'utilisateur du tableau du contexte
            foreach ($context->getUsers() as $user) {
                if (!in_array($user, $formArray, true)) {
                    $context->removeUser($user);
                }
            }
            $this->getDoctrine()->getManager()->flush();

            return new JsonResponse(['success' => 'Ok']);
        }
    $('#share-context-form').submit(function(e) {
        e.preventDefault();
        formData = $('#share_context_users').val();
        sendShareContext(formData, $('#context-id').val());
    })

function sendShareContext(formData, contextId) {
    $.ajax({
        type: 'POST',
        url: '/contextes/' + contextId + '/share',
        data: JSON.stringify(formData),
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json'
        },
        async: true,

(...)
它工作得很好。但如果我尝试在JS中执行以下操作:

$formData = $('#share-context-form').serialize();
并让控制器中的序列化对象执行以下操作:

$form->handleRequest($request);
if ($form->isValid()) {
(...)
}
$request->getContent() // Output : "share_context%5Busers%5D%5B%5D=25&share_context%5B…en%5D=vrWCjceU9LVGVrSKyMggRdXNNjeG4KTBDLW0HqIh3aQ"
它不起作用,因为当我尝试获取$request内容时,实际上会得到如下结果:

$form->handleRequest($request);
if ($form->isValid()) {
(...)
}
$request->getContent() // Output : "share_context%5Busers%5D%5B%5D=25&share_context%5B…en%5D=vrWCjceU9LVGVrSKyMggRdXNNjeG4KTBDLW0HqIh3aQ"
我还尝试使用Symfony的SerializerInterface对其进行反序列化,具体操作如下:

$serializer->deserialize($form, ShareContextType::class, 'json');
但它也不起作用。 如果你有什么想法,我将不胜感激。谢谢

看法 听起来您需要在JS中使用
serializeArray()
而不是
serialize()
。例如:

$('#share-context-form').submit(function(e) {

    e.preventDefault();

    $.post('/contextes/' + $('#context-id').val() + '/share', $(this).serializeArray(), function(data, textStatus, xhr) {
        // Success handler
    }).fail(function(xhr, textStatus, errorThrown) {
        // Fail handler (optional)
    }).always(function() {
        // Finished handler, success or fail
    });

});

对于此类提交,您可能希望a)验证上下文ID是否存在,以及b)防止意外双击:

var shareContextFormSubmitting = false;

$('#share-context-form').submit(function(e) {

    e.preventDefault();

    if (shareContextFormSubmitting) {
        // Optional: Prevents multiple forms submitting in parallel (blocks double click mistake)
        return false;
    }

    var contentId = $('#context-id').val();
    if (typeof contentId != "number" || isNaN(contentId) || contentId < 1) {
        // Do some alert or whatev because contentId is empty, I'm guessing you expect this to be an integer at least 1
        return false;
    }

    shareContextFormSubmitting = true;
    $.post('/contextes/' + contentId + '/share', $(this).serializeArray(), function(data, textStatus, xhr) {
        // Success handler
    }).fail(function(xhr, textStatus, errorThrown) {
        // Fail handler (optional)
    }).always(function() {
        shareContextFormSubmitting = false;
    });

});
jQuery采用一个表单,并以“application/x-www-form-urlencoded”编码为常规HTTP POST请求准备一个变量。(无关:这就是为什么不能使用
serializeArray()
上载文件字段。)

您试图将其理解为JSON,但实际上它是一个字符串“application/x-www-form-urlencoded”。你只需要使用Symfony的标准函数就可以了。我建议如下更新控制器功能:

我还没有对此进行测试,因此可能需要进行一些调整。我也不会说法语,所以我试图保留的一些评论和验证错误可能没有完全意义

使用Symfony\Component\Form\FormError;
公用函数共享(请求$Request,上下文$Context,用户还原$UsersRepository)
{
$users=$usersRepository->findAll();
$form=$this->createForm(ShareContextType::class,$context,['users'=>$users]);
$form->handleRequest($request);
//你的标准是什么
如果($form->isSubmitted()){
//利用率表
$formArray=[];
$usersField=$form->get('users');
$usersSubmitted=$usersField->getData();
if(is_数组($usersSubmitted)和计数($usersSubmitted)>=1){
foreach($users提交为$i=>$userId){
如果(($userId=intval($userId))<1){
$usersField->addError(新的FormError(sprintf(“ID-usilizateur%d非有效)”,($i+1));
继续;
}
如果(!($user=$usersRepository->find($userId))){
$usersField->addError(新的FormError(sprintf(“L'usitateur%d avec L'ID#%d est introvable..”,($i+1),$userId));
继续;
}
$context->addUser($user);
$formArray[]=$user;
}
}否则{
$USERSFELD->ADERROR(新格式错误(“使用非指定用途”);
}
//注册者协会(Enregister uniquement s'il n'y a pas eu d'erreurs)
如果($form->isValid()){
//这是一个需求表,是对内容表的补充
foreach($context->getUsers()作为$user){
if(!in_数组($user,$formArray,true)){
$context->removeUser($user);
}
}
$this->getDoctrine()->getManager()->flush();
return$request->isXmlHttpRequest()?new JsonResponse(['success'=>'Ok']):$this->addFlash('success','Enregistréavec success');
}
}
//AJAX标准内容部分公式化特使
$responseContent=$this->renderView('context/share.html.twig'[
'form'=>$form->createView(),
]);
return$request->isXmlHttpRequest()?新JsonResponse(json_encode($responseContent)):新响应($responseContent);
}
这样做的好处:

  • GET请求可以为常规请求和AJAX请求返回相同的模板
  • POST请求可以是常规的,也可以是使用
    serializeArray()
    的AJAX
  • 这允许对表单进行验证,并且仅当为每个指定的用户提供了有效ID且该用户存在时,才保存更改
  • 验证失败时,表单模板将返回并标记验证错误。您还将看到是否提供了多个错误的用户ID
  • 当验证通过时,JSON将到达
    ['success'=>“Ok”]
    ,但如果不希望表单再次出现,您可能需要将
    addFlash()
    调用更改为
    redirectToRoute()

非常感谢@Adambean给出的详细答案,我将尝试用您的示例改进我的代码。但是,我也尝试在我的$form内容上使用.serializeArray(),但是如果我在控制器上使用json解码,我会得到一个类似“share_context%5Busers%5D%5B%5D=25&share_context%5B…en%5D=VRWCJCEU9LVGVRSkyMggrdxNjeg4ktbdlW0HQIH3AQ”的字符串,或者null。这是正确的,但您需要在常规的“application/x-www-form-urlencoded”中读取帖子内容方法,使用
$request->request->get('users')
,或
$form->handleRequest($request)
。我建议您使用后者,因为它可以让您更轻松地进行表单验证我用一个控制器示例编辑了我的答案。