diff --git a/docs/cleaning-up-old-backups/overview.md b/docs/cleaning-up-old-backups/overview.md index 183eef56..19dcd671 100644 --- a/docs/cleaning-up-old-backups/overview.md +++ b/docs/cleaning-up-old-backups/overview.md @@ -11,6 +11,11 @@ You can clean up your backups by running: php artisan backup:clean ``` +If you want to clean a backup with a specific configuration with a specific configuration, run: +```bash +php artisan backup:clean --config=backup +``` + We'll tell you right off the bat that the package by default will never delete the latest backup regardless of its size or age. ## Determining which backups should be deleted diff --git a/docs/taking-backups/overview.md b/docs/taking-backups/overview.md index d656694f..e2d76565 100644 --- a/docs/taking-backups/overview.md +++ b/docs/taking-backups/overview.md @@ -104,7 +104,32 @@ This section of the configuration determines which files and databases will be b The specified databases will be dumped and, together with the selected files, zipped. The zip file will be named`/.zip`. The more files you need to backup, the bigger the zip will become. Make sure there's enough free space on your disk to create the zip file. After the source zip file has been copied to all destinations, it will be deleted. - + +### Running backups with a specific configuration +If you want to back up different areas of your Laravel application separately – for example with different schedules, database connections, filesystem disks, or cleanup settings – you can create custom backup configuration files. + +#### Example: +Additional config files placed in the config/ directory: + +- config/backup_database.php +- config/backup_invoices.php +- config/backup_uploads.php + +You can then run backups and cleanup commands individually: + +```bash +php artisan backup:run --config=backup_database +php artisan backup:clean --config=backup_database + +php artisan backup:run --config=backup_invoices +php artisan backup:clean --config=backup_invoices + +php artisan backup:run --config=backup_uploads +php artisan backup:clean --config=backup_uploads +``` + +This allows full flexibility in scheduling, retention, and target destinations for each backup scope. + ### Determining the destination of the backup The zipped backup can be copied to one or more filesystems. This section of the configuration is where you specify those destination filesystems. diff --git a/src/Commands/BackupCommand.php b/src/Commands/BackupCommand.php index 3ddeb225..b17217ca 100644 --- a/src/Commands/BackupCommand.php +++ b/src/Commands/BackupCommand.php @@ -15,7 +15,7 @@ class BackupCommand extends BaseCommand implements Isolatable { use Retryable; - protected $signature = 'backup:run {--filename=} {--only-db} {--db-name=*} {--only-files} {--only-to-disk=} {--disable-notifications} {--timeout=} {--tries=}'; + protected $signature = 'backup:run {--filename=} {--only-db} {--db-name=*} {--only-files} {--only-to-disk=} {--disable-notifications} {--timeout=} {--tries=} {--config=}'; protected $description = 'Run the backup.'; @@ -34,6 +34,10 @@ public function handle(): int set_time_limit((int) $this->option('timeout')); } + if ($this->option('config')) { + $this->config = Config::fromArray(config($this->option('config') ?? 'backup')); + } + try { $this->guardAgainstInvalidOptions(); @@ -92,8 +96,8 @@ public function handle(): int if (! $disableNotifications) { event( $exception instanceof BackupFailed - ? new BackupHasFailed($exception->getPrevious(), $exception->backupDestination) - : new BackupHasFailed($exception) + ? new BackupHasFailed($exception->getPrevious(), $exception->backupDestination) + : new BackupHasFailed($exception) ); } diff --git a/src/Commands/CleanupCommand.php b/src/Commands/CleanupCommand.php index be4b5332..76f1b0f7 100644 --- a/src/Commands/CleanupCommand.php +++ b/src/Commands/CleanupCommand.php @@ -16,7 +16,7 @@ class CleanupCommand extends BaseCommand implements Isolatable use Retryable; /** @var string */ - protected $signature = 'backup:clean {--disable-notifications} {--tries=}'; + protected $signature = 'backup:clean {--disable-notifications} {--tries=} {--config=}'; /** @var string */ protected $description = 'Remove all backups older than specified number of days in config.'; @@ -36,6 +36,10 @@ public function handle(): int $this->setTries('cleanup'); + if ($this->option('config')) { + $this->config = Config::fromArray(config($this->option('config') ?? 'backup')); + } + try { $backupDestinations = BackupDestinationFactory::createFromArray($this->config); diff --git a/tests/Commands/BackupCommandTest.php b/tests/Commands/BackupCommandTest.php index a51564ae..92878f2c 100644 --- a/tests/Commands/BackupCommandTest.php +++ b/tests/Commands/BackupCommandTest.php @@ -514,3 +514,29 @@ Storage::disk('local')->assertExists($this->expectedZipPath); }); + +it('can backup with runtime changed configuration', function () { + $this->date = Carbon::create('2025', 8, 1, 10, 1, 1); + Carbon::setTestNow($this->date); + + config()->set('backup.backup.destination.filename_prefix', 'prefix1_'); + $this->expectedZipPath = 'mysite/prefix1_2025-08-01-10-01-01.zip'; + $this->artisan('backup:run', ['--only-files' => true])->assertExitCode(0); + + Storage::disk('local')->assertExists($this->expectedZipPath); + Storage::disk('secondLocal')->assertExists($this->expectedZipPath); + + // Now change the configuration + config()->set('backup.backup.destination.filename_prefix', 'prefix2_'); + $this->expectedZipPath = 'mysite/prefix2_2025-08-01-10-01-01.zip'; + + // Run again without the config option, files should not exist + $this->artisan('backup:run', ['--only-files' => true])->assertExitCode(0); + Storage::disk('local')->assertMissing($this->expectedZipPath); + Storage::disk('secondLocal')->assertMissing($this->expectedZipPath); + + // Run again with specified configuration, backup should be created + $this->artisan('backup:run', ['--only-files' => true, '--config' => 'backup'])->assertExitCode(0); + Storage::disk('local')->assertExists($this->expectedZipPath); + Storage::disk('secondLocal')->assertExists($this->expectedZipPath); +});