DEV Community

Chris Wright
Chris Wright

Posted on

Integrating APIs with Wordpress and Contact Form 7

Contact Form 7 is an invaluable tool for building forms in Wordpress. My team has been using it for quite some time due to its ease of use and flexibility. Some of the features touted on its plugin page are:

  • Ability to manage multiple contact forms
  • Customization of form and mail contents
  • AJAX-powered submissions
  • reCAPTCHA support
  • Akismet spam filtering

While it definitely has some issues we've had to overcome, it is one of the first plugins installed on a new project. One such issue I've personally felt is—despite generally having decent documentation around its core functionality—a somewhat lack of insight around integrating with third party services. In this article I would like to outline an approach I've been using to integrate with external APIs during the submission process.

Hooks

Like all things in Wordpress, Contact Form 7 is built around a series of hooks. These hooks are exposed to us and grant access to seemingly every lifecycle event of a form from admin initialization to submission, tag rendering to validation triggers, and more.

A list of all provided hooks can be found over on hookr.io

add_action( string $tag, callable $function_to_add, int $priority = 10, int $accepted_args = 1 )
Enter fullscreen mode Exit fullscreen mode

If you're unfamiliar with hooks, I'll briefly explain what's happening here. add_action is how we can register a listener for specific events that get emitted by plugins or Wordpress itself. It takes up to 4 arguments:

  1. tag—the event we wish to listen for
  2. function_to_add—a reference to the function that will be invoked for the event. This can a function name, inline function, or class method
  3. priority—when the function shold be called
  4. accepted_args—the number of arguments to pass to the function

More information about actions can be found in the Codex.

Register listener

The hook we're currently interested in is wpcf7_before_send_mail as this represents the last step in the process before mail is actually sent to the recipient. We'll start by registering our listener for this hook:

function on_submit( $form, &$abort, $submission )
{
    // TODO
}

add_action( 'wpcf7_before_send_mail', 'on_submit', 10, 3 );
Enter fullscreen mode Exit fullscreen mode

You may have noticed that we've specified that our callback should receive 3 arguments: the form that was submitted, a flag representing failed submission, and the submission itself. Note that abort is actually passed by reference so that the calling class can use it to cancel the submission if we indicate it should do so.

Now that we've got the setup out of the way we can start integrating with whichever systems we need to.

Integrate all the things

Alt Text

You may have multiple forms (like in my contrived scenario above) and only want to run the integration on a specific form. The form object passed to our callback function is an instance of WPCF7_ContactForm, which means we can examine it to make sure it's the form we're interested in. The simplest way is to compare against the form's ID:

if ( $form->ID() !== 999 ) {
    return;
}
Enter fullscreen mode Exit fullscreen mode

Next we can perform the actual integration. Let's assume all we need is a simple POST request to some endpoint that accepts some fields. Like the form above, the submission object is an instance of WPCF7_Submission and we can use it to retrieve the data submitted to the form.

$data = $submission->get_posted_data();
Enter fullscreen mode Exit fullscreen mode

$data is now an array that contains all of the form information we need, including some additional meta data:

array(15) {
    ["_wpcf7"] => string(3) "999"
    ["_wpcf7_version"] => string(5) "5.1.3"
    ["_wpcf7_locale"] => string(5) "en_US"
    ["_wpcf7_unit_tag"] = > string(13) "wpcf7-f761-o1"
    ["_wpcf7_container_post"] => string(1) "0"
    ["email"] => string(12) "jane@doe.com"
    ["name"] => string(8) "Jane Doe"
}
Enter fullscreen mode Exit fullscreen mode

We can now extract the information we need and pass it to our API:

$email = sanitize_text_field($data['email']);
$name = sanitize_text_field($data['name']);

$response = wp_safe_remote_post(/* your API endpoint */, [
    'body' => json_encode([
        'email' => $email,
        'name' => $name,
    ]),
]);
Enter fullscreen mode Exit fullscreen mode

For the sake of this example, we're going to assume that when there is an issue with the request it returns an error (along with a status code other than 2XX). This should then return an error object that we can check for. If we do encounter an error, we can signal for submission to be aborted.

if ( is_wp_error($response) ) {
    $abort = TRUE;
}
Enter fullscreen mode Exit fullscreen mode

Gotcha

Flagging to abort the submission will only cancel the default CF7 submission processing. If you have other hooks registered they will continue to fire so you need to check if abort has been set in each.

if ( $abort === TRUE || $form->ID() !== 999 ) {
    return;
}
Enter fullscreen mode Exit fullscreen mode

A bit of polish

We now have the basic framework in place but we're lacking a bit of that user experience as the user is not properly notified of any issues. If your API returns user-friendly error messages then you can pass those along to the user with set_response, which will render your message on the form.

$body = wp_remote_retrieve_body($response);
$result = json_decode($body);

$submission->set_response($result->message);
Enter fullscreen mode Exit fullscreen mode

You may also wish to style your form based on its current state. For example, the form could shake momentarily when marked invalid or transition to a confirmation message upon success. To do this, we can set the submission's status property. By default CF7 sets its own statuses and applies classes to the form accordingly: if validation fails then the status is set to validation_failed with the class of invalid.

<form action="..." method="..." class="wpcf7-form invalid">
Enter fullscreen mode Exit fullscreen mode

You can view all of builtin statuses and their classes on the Plugin Directory

You can apply your own classes by setting a custom status along with your message. This will then be applied to the form with a custom- prefix and non-alphanumeric characters replaced with hyphens.

$submission->set_status('api_failed'); // custom-api-failed
Enter fullscreen mode Exit fullscreen mode

Wrapping it all up

For the sake of completeness, here is a look at what all of this looks like put together:

function on_submit( $form, &$abort, $submission )
{
    if ( $abort === TRUE || $form->ID() !== 999 ) {
        return;
    }

    $data = $submission->get_posted_data();

    $email = sanitize_text_field($data['email']);
    $name = sanitize_text_field($data['name']);

    $response = wp_safe_remote_post(/* your API endpoint */, [
        'body' => json_encode([
            'email' => $email,
            'name' => $name,
        ]),
    ]);

    if ( is_wp_error($response) ) {
        $abort = TRUE;

        $body = wp_remote_retrieve_body($response);
        $result = json_decode($body);

        $submission->set_response($result->error);
        $submission->set_status('api_failed');
    }
}

add_action('wpcf7_before_send_mail', 'on_submit', 10, 3);

Enter fullscreen mode Exit fullscreen mode

Final thoughts

Custom integrations may not be common with a plugin as feature-rich as Contact Form 7 but there is occasionally a need for them in real world projects. Hopefully this provides a decent jumping off point for writing your own. If you have to incorporate many solutions then I would recommend registering multiple functions and incrementing their priority to have them fire one after another.


Thank you for taking the time to join me on this adventure! I've recently been delving into Wordpress development and I've found there are some areas which could use more long-form content to really dig in. If there's something you'd like me to cover, or if you have any feedback on this article, leave a comment here or hit me up on Twitter.

Discussion (4)

Collapse
porquero profile image
Cristian Riffo Huez

Thanks! I use it to integrate with EspoCRM REST API

Collapse
jorbascrumps profile image
Chris Wright Author

Awesome! Glad you found it useful!

Collapse
manisvgsvk profile image
manisvgsvk

can I integrate more than one API its means,
one API response based call another API if first API faild second API should not call. is its possible?

Collapse
jorbascrumps profile image
Chris Wright Author • Edited

Sure you can! Setup a second action with a lower priority and then set $abort to true on the first. This will stop the second action from firing.