Sunday, February 3, 2013

WordPress Plugin Form Submissions

Development of my first WordPress plugin brought me to a basic piece of functionality, handling form submissions. I knew that I could submit a form via AJAX; however, that would not work for forms that allow file uploads without adding extra code and page markup. Being new to the world of WordPress, I went on a search for how this should be done. The search ended with a possible solution found on WordPress Answers.

The author of the solution explained that one needed to send the form action either to the site's homepage, or to a specific page URL. The author further explained that one can't have $_POST handling within a template because you need to redirect after your process it, and a redirect needs to be fired before any HTML output. It was suggested that this could be accomplished by adding a custom action by hooking into to the "template_redirect" action.

This made sense as it directly addressed my basic requirements:

  • All forms would need to be created from a single shortcode.
  • The shortcode that creates the forms would be added to a single page.
  • Form action attributes will be empty following WordPress practices.
  • Forms should be able to handle file uploads.
  • If the submission is successful, redirect the user to the correct page.
  • If the submission fails, redirect the user to an error page.

Custom Event Action

I implemented a custom action named "check_for_event_submissions" and hooked it to the "template_redirect" action. An event in this context could be more than a form submission. It could be any custom URL that triggers a plugin defined event that needs to be acted on and redirected. The example presented handles POST events only; however, it would be trivial to modify this function to handle a GET request if necessary. In short the function looks for two form post variables "event" and "view_redirect". The "event" variable describes the event to process and "view_redirect" the default redirection URL after the event is processed:

/**
  * Custom action that handles plugin event submissions.
  *
  * After the event is processed the action the redirects to the page
  * specified by the event.
  */
  public function check_for_event_submissions() {
     // Handle a form post event
     if(isset($_POST['event'])) {
         // Redirect to the specified page
         wp_redirect($_POST['view_redirect'])
         exit;
     } 
 }

 // Add Actions & Filters
 add_action('template_redirect', array(&$this, 'check_for_event_submissions'));

We will finish the custom event action later as we need to create an event controller to process the events first.

Event Controller

The next step was implementing an event controller that would be capable of identifying any number of custom events and calling the appropriate functions to process that event. It defines constants that are used to define events. The events are prefixed with the plugin name to prevent collisions:

// Exit if accessed directly
if ( !defined( 'ABSPATH' ) ) exit;

if ( !class_exists( 'ZSCritters_EventController.php' ) ) :
/**
 * @class ZSCritters_EventController.php
 * @abstract Implements the event controller used by the plugin
 */ 
class ZSCritters_EventController {

    const SAVE = 'ZSCRITTERS_SAVE';

    /**
     * Event controller
     *
     * @param $event string The event to handle
     * @return void
     */
    public function handleEvent($event) {
        $retval = 0;
        
        // Handle the specified event
        switch ($event)
        {
            case self::SAVE:
                $reval = do_something();
                break;
            default:
        }
        
        // If the event was not successful
        if($retval == FALSE) {
            $_POST['view_redirect'] = get_error_page_url();   
        }
    }
}
endif; // class_exists check

Finishing the Custom Event Action

Now that we have an event controller in place, we can add it to the custom event action:

/**
  * Custom action that handles plugin event submissions.
  *
  * After the event is processed the action the redirects to the page
  * specified by the event.
  */
  public function check_for_event_submissions() {
     // Handle a form post event
     if(isset($_POST['event'])) {
         // Pass the event to the event hander
            include_once('shortcodes/ZSCritters_EventController.php');
            $ec = new ZSCritters_EventController();
            $ec->handleEvent($_POST['event']);

         // Redirect to the specified page
         wp_redirect($_POST['view_redirect']);
         exit;
     } 
 }

That's my solution for handling plugin form posts. I really don't know if using "template_redirect" for this type of situation is a best practice in WordPress or not. I have tested the above solution with my own plugin and it seems to work very well.

If there is a better solution to this problem, I would like to know.