Pipit: pipit_form_response()

The function returns a JSON response detailing whether a form submission was successful and if not, what the errors are. This is useful for submitting forms via Javascript.

pipit_form_response($formID, $opts, $return);

Parameters:

Type Description
String The form ID. This should match the ID in your perch:form tag
Array Options array, see below
Boolean Set to true to have the value returned instead of echoed.

Options array:

Option Value
app The Perch app(s) that should handle the form submission
template The Perch template that should be used to validate the submission
dispatch Boolean. Set to true for the Pipit app to pass the form submission to Perch. Useful when submitting from a non-Perch-templated form, to a Runway endpoint or when submitting the data as JSON.

Responses

The function returns a JSON response like this:

{
    "status": 200,
    "errors": false
}
{
    "status": 422,
    "errors": { 
        "name": {"type": "required"},
        "email": {"type": "format"}
    }
}

If you turn on Perch debug mode, the response will include a debug message set by the Pipit app as well as the Perch debug message:

{
    "status": 422,
    "errors": { 
        "name": {"type": "required"},
        "email": {"type": "format"}
    },
    "debug": "You have some errors",
    "perch_debug": [...]
}

This is can be useful to diagnose issues in your set up. For instance if you specify a validation template that does not exist, you will get this response:

{
    "status": 500,
    "debug": "Template not found",
    "perch_debug": [...]
}

It is then your responsibility to proceed based on the response however you see best. For example, it is your responsibility to display error messages on the front-end.

Customising error data

By default the response only includes the error type:

"email": {"type": "format"}    

You can from the template specify additional data to be returned for errors using Perch’s perch:error conditional tags. Any response-* attributes on the tag is returned with the response:

<perch:error for="email" type="format" response-message="Please enter a valid email address"></perch:error>
"email": {
    "type": "format",
    "message": "Please enter a valid email address"
}

If you follow progressive enhancement principles and the form works without Javascript too, you can still include your server-rendered error message markup inside the perch:error tags:

<perch:error for="email" type="format" response-message="Please enter a valid email address">
    <span class="error">Please enter a valid email address.</span>
</perch:error>
<perch:error for="email" type="format" response-message="This is returned with the JSON response">
    <span class="error">This is a server-rendered error message. It is used when you submit the form without JS.</span>
</perch:error>

Usage

Where to add

Perch Runway: API endpoints

On Perch Runway you can add endpoints to templates/api/. You can add an endpoint like /api/enquiry for a contact form. In this context, you need to use the dispatch option:

pipit_form_response('contact', ['dispatch' => true]);

Standard Perch

On standard Perch you can add it to the same page as the form or on a separate page on its own. Add it right after the runtime include (before outputting anything) and inside a condition that only evaluates to true if your form has been posted.

<?php 
include('perch/runtime.php');

if(!empty($_POST)) {
    pipit_form_response('contact');
    exit;
}

If you are posting JSON, you can use:

<?php 
include('perch/runtime.php');

if(Pipit_Util::is_json_content_type()) {
    pipit_form_response('contact', ['dispatch' => true]);
    exit;
}

Non-Perch-templated forms

If you are using a non-Perch-template form (or even programmatically posting with JS), you need to specify a validation template and an app to handle the submission. You also need to use the dispatch option:

pipit_form_response('contact', [
    'template' => 'forms/contact.html',
    'app' => 'perch_forms',
    'dispatch' => true,
]);

Posting JSON

If you are posting the data as JSON, Perch won’t automatically handle this so you have to use the dispatch option:

pipit_form_response('contact', ['dispatch' => true]);

Submitting a Perch-templated form via JS

There are multiple ways of posting forms with Javascript. For instance, you can use XMLHttpRequest, fetch or a third-party library like axios.

Here is a basic contact form:

<perch:form id="contact" method="post" app="perch_forms" data-form="contact">

    <div class="field">
        <perch:label for="name">Name</perch:label>
        <perch:input id="name" type="text" label="Name" required />
    </div>

    <div class="field">
        <perch:label for="email">Email</perch:label>
        <perch:input type="email" id="email" label="Email" required />
    </div>

    <div class="field">
        <perch:label for="message">Message</perch:label>
        <perch:input id="message" type="textarea" label="Message" required />
    </div>

    <perch:input type="submit" id="submit" value="Send" />
</perch:form>

Submitting using XMLHttpRequest:

const myform = document.querySelector('[data-form="contact"]');

myform.addEventListener('submit', function(event) {
    event.preventDefault();
    let formData = new FormData(myform);
    let xhr = new XMLHttpRequest();

    xhr.open("POST", '/api/endpoint', true);
    xhr.send(formData);

    xhr.onload = function() {
        if (xhr.status == 200) {
            console.log('Success!');
            console.log(JSON.parse( this.responseText ));
        } else {
            console.log("Error " + xhr.status + " occurred.");
            console.log(JSON.parse( this.responseText ));
        }
    };
});

Submitting using axios:

const myform = document.querySelector('[data-form="contact"]');

myform.addEventListener('submit', function(event) {
    event.preventDefault();
    let formData = new FormData(myform);

    axios.post('/api/endpoint', formData)
    .then(function (response) {
        console.log(response.data);
    })
    .catch(function (error) {
        console.log(error);
        console.log(error.response);
    });
});
link