WordPress Post from Front End using REST API and Vue.js

This post is a simple proof of concept for using the new WordPress REST API to submit a new post draft from the front end. The form and user inputs are built using the Vue.js framework and vue-cli to create a simple Webpack build configuration. Demo on YouTube

Environment

This example was created using a fresh install of WordPress 4.7 served by XAMMP on Windows 10.

  • Nodejs version 6.9.1
  • NPM version 3.10.8
  • vue-cli version 2.6.0
  • git version 2.7.0

If you’re looking for an equivalant React version of this proof-of-concept, look no further. I’ve created a version for React here.

Plugin Development

Change to the plugins directory. For example,

cd c:/xampp/htdocs/wordpress/wp-content/plugins

If you want to skip ahead to install node modules and build with webpack, the plugin source code can be downloaded from GitHub, https://github.com/jimfrenette/wp-api-vuejs-poc

Create the directory for the new plugin. For example,

mkdir wp-api-vuejs-poc

Entry Point

Create a plugin entry point php file in this new plugin directory. For example,

# change to the new directory
cd wp-api-vuejs-poc

# create a new file
touch wp-api-vuejs-poc.php

Add the following php code to the plugin entry point. At the very least, add the comment block at the top to register the plugin with WordPress.

wp-api-vuejs-poc.php
<?php
/**
 * Plugin Name: WP API Vue.js Proof of Concept
 * Plugin URL: http://example.com
 * Description: WordPress plugin to draft posts from the front end using the new REST API and Vue.js
 * Version: 1.0
 * Author:
 * Author URI: http://example.com
 * Text Domain: wp-api-vuejs-poc
 */

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

if ( ! class_exists( 'WP_API_Vuejs_PoC' ) ) :

/**
 * Main WP_API_Vuejs_PoC Class.
 */
final class WP_API_Vuejs_PoC {

    /**
     * The single instance of the class.
     */
    protected static $_instance = null;

    /**
     * Ensures only one instance of WP_API_Vuejs_PoC is loaded or can be loaded.
     */
    public static function instance() {
        if ( is_null( self::$_instance ) ) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    public function __construct() {
        $this->define_constants();
        $this->includes();
    }

    /**
     * Define Constants.
     */
    private function define_constants() {
        $this->define( 'WAVP_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
        $this->define( 'WAVP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
    }

    private function define( $name, $value ) {
        if ( ! defined( $name ) ) {
            define( $name, $value );
        }
    }

    /**
     * What type of request is this?
     *
     * @param  string $type admin, ajax, cron or frontend.
        * @return bool
        */
    private function is_request( $type ) {
        switch ( $type ) {
            case 'admin' :
                return is_admin();
            case 'ajax' :
                return defined( 'DOING_AJAX' );
            case 'cron' :
                return defined( 'DOING_CRON' );
            case 'frontend' :
                return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' );
        }
    }

    public function includes() {
        if ( $this->is_request( 'frontend' ) ) {
            include( 'class-api-vpoc-page.php' );
        }
    }

}

endif;

/**
 * Main instance of WP_API_Vuejs_PoC.
 * Returns the main instance of WAVP to prevent the need to use globals.
 */
function WAVP() {
    return WP_API_Vuejs_PoC::instance();
}

// Global for backwards compatibility.
$GLOBALS['wp-api-vuejs-poc'] = WAVP();

Class for Front End Page Request

In the class of the plugin entry point file above, the includes function contains a request type check. When the request is made from the front end, include the class file for the page. Let’s create that class file now, for example,

touch class-api-vpoc-page.php

Add the following php code to create the class. The constructor method is called on the newly-created class object where we are subscribing to events using hooks.

The action hook is called during the page processing event for script loading, at this point, call our page_scripts function to load the app javascript.

The filter hook is called during data processing, when the content is being loaded, call our page_content function to create the app bootstrap element.

class-api-vpoc-page.php
<?php

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

/**
 * WP_API_Vuejs_PoC_Page class.
 */
class WP_API_Vuejs_PoC_Page {

    /**
     * Access
     */
    private static $user_can = 'edit_posts';
    private static $page_slug = 'api-test';

    /**
     * Constructor.
     */
    public function __construct() {
        add_action( 'wp_enqueue_scripts', array( $this, 'page_scripts' ) );
        add_filter( 'the_content', array( $this, 'page_content' ) );
    }

    public function page_scripts() {
        if ( is_page( self::$page_slug ) ) {
            // load the Vue.js app
            wp_enqueue_script( 'wp-api-vuejs-poc', WAVP_PLUGIN_URL . 'dist/build.js', array(), false, true );

            // localize data for script
            wp_localize_script( 'wp-api-vuejs-poc', 'wp_api_vuejs_poc', array(
                'rest_url' => esc_url_raw( rest_url() ),
                'nonce' => wp_create_nonce( 'wp_rest' ),
                'success' => __( 'Post submitted', 'wp-api-vuejs-poc' ),
                'failure' => __( 'Post could not be processed.', 'wp-api-vuejs-poc' ),
                'current_user_id' => get_current_user_id()
                )
            );
        }
    }

    public function page_content($content) {
        if ( is_page( self::$page_slug ) ) {
            // output only to logged in users who can edit posts
            if ( is_user_logged_in() && current_user_can( self::$user_can ) ) {
                // app bootstrap element
                ob_start();?>
                <div id="app"></div>
                <?php
                $content .= ob_get_clean();
            }else{
                $content .=  sprintf( '<a href="%1s">%2s</a>', esc_url( wp_login_url() ), __( 'Log in', 'wp-api-vuejs-poc' ) );
            }
        }

        return $content;
    }

}

return new WP_API_Vuejs_PoC_Page();

The next page covers the Vue.js front end.

comments powered by Disqus