Form Serialization mit Ajax
Die Formulare in Symfony sind ab der Version 3.0.0 ein abgeschlossenes System: Entity + Formular + Template + Controller anlegen, um den Rest kümmert sich Symfony. Etwas komplexer wird die ganze Sache, wenn Formulardaten per XmlHttpRequest übergeben werden sollen, also ein Controller die Daten eines Formulars per POST erwartet. Dafür liefert Symfony leider bisher keine Hilfestellung. Hier also eine kleiner Guide für die Form Serialization mit Ajax.
Update: Dieser Beitrag ist etwas älter und wurde durch diesen Beitrag erweitert! Um das Konstrukt vollständig nachvollziehen zu können, sind trotzdem beide Beiträge empfehlenswert.
Form-Submit per JavaScript abfangen
var form = $('form'), form_name = form.attr('name'), target = form.attr('action') ; function unpackInput(field_name, form_name) { return field_name.replace(form_name, '').split('[').join('').split(']').join(''); } form.submit(function (e) { e.preventDefault(); // get the form content var form_content = {}; $.each(form.serializeArray(), function(i, field) { var field_name = unpackInput(field.name, form_name); if (field_name in form_content) { if ('string' === typeof form_content[field_name]) { form_content[field_name] = [form_content[field_name]]; } form_content[field_name].push(field.value); } else { form_content[field_name] = field.value; } }); // create a form callback thats readable by symfony var callback = {}; callback[form_name] = form_content; // send the data to symfony controller $.ajax({ url: target, method: "POST", data: callback, success: function (data) { // do something on success }, error: function (data) { // do something on failure } }); });
In diesem Fall wollen wir nicht, dass die Seite neu geladen wird. Stattdessen parsen wir alle Formelemente und senden diese per Ajax an das action
Attribut, welches im form
Tag steht. Symfony vergibt automatisch Namen-Attribute für alle Elemente innerhalb der Form und für das Formular selbst.
Zwei Controller für eine Funktion
Der Aufbau der Controller ist etwas komplexer geworden und doppelt einige Lines of Code, funktioniert aber recht einfach:
/** * Create the form to make a feature * @Route("/create", name="feature_create") * @return Response */ public function featureCreateAction() { // create a form $form = $this->createForm(FeatureForm::class, new FeatureEntity(), [ 'action' => $this->generateUrl('feature_create_exec'), ]); return $this->render('FeatureBundle::featureChange.html.twig', [ 'form' => $form->createView(), ]); }
Der erste Controller erstellt nur das Formular mit der leeren Entität. Wichtig hier ist die Zuteilung der action
, da Symfony unser eigentliches Ziel nicht kennen kann. Wurde das Formular dann abgesendet, sprechen wir einen Controller unter der Route feature_create_exec
an, der wie folgt aussieht:
/** * Create the form to create a feature * @Route("/create-exec/{featureID}", name="feature_create_exec") * @param Request $request * @return JsonResponse */ public function featureCreateExecAction(Request $request) { // was it sent with Ajax? if (false === $request->isXmlHttpRequest()) { return $this->json([ 'failure' => 'This request was not send from Ajax!', ]); } // create the form again $form = $this->createForm(FeatureForm::class, new FeatureEntity()); // the form has to be submitted manually $form->submit($request->request->get($form->getName())); if ($form->isSubmitted() && $form->isValid()) { // do whatever is needed // e.g. save data to DB return $this->json([ 'success' => 'The feature was created successfully', ]); } // form is not valid return $this->featureListAction([ 'failure' => 'The form data is not valid!', ]); }
Im JavaScript erhalten wir unter data.success
bzw. data.failure
eine Nachricht und können damit weitermachen. Und fertig ist die Form Serialization mit Ajax.
Fazit
Die Methode ist etwas aufwändiger, da hier auch JavaScript zum Einsatz kommen muss, um Formulardaten zu erhalten. Auch jQuery ist hier mit von der Partie. Mit Sicherheit lassen sich einige Funktionen noch besser bündeln und wiederverwenden. Um mit dem JavaScript noch etwas aufzuräumen, solltet ihr auch in diesem Beitrag weiter lesen.