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.
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.