PHPackages                             purusottampanta/laravel-gdrive-backup - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. purusottampanta/laravel-gdrive-backup

ActiveLibrary[Queues &amp; Workers](/categories/queues)

purusottampanta/laravel-gdrive-backup
=====================================

Automatic incremental + weekly full backups of Eloquent models to a personal Google Drive account via OAuth2, driven entirely by config.

v1.0.2(today)011↑2900%MITPHPPHP ^8.2

Since Aug 9Pushed todayCompare

[ Source](https://github.com/purusottampanta/laravel-gdrive-backup)[ Packagist](https://packagist.org/packages/purusottampanta/laravel-gdrive-backup)[ Docs](https://github.com/purusottampanta/laravel-gdrive-backup)[ RSS](/packages/purusottampanta-laravel-gdrive-backup/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (8)Versions (4)Used By (0)

Google Drive Backup for Laravel
===============================

[](#google-drive-backup-for-laravel)

Automatic, config-driven backups of your Eloquent models to a **personal**Google Drive account (OAuth2, not a service account):

- Every `created` / `updated` / `deleted` on a configured model queues an incremental JSON event, uploaded to `backups/incremental/{date}/`.
- A weekly full snapshot exports every configured table as JSON Lines to `backups/full/{date}/`, then prunes old snapshots and superseded incremental cycles.
- A restore command replays the latest full snapshot plus every incremental event since, idempotently.
- One config flag turns the whole thing on or off — disabled means observers are never attached and nothing is scheduled, not just a runtime no-op.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10
- `QUEUE_CONNECTION=database` (or any queue driver — jobs are queued, never run sync)

Installation
------------

[](#installation)

```
composer require purusottampanta/laravel-gdrive-backup
```

Laravel's package auto-discovery registers `Puru\GdriveBackup\Providers\BackupServiceProvider` automatically — no manual provider registration needed.

Publish the config file:

```
php artisan vendor:publish --tag=gdrive-backup-config
```

This creates `config/backup.php` in your app. Migrations for the database queue (`jobs`, `job_batches`, `failed_jobs` tables) and the OAuth routes are auto-loaded by the package — you don't need to publish them unless you want to customize them, in which case:

```
php artisan vendor:publish --tag=gdrive-backup-migrations
php artisan vendor:publish --tag=gdrive-backup-routes
php artisan migrate
```

Configuration
-------------

[](#configuration)

Everything lives in `config/backup.php` and environment variables.

```
# .env

BACKUP_ENABLED=true

QUEUE_CONNECTION=database

GOOGLE_DRIVE_CLIENT_ID=
GOOGLE_DRIVE_CLIENT_SECRET=
GOOGLE_DRIVE_REDIRECT_URI=https://your-app.example.com/backup/google/callback
GOOGLE_DRIVE_HTTP_TIMEOUT=30

BACKUP_ROOT_FOLDER=backups
BACKUP_CHUNK_SIZE=500
BACKUP_KEEP_WEEKLY_SNAPSHOTS=4
BACKUP_WEEKLY_CRON="0 2 * * 0"
BACKUP_RETRY_CRON="*/15 * * * *"
```

### The single on/off switch

[](#the-single-onoff-switch)

```
// config/backup.php
'enabled' => (bool) env('BACKUP_ENABLED', true),
```

Set `BACKUP_ENABLED=false` in any environment (e.g. local, staging, a throwaway QA box) and:

- Model observers are **never attached** — `BackupServiceProvider` skips `Model::observe()` entirely at boot, so create/update/delete on your models has zero backup-related overhead, not just a skipped upload.
- The weekly snapshot and failed-upload-retry schedules are **never registered** with the Laravel scheduler.
- `backup:snapshot`, `backup:restore`, and `backup:retry-failed` refuse to run and print a warning, unless you pass `--force`.
- Any job already on the queue from before you flipped the switch off will log and no-op instead of calling the Google API.

Flip it back to `true` and everything re-attaches on the next request/deploy — no code changes required.

### Registering your models

[](#registering-your-models)

```
// config/backup.php
'models' => [
    \App\Models\Consignment::class => 'consignments',
    \App\Models\Box::class => 'boxes',
    \App\Models\Item::class => 'items',
    \App\Models\Location::class => 'locations',
    \App\Models\LabelRequest::class => 'label_requests',
    \App\Models\ThirdPartyDetail::class => 'third_party_details',
    \App\Models\Bill::class => 'bills',
    \App\Models\BillDetail::class => 'bill_details',
],
```

Add or remove entries here per project — nothing else needs to change. Optionally implement the marker interface for type clarity:

```
final class Consignment extends Model implements \Puru\GdriveBackup\Contracts\Backupable
{
    public function backupTableName(): string
    {
        return 'consignments';
    }
}
```

Google OAuth setup
------------------

[](#google-oauth-setup)

1. In Google Cloud Console, create an OAuth 2.0 Client ID (type "Web application") with scope `https://www.googleapis.com/auth/drive.file`and the redirect URI from `GOOGLE_DRIVE_REDIRECT_URI` above.
2. Restrict `routes/backup.php`'s `auth` middleware (or add your own gate) so only a trusted operator can authorize/re-authorize access — completing the flow grants Drive access under their personal account.
3. Visit `/backup/google/redirect` while logged in as that operator and complete Google's consent screen.
4. The resulting token is encrypted (Laravel `Crypt`, so it's tied to your `APP_KEY`) and stored at `storage/app/google/oauth.enc` — never in the database. It refreshes itself automatically thereafter.

Running it
----------

[](#running-it)

```
# Queue worker (any host that allows a long-running process, or a
# cron-triggered worker on shared hosting):
php artisan queue:work --queue=backups

# Single cron entry drives the scheduler (weekly snapshot + retry sweep):
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
```

Manual commands:

```
php artisan backup:snapshot [--sync] [--force]
php artisan backup:restore [--date=YYYY-MM-DD] [--force]
php artisan backup:retry-failed [--force]
```

Testing
-------

[](#testing)

The package ships with a full PHPUnit + Orchestra Testbench suite that never touches the real Google API — all Drive calls go through an in-memory fake (`tests/Support/FakeGoogleDriveClient.php`), and OAuth HTTP calls are covered separately using Laravel's `Http::fake()`.

```
composer install
composer test
# or directly:
vendor/bin/phpunit
# single file / filter:
vendor/bin/phpunit tests/Unit/Services/RestoreServiceTest.php
vendor/bin/phpunit --filter test_restore_is_idempotent
```

What's covered:

- **DTOs** — `IncrementalEventDTO` (filename generation, JSON round-trip), `OAuthTokenDTO` (expiry/leeway logic, immutable refresh).
- **`BackupFolderResolver`** — folder hierarchy creation and caching (no duplicate folders on repeated calls).
- **`IncrementalBackupService`** — uploads land in the correct dated folder with exact JSON contents.
- **`FullBackupService`** — chunked JSONL export, including the zero-records case.
- **`RetentionService`** — keeps exactly N full snapshots, prunes superseded incremental day-folders, cascades deletes to children.
- **`RestoreService`** — full snapshot restore, chronological incremental replay (independent of upload order), idempotency, "no snapshot available" failure, "latest snapshot" resolution.
- **`GoogleOAuthService`** — consent URL construction, code exchange, encrypted-at-rest token persistence (asserts the raw file never contains the plaintext token), cached-vs-refreshed access token logic, missing-refresh-token failure.
- **The enable/disable switch** — a dedicated test class (`tests/Feature/DisabledSwitchTest.php`) boots the app with `backup.enabled = false` already set, then asserts the observer is never attached (no job dispatched on model changes) and commands refuse to run without `--force` — verifying the boot-time behavior, not just a runtime short-circuit.
- **`BackupableObserver`** — dispatches the right job with the right payload on create/update/delete, and is silent for unregistered models.
- **Jobs** — `UploadIncrementalBackupJob` skips work while disabled, uploads correctly while enabled, and persists a replayable local file in `failed()`; `RetryFailedUploadsJob` re-dispatches and clears persisted failures, leaving malformed files alone for manual review.
- **Artisan commands** — `backup:snapshot`, `backup:restore` (including the confirmation prompt, `--force`, `--date` validation, and a full restore run against the fake Drive), and `backup:retry-failed`.
- **Service provider** — config merging and the Drive client singleton binding.

Run this before every tag/release, and definitely before your first `composer require your-vendor/gdrive-backup` in a real project.

Publishing this package yourself
--------------------------------

[](#publishing-this-package-yourself)

To ship this under your own name on GitHub + Packagist:

1. Rename the `Puru\GdriveBackup` namespace throughout `src/` (and in `composer.json`'s `autoload.psr-4` / `extra.laravel.providers`) to your own vendor namespace, e.g. `YourOrg\GdriveBackup`.
2. Update `composer.json`'s `name` field to `your-vendor/gdrive-backup`and the `authors` block.
3. `git init`, commit, push to a new GitHub repo named to match (e.g. `your-vendor/gdrive-backup`).
4. Tag a release: `git tag v1.0.0 && git push --tags`.
5. On [packagist.org](https://packagist.org), "Submit" the GitHub repo URL. Enable the GitHub Service Hook (Packagist prompts for this) so new tags auto-publish.
6. In any project: `composer require your-vendor/gdrive-backup`.

For private/internal use without Packagist, add a VCS repository entry to the consuming project's `composer.json` instead:

```
{
    "repositories": [
        { "type": "vcs", "url": "https://github.com/your-vendor/gdrive-backup" }
    ]
}
```

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance100

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/aa50ec436259d961e8da6c3262975721d2e4176ae9aade4d68787509fb12d1de?d=identicon)[purusottampanta](/maintainers/purusottampanta)

---

Top Contributors

[![purusottampanta](https://avatars.githubusercontent.com/u/24473007?v=4)](https://github.com/purusottampanta "purusottampanta (9 commits)")

---

Tags

laravelbackupqueueoauth2google-driveshared hosting

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/purusottampanta-laravel-gdrive-backup/health.svg)

```
[![Health](https://phpackages.com/badges/purusottampanta-laravel-gdrive-backup/health.svg)](https://phpackages.com/packages/purusottampanta-laravel-gdrive-backup)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k31.8M158](/packages/laravel-cashier)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

79227.1M208](/packages/laravel-mcp)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9922.4M146](/packages/roots-acorn)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1239.7k25](/packages/fleetbase-core-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
