grafite/database
Grafite Database provides handy database utilities and helpers for Laravel applications, streamlining common query and schema tasks so you can work with your data more efficiently.
grafite/database with an explicit constraint (composer require grafite/database:^1.0) rather than floating — with 4 stars and no dependents, you want reproducibility more than you want patches.vendor/grafite/database/composer.json and look at the illuminate/support (or laravel/framework) requirement before you write any code against it. If it caps at Laravel 5.x/6.x you'll need --ignore-platform-reqs or a fork; decide that up front, not after wiring it into a deploy script.config/app.php. Confirm the exact class name from the package's composer.json extra.laravel.providers block.php artisan list | grep -i -E 'db:|database|mysql'
and then php artisan help <command> for each hit. The command signatures (--database=, --path=, --force, etc.) are the actual API surface you'll be building on.php artisan vendor:publish --tag=grafite-database
If nothing publishes, the package reads straight from config/database.php — expect it to honour the named connections there.mysqldump / mysql. Confirm they're on the PATH of the user running PHP-FPM/CLI:
which mysqldump mysql
php artisan db:backup # or whatever the dump command is named
php artisan migrate
If the migration goes sideways you have a file to restore from — that's the whole value proposition of the package in one loop.Pre-migration snapshot in a deploy script. The most common day-to-day use. Wrap it so the deploy aborts if the dump fails:
php artisan db:backup --database=mysql || { echo "backup failed"; exit 1; }
php artisan migrate --force
Treat the dump's exit code as a gate — a silent backup failure that lets migrations proceed is worse than no backup.
Prod → local refresh. Dump on the remote host, stream down:
ssh prod 'cd /var/www/app && php artisan db:backup --path=storage/backups'
scp prod:/var/www/app/storage/backups/latest.sql storage/backups/
php artisan db:restore storage/backups/latest.sql --database=local
Set APP_ENV/--database explicitly — never rely on the default connection for a destructive import.
Scheduled backups. Register in app/Console/Kernel.php:
$schedule->command('db:backup')->dailyAt('02:30')->environments(['production']);
Pair with a retention sweep (a find storage/backups -mtime +14 -delete in the same job, or your own cleanup command), otherwise the disk fills quietly.
Schema-only vs. data. Keep two commands in your own wrapper: a full dump for DR, and --no-data (or equivalent) for seeding test environments with a realistic structure. Most packages expose one but not both cleanly — if yours doesn't, write a thin custom command that shells out to mysqldump --no-data.
CI/test database reset. Use the package's dump/restore to snapshot a seeded test database once, then restore between test suites instead of re-running migrate:fresh --seed. Massive wall-clock win on suites with heavy fixtures. Cache the .sql file keyed on the migrations directory hash.
Wrap it in your own Artisan command. Because the upstream surface is small and unlikely to grow, the sustainable pattern is: keep the package purely as the dump/restore primitive, and put your team's workflow (naming, retention, S3 upload, Slack notification) in app/Console/Commands/DbSnapshot.php calling it via Artisan::call() or duplicating the Process invocation. That way an abandoned package costs you one file, not your whole backup story.
Store dumps off the app server. Pipe to object storage in the same job:
Storage::disk('s3')->put('backups/'.now()->format('Y-m-d').'.sql.gz', gzencode($dumpContents));
The package writes to local disk; the upload is your responsibility.
db:backup doesn't explode in a SQLite CI job.mysqldump version skew is the #1 failure. MySQL 8 client against a 5.7 server throws Unknown table 'COLUMN_STATISTICS' in information_schema. Fix with --column-statistics=0 (8.0.21+) or by aligning client/server versions. If the package doesn't expose extra flags, that's your first reason to write your own command.mysqldump -u… -p… from config/database.php, the password is visible in ps on shared hosts. Use --defaults-extra-file with a 0600 file if this matters to you — another argument for a thin custom wrapper.mysqldump omits triggers, routines, and events unless you pass --routines --triggers --events; views appear as tables in some configurations. Verify a restore actually reproduces your stored procedures before trusting it.--single-transaction (InnoDB) the dump locks tables. If the package doesn't pass it, production backups will stall writes. Test on a replica first.--force-style confirmation yourself in production, and always take a fresh dump before restoring. php artisan db:restore pointed at the wrong --database is an unrecoverable afternoon.memory_limit. Cap it or switch to a shell pipeline (mysqldump | gzip > …).storage/app or a backups/ path), add it to .gitignore, and make sure it's excluded from your deploy sync — otherwise you'll ship dev dumps to production.--database option, and the output path. Anything else means subclassing the command or replacing it. Plan accordingly — for anything production-critical, compare against spatie/laravel-backup (retention, destinations, health checks) or barryvdh/laravel-db-snapshots (streamed, driver-aware) before committing to this one.-vvv to see the underlying shell invocation — invaluable when a dump silently produces a zero-byte file.How can I help you explore Laravel packages today?