Using Laravel and WordPress to build a fast and flexible API – Part 2

Categories: Development, WordPress

This is part 2 of building a WordPress powered API with Laravel.

If you are just joining me now then please read part 1 here to get caught up.

In part 2 we are going to beef up our API and look at caching, How to purge the cache and create a few helpers to keep the code DRY!

Let’s start by creating some Helper stubs

These stubs will help us think about what we need to do, It will also help me to give you a bit of context because I will have to jump around a bit.

Start by creating app/ApiHelpers.php, Most of our code is going to live in this file.

namespace App;

use Request;
use Cache;
use Carbon\Carbon;

class ApiHelpers {

    static function handleWordPressQuery( $cacheKey, $queryString, $cacheTimeInMinutes = 240 ) { }

    static function handleCachePurge( $cacheKey ) { }

    private static function getAcfData( $post_id ) { }
}

Update the $queryString block to include a $cacheKey, I will touch on this a little later on.

if ( $slug === null ):
    $cacheKey    = 'store_all';
    $queryString = array(
        'post_type'      => 'store',
        'posts_per_page' => - 1,
        'orderby'        => 'date',
    );
else:
    $cacheKey    = 'store_' . $slug;
    $queryString = array(
        'post_type'      => 'store',
        'name'           => $slug,
        'posts_per_page' => 1,
        'orderby'        => 'date',
    );
endif;

Update the store method to return the results of our new Query Helper.

return ApiHelpers::handleWordPressQuery( $cacheKey, $queryString );

Last, Import the class into the API Controller:

namespace App\Http\Controllers;

use Request;
use App\ApiHelpers;

Let’s make a Query Helper

static function handleWordPressQuery( $cacheKey, $queryString, $cacheTimeInMinutes = 240 ) {
    $query = new \WP_Query( $queryString );
    $posts = $query->get_posts();

    $object['total']  = count( $posts );
    $object['status'] = 200;
    $object['age']    = date( "M d Y, H:i:s" );

    foreach ( $posts as $post ):
        $single_acf = self::getAcfData( $post->ID );
        $category   = get_the_category( $post->ID );

        if ( ! empty( $category ) ):
            $category = $category[0]->slug;
        else:
            $category = null;
        endif;

        if ( empty( $single_acf ) ) {
            $single_acf = null;
        }

        if ( empty( $queryString['name'] ) ):
            $path = Request::url() . '/' . $post->post_name;
        else:
            $path = Request::url();
        endif;

        $object['results'][] = [
            'created'  => $post->post_date,
            'modified' => $post->post_modified,
            'path'     => $path,
            'category' => $category,
            'slug'     => $post->post_name,
            'data'     => $single_acf
        ];
    endforeach;


    return $object;

}

Not too bad hey? Basically took what we had in the controller and plopped it into a function.

I also added some extra data to make requests a bit more helpful and flexible as created, modified, category, and slug.

Handle ACF

Not a whole lot to see here. Simple passing $post_id to get_fields().

private static function getAcfData( $post_id ) {
    return get_fields( $post_id );
}

Now let’s add Caching

We already added the $cacheKey to $queryString, this is what will allow our caching service to store, update, and soon delete cache entries using Laravel’s Cache. In my case, I will be using Memcached as it’s installed and running on my server and also locally. Feel free to use whatever suits your needs.

Now we need to update the query helper:

if ( Cache::has( $cacheKey ) ):
    $object = Cache::get( $cacheKey );
else:
    // Original code here!

    Cache::put( $cacheKey, $object, Carbon::now()->addMinutes( $cacheTimeInMinutes ) );
endif;

If the cache key exists and is not expired then we will pull our data out of memory, Otherwise, make the call to WordPress / DB and add it to the cache while returning a response.

Delete caching

Add Route::get( '/store/{slug}/purge', 'ApiController@purgeStore' ); to the API Routes, also add purgeStore to the API Controller:

public function purgeStore( $slug = null ) {
    ApiHelpers::handleCachePurge( 'store_' . $slug );

    return $this->store( $slug );
}

Now update the helper:

static function handleCachePurge( $cacheKey ) {
    Cache::forget( $cacheKey );
}

Once handleCachePurge is called we pass the slug to store which will return a fresh store object.

Done!

We have added Caching, The ability to clear cached results, and we also made a WordPress query handler that will help to keep the code nice and clean!

If this article helped you out or you thought that it was interesting then please consider sharing it, Also let me know what you plan to do with your WordPress API.

A small note on performance.

In sharing Part 1 on /r/PHP, I received a number of comments about speed.

Speed like anything is what you put into it. I don’t think it’s possible to simply install Anything and have a sub 40ms response time. It’s ridiculous to think that.

The way I created sub 80ms response times was by using Amazon CloudFront CDN‎.

I wrote a post a while back about caching and some of the methods that I used to track down performance issues here: Caching – “Because It’s Faster, Of Course”, Have a read. I think you will find some value in it.


Adam Patterson

Adam Patterson

User Interface Designer & Developer with a background in UX. I have spent 5 years as a professionally certified bicycle mechanic and ride year-round.

I am a husband and father of two, I enjoy photography, music, movies, coffee, and good food.

You can find me on Twitter, Instagram, and YouTube!