Part 3 of 13 · Elite Limo Booking

Building the Plugin Skeleton: Hooks, Shortcodes, and AJAX

WordPress plugin skeleton diagram showing activation hooks, shortcodes, AJAX handlers, REST routes, and admin menu

A plugin is not just files in a folder. Those files are inert until WordPress calls them, and WordPress only calls what you have explicitly wired in. The wiring has names, hooks, shortcodes, AJAX actions, and getting them right is the difference between a plugin and a folder of PHP. This post is the actual skeleton of the limo plugin, the lines that plug it into WordPress.

Everything wires up in the constructor and init:

public function __construct() {
    add_action('init', array($this, 'init'));
    register_activation_hook(__FILE__, array($this, 'activate'));
    register_deactivation_hook(__FILE__, array($this, 'deactivate'));
}

public function init() {
    $this->load_dependencies();

    add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
    add_action('wp_ajax_elite_limo_ajax',        array($this, 'handle_ajax'));
    add_action('wp_ajax_nopriv_elite_limo_ajax', array($this, 'handle_ajax'));
    add_action('admin_menu', array($this, 'admin_menu'));

    add_shortcode('elite_limo_booking',  array($this, 'booking_form_shortcode'));
    add_shortcode('elite_limo_tracking', array($this, 'tracking_page_shortcode'));
    add_shortcode('elite_limo_driver',   array($this, 'driver_dashboard_shortcode'));
    add_shortcode('elite_limo_history',  array($this, 'booking_history_shortcode'));
}

Three kinds of wiring, three different jobs. register_activation_hook runs once, at the moment the plugin is switched on, which is where the database tables get created, table creation does not belong in normal page loads, it belongs in activation. The add_shortcode lines are how the plugin appears on pages, an admin writes [elite_limo_booking] on any page and the booking form renders there, four shortcodes meaning the site owner decides where booking, tracking, the driver dashboard, and history live, instead of the plugin dictating pages.

The AJAX pair deserves the most attention because it is the most misunderstood wiring in WordPress. Browser JavaScript cannot call PHP functions, it can only send requests, and WordPress routes AJAX requests by an action name. The two lines register the same handler twice for one reason:

add_action('wp_ajax_elite_limo_ajax',        array($this, 'handle_ajax')); // logged-in users
add_action('wp_ajax_nopriv_elite_limo_ajax', array($this, 'handle_ajax')); // visitors

The plain hook fires only for logged-in users. The nopriv one fires for everyone else, and customers booking a ride are everyone else. Register only the first, which is the classic mistake, and the booking form works perfectly for you, the logged-in admin testing it, then silently returns 0 for every real visitor. That exact symptom, AJAX that works for admins and dies for visitors, is a missing nopriv registration almost every time.

A few things people ask me about this

Why does my WordPress AJAX return 0 for visitors? You registered only wp_ajax_youraction. Add wp_ajax_nopriv_youraction with the same handler for logged-out users.

Where should a plugin create its database tables? In the register_activation_hook callback, once, at switch-on. Never on init or page load, that would attempt creation on every request.

Next

With the skeleton wired, I hit the wall this project is remembered for. The plugin installed fine, and refused to activate, with a fatal error and a message that told me almost nothing. That is the next post.

Leave a Reply

Your email address will not be published. Required fields are marked *