Laravel Google Drive API – The Best way to take a full backup of your app

Taking backups is the fundamental part of software development keeping backups can be a very decision. So in this post on Laravel Google Drive API – The Best way to take a full backup of your app we’ll cover the zipping project and taking complete project backup then send it to google drive.

Table of Contents

Configuring and Installing Google Client

The first step would be to turn on Driver API in the process you have to provide the project name, select integration type web server, then specify the callback URL of your application and after a few click, it will ask you to download the credentials.js file.

Installing google/apiclient

Open the composer.json file of your Laravel application and add “google/apiclient”: “^2.0” within require.

"require": {
    "...",
    "...",
    "google/apiclient": "^2.0"
},

and then run composer update command.

Note: If you directly run composer require google/apiclient:^2.0 within terminal then you’ll get an exception that says Installation failed, requirements could not be resolved. so follow the above step only.
Google/APIClient Error while directly installing via composer
Google/APIClient Error while directly installing via composer

Project Structure

test_laravel/
        |-app
        |   |-Http
        |   |    |-BackupController.php
        |
        |-Utils
        |    |-Drive.php
        |
        |-public
        |    |-secret
        |          |- credentials.json
        |          |- token.json
        |    |-zip_files_backups
        |          |- files.zip
        |-resource
        |    |-views
        |       |-backups
        |           |-default.blade.php     
        |-routes
        |   |-web.php

Creating a helper class for Drive

To communicate with google drive we need google client and Google_Service_Drive and to manage all those authentications, creating, retrieving of files I have created a helper class called Drive.

In app\Utils\Drive.php.

<?php
namespace App\Utils;

use Exception;
use Google_Service_Drive;
use Google\Client as Google_Client;
use Google_Service_Drive_DriveFile;

class Drive {

    protected $drive;
    protected $credentials;

    public function __construct()
    {
        $this->initialize();
        $this->prepareToken();
    }

    public function initialize(){
        $this->credentials = public_path( 'secret/credentials.json' );

        $this->drive = new Google_Client();
        $this->drive->setApplicationName('Laravel Google Drive');
        $this->drive->setScopes(Google_Service_Drive::DRIVE);
        $this->drive->setAuthConfig( $this->credentials );
        $this->drive->setAccessType('offline');
        $this->drive->setPrompt('select_account consent');
    }

    // here we should take care of access token
    public function getTokenFile(){
        return public_path('secret/token.json');
    }

    public function prepareToken(){

        $info = [
            'is_token_valid' => false,
            'auth_url' => null,
        ];

        $token_file = $this->getTokenFile();
        if (file_exists( $token_file )) {
            $accessToken = json_decode(file_get_contents( $token_file ), true);
            $this->drive->setAccessToken($accessToken);
            $info['is_token_valid'] = true;
        }

        if ($this->drive->isAccessTokenExpired()) {
            if ($this->drive->getRefreshToken()) {
                $this->drive->fetchAccessTokenWithRefreshToken($this->drive->getRefreshToken());
                $info['is_token_valid'] = true;
            } else {
                $info['is_token_valid'] = false;
                $info['auth_url'] = $this->drive->createAuthUrl();
            }
        }

        return $info;
    }

    public function prepareAuthCode( $code ){
        $is_success = false;
        try {
            $code = trim( $code );

            $accessToken = $this->drive->fetchAccessTokenWithAuthCode($code);
            $this->drive->setAccessToken($accessToken);

            // Check to see if there was an error.
            if (array_key_exists('error', $accessToken)) {
                throw new Exception(join(', ', $accessToken));
            }

            // Save the token to a file.
            $tokenPath = $this->getTokenFile();
            if (!file_exists(dirname($tokenPath))) {
                mkdir(dirname($tokenPath), 0700, true);
            }
            file_put_contents($tokenPath, json_encode($this->drive->getAccessToken()));
            $is_success = true;
        } catch (\Exception $th) {
            $is_success = false;
            throw $th;
        }

        return $is_success;
        
    }

    public function uploadToDrive( $file_name, $file_path ){

        $service = new Google_Service_Drive( $this->drive );
        $file = new Google_Service_Drive_DriveFile();
        $file_path = $file_path."/".$file_name;
        $folder_id = $this->createFolder( "my-drive-folder" );

        $file->setName( $file_name );

        if( !empty( $folder_id ) ){
            $file->setParents( [ $folder_id ] );        
        }

        $result = $service->files->create(
            $file,
            array(
                'data' => file_get_contents($file_path),
                'mimeType' => 'application/octet-stream',
            )
        );

        $is_success = false;
        
        if( isset( $result['name'] ) && !empty( $result['name'] ) ){
            $is_success = true;
        }

        return $is_success;
    }

    public function createFolder( $folder_name, $parent_folder_id=null ){

        $folder_list = $this->checkFolderExists( $folder_name );
    
        // if folder does not exists
        if( count( $folder_list ) == 0 ){
            $service = new Google_Service_Drive( $this->drive );
            $folder = new Google_Service_Drive_DriveFile();
        
            $folder->setName( $folder_name );
            $folder->setMimeType('application/vnd.google-apps.folder');
            if( !empty( $parent_folder_id ) ){
                $folder->setParents( [ $parent_folder_id ] );        
            }
    
            $result = $service->files->create( $folder );
        
            $folder_id = null;
            
            if( isset( $result['id'] ) && !empty( $result['id'] ) ){
                $folder_id = $result['id'];
            }
        
            return $folder_id;
        }
    
        return $folder_list[0]['id'];
        
    }

    public function checkFolderExists( $folder_name ){
    
        $service = new Google_Service_Drive( $this->drive );
    
        $parameters['q'] = "mimeType='application/vnd.google-apps.folder' and name='$folder_name' and trashed=false";
        $files = $service->files->listFiles($parameters);
    
        $op = [];
        foreach( $files as $k => $file ){
            $op[] = $file;
        }
    
        return $op;
    }

}

Explanation of Drive Class

initialize() method

The initialize() method initializes Google_Client class and we’ll also pass $this->credentials.

getTokenFile() Method

Simply returns the path of the token file.

prepareToken() Method

Extracts the data from the token file and check if the access code is valid and if not then refreshes the token or else returns google drive login URL.

prepareAuthCode() Method

The response returned by google drive which calls the callback URL drive_callback this method is called in controller. It takes a single argument of verification code and via that verification code it generates an access token and also updates the token.json file with new access token information.

createFolder() Method

This method creates a folder with the given name and if the folder already exists then simple returns its id.

checkFolderExists() Method

Checks whether the folder with the given name exists in the drive.

uploadToDrive() method

As the name says, this method will upload files to google drive inside the folder called my-drive-folder.

Setting up routes

In web.php

Route::get( '',  [ BackupController::class, 'index' ] );
Route::get( 'drive_callback',  [ BackupController::class, 'fetchVerificateCode' ] );
Route::get( 'upload-file',  [ BackupController::class, 'sendToDrive' ] );

Routes Explanation

  • default route: This is the entry point of the application and will take to the login page of google drive
  • drive_callback: Callback from google drive here we’ll receive the verification code which in turn will generate our access token.
  • upload-file: Our project is zipped and the file will be uploaded to drive.
Udemy Laravel Course for Beginners
Udemy Laravel Course for Beginners

Setting up Controllers

We’ll be calling the drive methods within the controller itself and also after successful login we have to zip the directory which in our case is the root directory and we’ll be zipping all the files.

In app\Http\Controllers\BackupController.php

<?php
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Utils\Drive;
use Illuminate\Http\Request;
use DirectoryIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;

class BackupController extends Controller{

    public function index( Request $request ){
        
        $token_info = ( new Drive() )->prepareToken();
        
        $info = [
            'is_authenticated' => ( $token_info['is_token_valid'] )? true : false,
            'drive_login_url' => $token_info['auth_url'],
        ];

        return view( 'backups.default', $info );
    }

    public function fetchVerificateCode( Request $request ){
        
        $code = $request->code;

        $is_success = ( new Drive() )->prepareAuthCode( $code );

        if( $is_success ){
            echo "You have logged successfully.";
        } else {
            echo "You have error while login.";
        }
        
    }

    public function zipApplicationFiles( ){

        // Get real path for our folder
        $rootPath = realpath('.');

        // Initialize archive object
        $zip_file = public_path('zip_files_backups/files.zip');
        $zip = new ZipArchive();
        $zip->open($zip_file, ZipArchive::CREATE | ZipArchive::OVERWRITE);

        // Create recursive directory iterator
        /** @var SplFileInfo[] $files */
        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($rootPath),
            RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($files as $name => $file)
        {
            // Skip directories (they would be added automatically)
            if (!$file->isDir())
            {
                // Get real and relative path for current file
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($rootPath) + 1);

                // Add current file to archive
                $zip->addFile($filePath, $relativePath);
            }
        }

        // Zip archive will be created only after closing object
        $zip->close();

        return $zip_file;
    }


    public function sendToDrive(){
        $res = ( new Drive() )->uploadToDrive( "files.zip", public_path('zip_files_backups') );
    }
}

In resources\views\backups\default.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- We'll check where the user has logged if not than will ask him for login. -->
    @if( !$is_authenticated )
        <a href="{{ $drive_login_url }}">Login to Drive</a>
    @endif

</body>
</html>

Explaination of BackupController

index() Method

In this method, we’ll check if the user has logged in successfully in google drive or else the key is_authenticated will be false.
The key drive_login_url is the login URL of google drive.

fetchVerificateCode() method

This method is the callback method after you have successfully logged into google drive. You’ll also receive a code parameter with $_GET a request which in turn used to generate an access token.

zipApplicationFiles() Method

This method zips all the directories and files of the application and sends it to google drive.

Conclusion

You have reached the end of the post on Laravel Google Drive API – The Best way to take a full backup of your app. If you have any queries then comment below and we’ll surely reach you.

Related Posts

Summary
Review Date
Reviewed Item
Laravel Google Drive Api - The Best way to take full backup of your app
Author Rating
51star1star1star1star1star
Software Name
Laravel Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development