使用 AJAX 保存来自 Symfony 4 表单的数据
Persisting data from a Symfony 4 form with AJAX
我正在使用 Symfony 表单修改我的 "Context" 实体,但我无法从 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');
但是也没用。
如果您有一些想法,我将不胜感激。谢谢!
查看
听起来你需要 serializeArray()
而不是你的 JS 中的 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 的标准函数。我建议按如下方式更新您的控制器功能:
我还没有对此进行测试,因此可能需要进行一些调整。我也不会说法语,所以我试图保留的一些评论和验证错误可能没有任何意义。
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 请求可以 return 常规和 AJAX 请求的相同模板。
- POST 请求可以是常规的,或者 AJAX 使用
serializeArray()
。
- 这允许验证表单,并且仅当指定的每个用户都被赋予有效 ID 并且存在时才保存更改。
- 当验证失败时,表单模板将被 return 编辑并标记验证错误。您还会看到是否提供了多个错误的用户 ID。
- 当验证通过时,JSON
['success' => "Ok"]
将到达,但如果您不想要表单,您可能希望将 addFlash()
调用更改为 redirectToRoute()
再次出现。
我正在使用 Symfony 表单修改我的 "Context" 实体,但我无法从 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');
但是也没用。 如果您有一些想法,我将不胜感激。谢谢!
查看
听起来你需要 serializeArray()
而不是你的 JS 中的 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 的标准函数。我建议按如下方式更新您的控制器功能:
我还没有对此进行测试,因此可能需要进行一些调整。我也不会说法语,所以我试图保留的一些评论和验证错误可能没有任何意义。
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 请求可以 return 常规和 AJAX 请求的相同模板。
- POST 请求可以是常规的,或者 AJAX 使用
serializeArray()
。 - 这允许验证表单,并且仅当指定的每个用户都被赋予有效 ID 并且存在时才保存更改。
- 当验证失败时,表单模板将被 return 编辑并标记验证错误。您还会看到是否提供了多个错误的用户 ID。
- 当验证通过时,JSON
['success' => "Ok"]
将到达,但如果您不想要表单,您可能希望将addFlash()
调用更改为redirectToRoute()
再次出现。