Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Flysystem Google Drive Ext Laravel Package

masbug/flysystem-google-drive-ext

Flysystem adapter for Google Drive that hides Google’s file/folder IDs by translating between virtual ID paths and human-friendly display paths. Supports Flysystem v2/v3 (Laravel 9+) with seamless path mapping for common filesystem operations.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Install the Package**
   ```bash
   composer require masbug/flysystem-google-drive-ext

Ensure league/flysystem (v2+) is also installed.

  1. Configure Google Drive Credentials Store your Google OAuth credentials in .env:

    GOOGLE_DRIVE_CLIENT_ID=your_client_id
    GOOGLE_DRIVE_CLIENT_SECRET=your_client_secret
    GOOGLE_DRIVE_REDIRECT_URI=your_redirect_uri
    GOOGLE_DRIVE_TOKEN=your_refresh_token  # Optional: Pre-saved token for testing
    
  2. Define a Filesystem in config/filesystems.php

    'disks' => [
        'google-drive' => [
            'driver' => 'google-drive',
            'client_id' => env('GOOGLE_DRIVE_CLIENT_ID'),
            'client_secret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
            'redirect_uri' => env('GOOGLE_DRIVE_REDIRECT_URI'),
            'token' => env('GOOGLE_DRIVE_TOKEN'),
            'root_folder_id' => env('GOOGLE_DRIVE_ROOT_FOLDER_ID', 'root'), // Optional: Custom root
            'virtual_root' => env('GOOGLE_DRIVE_VIRTUAL_ROOT', '/'), // Maps to display paths
        ],
    ],
    
  3. First Use Case: Upload a File

    use Illuminate\Support\Facades\Storage;
    
    Storage::disk('google-drive')->put('path/in/drive/my-file.txt', 'Hello, Drive!');
    
    • The adapter automatically translates path/in/drive to Google Drive’s virtual ID path.

Implementation Patterns

Core Workflows

  1. Path Translation

    • Virtual ↔ Display Paths: The adapter handles conversion between Google’s opaque IDs (e.g., /Xa3X9GlR6EmbnY1RLVTk5VUtOVkk) and user-friendly paths (e.g., /My Nice Dir/).
    • Example:
      // Upload with display path
      Storage::disk('google-drive')->put('projects/report.pdf', file_get_contents('local.pdf'));
      
      // Download using virtual path (hidden by adapter)
      $contents = Storage::disk('google-drive')->get('Xa3X9GlR6EmbnY1RLVTk5VUtOVkk');
      
  2. Directory Operations

    • Create directories with display paths:
      Storage::disk('google-drive')->mkdir('team/assets');
      
    • List files using display paths:
      $files = Storage::disk('google-drive')->files('team/');
      // Returns paths like ['team/report.pdf', 'team/image.png'] (not virtual IDs).
      
  3. Symlinks and Metadata

    • Use Flysystem’s standard methods (e.g., url(), size(), lastModified()) to work with metadata.
    • Symlinks: Not natively supported by Google Drive, but you can simulate them with metadata or custom logic.
  4. Streaming Large Files

    • Leverage Laravel’s Storage::disk()->writeStream() for large uploads:
      Storage::disk('google-drive')->writeStream('large-video.mp4', fopen('local.mp4', 'r'));
      

Integration Tips

  1. Laravel Filesystem Integration

    • Use the disk in config/filesystems.php to replace local/S3 storage for Google Drive.
    • Example in config/filesystems.php:
      'disks' => [
          'public' => [
              'driver' => 'google-drive',
              'root' => 'public', // Maps to a virtual root folder
          ],
      ],
      
  2. Customizing Virtual Roots

    • Override the default root (/) to map to a specific folder:
      'virtual_root' => 'company-projects/', // All paths prepended with this.
      
    • Useful for multi-tenant apps where each tenant has a dedicated Drive folder.
  3. OAuth Flow

    • For production, implement a custom OAuth handler (e.g., via a controller) to generate tokens:
      use Masbug\FlysystemGoogleDriveExt\GoogleDriveAdapter;
      
      $adapter = new GoogleDriveAdapter(
          $client,
          $rootFolderId,
          $virtualRoot = '/'
      );
      
    • Store tokens in the database or cache for persistence.
  4. Fallback for Missing Files

    • Handle FileNotFoundException gracefully:
      try {
          Storage::disk('google-drive')->get('nonexistent.pdf');
      } catch (\League\Flysystem\FileNotFoundException $e) {
          Log::warning("File not found in Google Drive: {$e->getMessage()}");
      }
      
  5. Testing

    • Use a mock adapter or a test Drive account with pre-uploaded files:
      $this->actingAs($user)
           ->fakeGoogleDrive() // Custom trait to mock responses
           ->assertUploadedToDrive('test.pdf');
      

Gotchas and Tips

Pitfalls

  1. Token Expiry

    • Google OAuth tokens expire. Implement a token refresh mechanism:
      // Example: Refresh token before operations
      $adapter->refreshToken($newRefreshToken);
      
    • Tip: Use Laravel’s Auth or Cache to store tokens and auto-refresh.
  2. Path Case Sensitivity

    • Google Drive paths are case-sensitive. Ensure consistency:
      // Avoid:
      Storage::disk('google-drive')->put('File.TXT', $content); // May fail if 'file.txt' exists.
      
  3. Quota Limits

    • Google Drive has file size limits (max 5TB per file). Validate uploads:
      if (filesize('large-file.zip') > 5 * 1024 * 1024 * 1024) {
          throw new \Exception("File too large for Google Drive.");
      }
      
  4. Virtual Path Collisions

    • If two display paths resolve to the same virtual ID (e.g., /folder and /FOLDER), the adapter may overwrite files.
    • Fix: Normalize paths in your app (e.g., strtolower()).
  5. Permission Denied

    • Ensure the service account or OAuth user has edit access to the root folder.
    • Debug: Check Google’s API error messages for 403 responses.

Debugging

  1. Enable Logging

    • Add to config/logging.php:
      'channels' => [
          'google_drive' => [
              'driver' => 'single',
              'path' => storage_path('logs/google-drive.log'),
              'level' => 'debug',
          ],
      ],
      
    • Log virtual ↔ display path translations:
      \Log::debug('Virtual path:', [$virtualPath, $displayPath]);
      
  2. Inspect Virtual Paths

    • Dump the raw virtual path for debugging:
      $adapter = Storage::disk('google-drive')->getAdapter();
      $virtualPath = $adapter->applyVirtualPath('my-file.txt');
      dd($virtualPath); // Shows the actual Google Drive ID path.
      
  3. Common Errors

    • InvalidArgumentException: Invalid root folder ID or malformed paths.
    • Google_Service_Exception: API quota exceeded or permissions issue.

Extension Points

  1. Custom Path Translator

    • Extend the adapter to add custom path rules:
      use Masbug\FlysystemGoogleDriveExt\PathTranslator;
      
      class CustomTranslator extends PathTranslator {
          protected function translateToVirtual($displayPath) {
              // Add custom logic (e.g., tenant IDs)
              return parent::translateToVirtual($displayPath);
          }
      }
      
    • Bind the custom translator in a service provider:
      $this->app->bind(Masbug\FlysystemGoogleDriveExt\PathTranslator::class, function () {
          return new CustomTranslator();
      });
      
  2. Event Listeners

    • Listen for file operations (e.g., post-upload):
      Storage::disk('google-drive')->addListener('postWrite', function ($event) {
          Log::info("File uploaded: {$event->path()}");
      });
      
  3. Flysystem Events

    • Use Flysystem’s events for cross-cutting concerns:
      $adapter->addListener('file.written', function ($event) {
          // Trigger a webhook or update a database.
      });
      
  4. Testing Helpers

    • Create a trait for testing:
      trait GoogleDriveTestHelper {
          protected function assertFileExistsInDrive($path) {
              $this->assertTrue(
                  Storage::disk
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nasirkhan/laravel-sharekit
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony