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.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.