This post is part of a series called “Learning Vue.js as a WordPress Developer”
- Part 1 – Getting Started
- Part 2 – Structured PHP, Rest API and Axios
- Part 3 – Markdown and WordPress Embeds
- Part 4 – Authenticated Rest API
- Part 5 – Decoupled Page Template
The previous post included lots of hacky PHP/Javascript and no Rest API usage. That was the easiest way I knew to take an existing custom WordPress template and patch the data into Vue.js. However that’s not very maintainable. Let’s clean things up 🔧.
Step 1: Move custom PHP code to structured PHP object
A little bit of object oriented programing can really clean things up and make large parts of the code reusable. Structured PHP is not something I’ve done a whole lot of, so I spent some reviewing this course by Laracasts: https://laracasts.com/series/object-oriented-bootcamp-in-php. If you’re in the same boat as me I’d highly recommend watching it.
That said I came up with the following objects CaptainCore\Sites
and CaptainCore\Site
. The Sites object handles the WordPress loop and the Site object handles the formatting.
<?php
namespace CaptainCore;
class Sites {
protected $sites = [];
public function __construct( $sites = [] ) {
$arguments = array(
'post_type' => 'captcore_website',
'posts_per_page' => '-1',
'order' => 'asc',
'orderby' => 'title',
);
// Loads websites
$sites = get_posts( $arguments );
$this->sites = $sites;
}
public function all() {
return $this->sites;
}
}
class Site {
public function get( $site ) {
if ( !is_object( $site ) ) {
return;
}
$plugins = json_decode( get_field( 'plugins', $site->ID ) );
$themes = json_decode( get_field( 'themes', $site->ID ) );
// Prepare site details to be returned
$site_details = new \stdClass();
$site_details->id = $site->ID;
$site_details->name = get_the_title( $site->ID );
$site_details->visible = true;
$site_details->selected = false;
$site_details->core = get_field( 'core', $site->ID );
$site_details->home_url = $home_url;
if ( $plugins && $plugins != '' ) {
$site_details->plugins = $plugins;
} else {
$site_details->plugins = array();
}
if ( $themes && $themes != '' ) {
$site_details->themes = $themes;
} else {
$site_details->themes = array();
}
return $site_details;
}
}
This is essentially the same structure as I was previously outputting manually to the template with a mix of PHP and Javascript. The key here is any structure changes only have to happen within these 2 classes. For use within Vue.js we simply fetch in CaptainCore\Sites or CaptainCore\Site as needed.
Step 2: Delivering the PHP object via the Rest API
Rather than inject this new object directly in the template using PHP, it can be fetched after the page load using the Rest API via a Javascript AJAX request. The following code establishes two new Rest API endpoints captaincore/v1/site/<site-id>
and captaincore/v1/sites
.
function captaincore_site_func( $request ) {
$site = new CaptainCore\Site;
return $site->get( $site_id );
}
function captaincore_sites_func( $request ) {
$sites = new CaptainCore\Sites();
$all_sites = array();
foreach( $sites->all() as $site ) {
$all_sites[] = ( new CaptainCore\Site )->get( $site );
}
return $all_sites;
}
// Custom endpoint for CaptainCore site
register_rest_route(
'captaincore/v1', '/site/(?P<id>[\d]+)', array(
'methods' => 'GET',
'callback' => 'captaincore_site_func',
'show_in_index' => false
)
);
// Custom endpoint for CaptainCore sites
register_rest_route(
'captaincore/v1', '/sites/', array(
'methods' => 'GET',
'callback' => 'captaincore_sites_func',
'show_in_index' => false
)
);
Anything returned from the Rest API is automatically encoded as JSON. That means the returning of the PHP object from the Rest API, as shown above, will be immediately usable by Javascript.
Step 3: Fetch data with Axios
When working with modern Javascript like Vue.js a common thing is making ajax requests. While this could be handled with jQuery, I’ve been slowing switching to Axios which is a very popular modern standalone library for handling ajax requests. Adding Axios to WordPress can be done with a simple wp_enqueue_script
.
wp_enqueue_script( 'axios', 'https://unpkg.com/axios/dist/axios.min.js' );
Next we need to expand the Vue script as shown below. The sites object is now defined as an empty array with the axios request happening after Vue.js is mounted.
<script>
new Vue({
el: '#app',
data: {
sites: [],
},
mounted() {
axios.get('/wp-json/captaincore/v1/sites')
.then(response => {
this.sites = response.data;
});
},
});
</script>
All the same functionality found in Part 1 will continue to function the same. However it’s now a more manageable code base to work with.