Google Drive API and FlySystem – Obtaining the IDs of newly created folders

TL;DR -> Extend the \League\Flysystem\Filesystem class and implement your own version of createDir() that doesn’t cast the adapter’s response to a bool before returning, or make a new method called something like createDirReturnId() that is a clone of createDir() without the bool casting, if like me you are paranoid about breaking some other abstraction that expects the original implementation of createDir() to exist. You’ll also need to instantiate your new child class inside of GoogleDriveServiceProvider.php, replacing \League\Flysystem\Filesystem.

Along with Laravel, I’ve been using the amazing Flysystem – Filesystem Abstraction for PHP with Naoki Sawada’s great adapter for Google Drive.

Unlike many of the filesystems that Flysystem allows you to interact with, Google Drive is not a traditional file system, and is based on file and folder IDs that are generated for you. With Google Drive, the name or the file or folder is different from the ID, and it can be changed, whereas the ID stays the same for the life of that file or folder. And I believe that all files and folders are essentially the same thing, i.e. a folder is just a file with a MIME type of application/vnd.google-apps.folder.

When I create a new Google Drive folder for an entity in my program, I want to get that folder’s ID back so that I can store it in the database for later use. However, when I use Flysystem’s createDir() method to make new folders in Drive, the response is limited to a boolean (true or false):

$response = \Storage::drive('google')
->createDir(\Config::get('constants.drive_paths.people') . '/New Folder');
// Here I'm creating a new folder called 'New Folder' inside my 'people' directory.
// $response will be true if the folder was successfully created, or false if there was a problem.

It seems like Flysystem is built to be pretty streamlined out of the box, and perhaps is expected to be used with more traditional filesystems where the name of the folder is the only identifier you would need. So if you’ve already specified the folder name when you asked for it to be created, then why would you need any extra info back, other than a simple success or failure? You already know where it is and how to address it. Makes sense.

For Google Drive this becomes a problem (at least for the way I was trying to go about it).

At first I thought about making a Temp folder manually, through Drive’s web interface, and then having my program create any new folders in the Temp folder first, then scanning the temp folder to see all the entities inside, which would only be one, hopefully, getting the ID of that one entity (the folder we just created), storing the ID in the DB, and then moving that entity to its final destination. But that seems a bit roundabout, and I was worried that it would break if the program was handling a huge amount of simultaneous calls to create folders (if the scan of the temp folder showed more than one folder inside it). Probably mathematically unlikely, but I don’t really know.

After searching through Naoki’s code on GitHub, I found that his adapter does return more than just a boolean, and after searching through the definition of createDir() in Flysystem, I found that it is casting the return from the adapter to a boolean:

// \League\Flysystem\Filesystem.php
public function createDir($dirname, array $config = [])
{
    $dirname = Util::normalizePath($dirname);
    $config = $this->prepareConfig($config);
    return (bool) $this->getAdapter()->createDir($dirname, $config);
}

So I created a new class that extends theirs, with a new method that doesn’t cast to a boolean:

// \App\Gfilesystem.php

namespace App;

use League\Flysystem\Util;

class Gfilesystem extends \League\Flysystem\Filesystem
{

    public function createDirReturnId($dirname, array $config = [])
    {
        $dirname = Util::normalizePath($dirname);
        $config = $this->prepareConfig($config);

        return $this->getAdapter()->createDir($dirname, $config);
    }

}

And I edited this file:

// App\Providers\GoogleDriveServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class GoogleDriveServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        \Storage::extend('google', function($app, $config) {
            $client = new \Google_Client();
            $client->setClientId($config['clientId']);
            $client->setClientSecret($config['clientSecret']);
            $client->refreshToken($config['refreshToken']);
            $service = new \Google_Service_Drive($client);
            $adapter = new \Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter($service, $config['folderId']);
            return new \League\Flysystem\Filesystem($adapter);
        });
    }
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

To instantiate my new child class instead:

public function boot()
{
    \Storage::extend('google', function($app, $config) {
        $client = new \Google_Client();
        $client->setClientId($config['clientId']);
        $client->setClientSecret($config['clientSecret']);
        $client->refreshToken($config['refreshToken']);
        $service = new \Google_Service_Drive($client);
        $adapter = new \Hypweb\Flysystem\GoogleDrive\GoogleDriveAdapter($service, $config['folderId']);
        return new \App\GFilesystem($adapter);
    });
}

The new response is an array that includes the ID of the new folder and the ID of the parent folder, separated by a slash:

$drive_folder = explode("/", $response['path'])[1];

And we’re good! So far this has been working out for my purposes.

3 Months of Pluralsight Training – Free c/o Microsoft

Microsoft is at it again!

Just by joining their new online platform Visual Studio Dev Essentials, you gain access to a 3 month credit at Pluralsight, Linux Academy, Opsgility, WintellectNOW and tons of other services and opportunities.

I’m a week into my 3 month free period at Pluralsight, using it to brush up on Node and learn React thoroughly.

Scrimba – Another Learner’s Paradise

Interactive screencast learning!

This morning I discovered Scrimba, a free tool for creating interactive screencasts for teaching web development and all things coding. Thanks to the Papaerbot from Chingu’s Slack, who let me know!

You can browse the available lessons in a simple, streamlined interface. There is already some really cool stuff on there, and I have to assume this is going to explode in popularity very quickly. I’m going to check back every day to consume as much as I can!

For more learning resources, check out my posts tagged with Learning.

Adding JS to WordPress effortlessly with Code Snippets

I’ve been using Code Snippets to keep my PHP additions organized, but I also had a lot of javascript files being enqueued in my theme’s functions.php file. This meant that they were likely to disappear on my next theme update or switch. I could have used a simple JS plugin like JavaScript AutoLoader, but I wanted more control over when certain scripts were used, for example with the is_product() function in WooCommerce.

So I started enqueuing my scripts in a snippet with Code Snippets:

add_action( 'wp_footer', function () { ?>
<script>
/* Loose js for footer can go here */
</script>
<?php } );

function load_our_lovely_js_please() {
    if (is_product()) {
        wp_enqueue_script('prodscript','https://mastersonmethodequinetherapy.com/wp-content/themes/Avada/js/product.js', false, '1.0', true );
    }
}

add_action( 'wp_enqueue_scripts', 'load_our_lovely_js_please' );

In the first half of this snippet there is a place for putting straight javascript into the page, and later on we have a chance to add external js files. I also used a fluffy function name to avoid colliding with other loader functions being used, which may or may not be necessary. Note that I don’t add the opening <?php tag at the start of this snippet, as per Code Snippets’ workflow.

I didn’t have luck using the is_product() function to determine when I should call add_action(), so I’m using it inside my loader function and it works fine on wp_enqueue_script(). Perhaps when add_action() is called, the system doesn’t know if we are on a product page yet? In any case, using it inside my loader function allows me to have just 1 loader function with my loading decisions inside:

add_action( 'wp_footer', function () { ?>
<script>
/* Loose js for footer can go here */
</script>
<?php } );

function load_our_lovely_js_please() {
    wp_enqueue_script('beeboo','https://mastersonmethodequinetherapy.com/wp-content/js/beeboo.js', false, '1.0', true );
    if (is_product()) {
        wp_enqueue_script('prodscript','https://mastersonmethodequinetherapy.com/wp-content/js/product.js', false, '1.0', true );
    }
}

add_action( 'wp_enqueue_scripts', 'load_our_lovely_js_please' );

This way of doing it was patched together from some answers given by Code Snippets’ author Shea Bunge in the WP Plugin support section. Thanks Shea! I opted to use add_action() instead of just wp_enqueue_script() by itself, because that didn’t seem to work in this case.

If you’ve got a better way of managing and enqueuing JS, please let me know!

Code Snippets – The answer to all your WordPress hacking needs

I’ve been using a great WordPress plugin called Code Snippets.

Rather than dropping PHP directly into your theme’s functions.php file, where it will be lost if you update or switch themes, (and where you can easily overwrite or break your theme’s code accidentally) you can instead keep your code additions organized with Code Snippets. They are saved in separate little snippets, like posts, which can easily be named, tagged, documented, exported/imported and independently activated and deactivated.

Just install it from the WP plugin repository, activate it, and dive in from the Snippets menu item in the admin area of WordPress.

Enlighter

I’m now using Enlighter to embed code snippets in my posts.

Let’s try it out:

<?

class Display
{

    public $db;

    public function __construct(PDO $pdo)
    {
        $this->db = $pdo;
    }
}

?>

Cool!

Enlighter has several themes included, like Atomic, which is somewhat similar to Monokai.

It also has a theme customizer which allows you to build on top of the included themes. Curiously, the customizer lacks the ability to change the background color from the base theme, so I can’t get that sexy Monokai grey that I’m craving. Sounds like a great opportunity for my first open-source contribution on GitHub!

Free Code Camp

Free Code Camp

I just discovered freeCodeCamp, and I’m addicted. I highly recommend it for anyone who is just starting coding or web learning, or for anyone like me who has a hodgepodge of technical knowledge but would like to fill in the cracks and learn in a systematized way, with best practices and common design patterns.

As the name says, it’s free. It also offers free certificates for some of its milestones, which is a nice thing to have, and will probably carry more weight with employers in the coming months and years.

Check it out!