【问题标题】:Persisting data from a Symfony 4 form with AJAX使用 AJAX 持久化 Symfony 4 表单中的数据
【发布时间】:2020-05-11 11:02:34
【问题描述】:

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

上下文控制器:

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

我的 JS:

    $('#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();

并让我的 Controller 中的序列化对象执行以下操作:

$form->handleRequest($request);
if ($form->isValid()) {
(...)
}

它不起作用,因为当我尝试获取 $request 内容时,我实际上得到了类似的东西:

$request->getContent() // Output : "share_context%5Busers%5D%5B%5D=25&share_context%5B…en%5D=vrWCjceU9LVGVrSKyMggRdXNNjeG4KTBDLW0HqIh3aQ"

我也尝试用 Symfony 的 SerializerInterface 反序列化它,这样做:

$serializer->deserialize($form, ShareContextType::class, 'json');

但它也不起作用。 如果您有一些想法,我将不胜感激。谢谢!

【问题讨论】:

    标签: ajax forms symfony json-deserialization


    【解决方案1】:

    查看

    听起来您在 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;
        });
    
    });
    

    控制器

    问题也出在你的控制器上:

    // 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 = [];
    

    jQuery 的serializeArray() 采用“application/x-www-form-urlencoded”编码格式并为常规 HTTP POST 请求准备一个变量。 (无关:这就是为什么您不能使用serializeArray() 上传文件字段的原因。)

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

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

    use Symfony\Component\Form\FormError;
    
    public function share(Request $request, Context $context, UsersRepository $usersRepository)
    {
        $users = $usersRepository->findAll();
        $form = $this->createForm(ShareContextType::class, $context, ['users' => $users]);
        $form->handleRequest($request);
    
        // Gérer la soumission du formulaire, que ce soit AJAX ou standard
        if ($form->isSubmitted()) {
            // Ajoute chaque utilisateur aux tableaux d'utilisateurs de ce contexte
            $formArray = [];
            $usersField = $form->get('users');
            $usersSubmitted = $usersField->getData();
    
            if (is_array($usersSubmitted) && count($usersSubmitted) >= 1) {
                foreach ($usersSubmitted as $i => $userId) {
                    if (($userId = intval($userId)) < 1) {
                        $usersField->addError(new FormError(sprintf("ID utilisateur %d non valide.", ($i + 1))));
                        continue;
                    }
    
                    if (!($user = $usersRepository->find($userId))) {
                        $usersField->addError(new FormError(sprintf("L'utilisateur %d avec l'ID # %d est introuvable..", ($i + 1), $userId)));
                        continue;
                    }
    
                    $context->addUser($user);
                    $formArray[] = $user;
                }
            } else {
                $usersField->addError(new FormError("Utilisateurs non spécifiés."));
            }
    
            // Enregistrer uniquement s'il n'y a pas eu d'erreurs
            if ($form->isValid()) {
                // 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 $request->isXmlHttpRequest() ? new JsonResponse(['success' => 'Ok']) : $this->addFlash('success', 'Enregistré avec succès.');
            }
        }
    
        // Envoyez le formulaire de partage de contexte en AJAX ou en standard
        $responseContent =  $this->renderView('context/share.html.twig', [
            'form' => $form->createView(),
        ]);
    
        return $request->isXmlHttpRequest() ? new JsonResponse(json_encode($responseContent)) : new Response($responseContent);
    }
    

    这样做的好处:

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

    【讨论】:

    • 非常感谢@Adambean 的详细回答,我会尝试用您的示例改进我的代码。但是,我也尝试在我的 $form 内容上使用 .serializeArray() ,但我得到了一个类似 "share_context%5Busers%5D%5B%5D=25&share_context%5B...en%5D=vrWCjceU9LVGVrSKyMggRdXNNjeG4KTBDLW0HqIh3aQ" 或 null 如果我在控制器上使用 json_decode。
    • 没错,但您需要以常规的“application/x-www-form-urlencoded”方式读取 POST 内容,使用$request-&gt;request-&gt;get('users')$form-&gt;handleRequest($request)。我建议使用后者,因为它可以让您更轻松地进行表单验证。 -- 我已经用控制器示例编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 2014-11-20
    • 2014-08-25
    相关资源
    最近更新 更多