Skip to content

Commit

Permalink
Merge pull request #4 from WatheqAlshowaiter/feature/console-command
Browse files Browse the repository at this point in the history
Feature/console command @coderabbitai
  • Loading branch information
WatheqAlshowaiter authored Jan 28, 2025
2 parents 2b53f11 + 70fb63d commit 9ee12f3
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 19 deletions.
51 changes: 36 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

Backup single or multiple database tables with ease.

> Note: if you want a full database backup with many features go for [Spatie Laravel Backup](https://github.com/spatie/laravel-backup).
> [!NOTE]
> If you want a full database backup with many features, go
> for [Spatie Laravel Backup](https://github.com/spatie/laravel-backup).
## Installation

Expand All @@ -26,6 +28,9 @@ Use the `BackupTables::generateBackup($tableToBackup)` Facade anywhere in your a
generate `$tableToBackup_backup_2024_08_22_17_40_01` table in the database with all the data and structure. Note that
the datetime `2024_08_22_17_40_01` will be varied based on your datetime.

You can also use the `php artisan backup:tables <targets>` command to back up tables,
where `<targets>` is a space-separated list of table names or models.

```php
use WatheqAlshowaiter\BackupTables\BackupTables; // import the facade

Expand All @@ -35,14 +40,14 @@ class ChangeSomeData
{
BackupTables::generateBackup('users'); // will result: users_backup_2024_08_22_17_40_01

// change some data..
// change some data..
}
}
```

And More Customizations

- You can use an array to backup more than one table
- You can use an array to back up more than one table

```php
BackupTables::generateBackup(['users', 'posts']);
Expand All @@ -55,7 +60,8 @@ BackupTables::generateBackup(['users', 'posts']);
```php
BackupTables::generateBackup(User::class); // users_backup_2024_08_22_17_40_01
// or
BackupTables::generateBackup([User::class, Post::class]); // users_backup_2024_08_22_17_40_01, posts_backup_2024_08_22_17_40_01
BackupTables::generateBackup([User::class, Post::class]); //-php artisan backup:tables users posts # users_backup_2024_08_22_17_40_01, posts_backup_2024_08_22_17_40_01
users_backup_2024_08_22_17_40_01, posts_backup_2024_08_22_17_40_01

```

Expand All @@ -65,25 +71,38 @@ BackupTables::generateBackup([User::class, Post::class]); // users_backup_2024_0
BackupTables::generateBackup('users', 'Y_d_m_H_i'); // users_backup_2024_22_08_17_40
```

> *Note: be aware if you customize the datetime to wide datetime the package will check the backup datetime file and
> will be skipped
> the exact same datetime, so most of the time the default will be fine
> For example: if you use this `Y_d_m_H` you can not generate the same backup in the same hour
> [!WARNING]
> When customizing the datetime format, be aware that backups with identical datetime values will be skipped.
> For example, if you use this `Y_d_m_H` you cannot generate the same backup in the same hour.
> The default format (Y_m_d_H_i_s) is recommended for most cases.
```php
BackupTables::generateBackup('users', 'Y_d_m_H'); // can not generate the same backup in the same hour
BackupTables::generateBackup('users', 'Y_d_m'); // can not generate the same backup in the same day
```

- Using the artisan command for one or more tables/models

```bash
php artisan backup:tables users posts # users_backup_2024_08_22_17_40_01, posts_backup_2024_08_22_17_40_01
php artisan backup:tables \\App\\Models\\User \\App\\Models\\Post # users_backup_2024_08_22_17_40_01, posts_backup_2024_08_22_17_40_01
```

## Why?

Sometimes you want to backup some database tables before changing data for whatever reason, this package serves this
need. I used it personally before adding foreign keys for tables that required the removal of unlinked fields for parent tables.
You may find some situation where you play with table data or you're afraid of missing data so you backup these tables
Sometimes you want to back up some database tables before changing data for whatever reason, this package serves this
need.

I used it personally before adding foreign keys to tables that required removing unlinked fields from parent tables.

You may find some situation where you play with table data, or you're afraid of missing data, so you back up these
tables
beforehand.

## Features

✅ Backup tables from the code using (Facade) or from the console command.

✅ Supports Laravel versions: 11, 10, 9, 8, 7, and 6.

✅ Supports PHP versions: 8.2, 8.1, 8.0, and 7.4.
Expand All @@ -102,11 +121,15 @@ composer test

## Changelog

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
Please see [CHANGELOG](CHANGELOG.md) for more information on recent changes.

## Contributing

If you have any ideas or suggestions to improve it or fix bugs, your contribution is welcome. I encourage you to look at [todos](./todos.md) which are the most important features that need to be added. If you have something different, submit an issue first to discuss or report a bug, then do a pull request.
If you have any ideas or suggestions to improve it or fix bugs, your contribution is welcome.

I encourage you to look at [todos](./todos.md) which are the most important features that need to be added.

If you have something different, submit an issue first to discuss or report a bug, then do a pull request.

## Security Vulnerabilities

Expand All @@ -118,11 +141,9 @@ them.
- [Watheq Alshowaiter](https://github.com/WatheqAlshowaiter)
- [Omar Alalwi](https://github.com/omaralalwi) - This package is based on his initial code.
- [All Contributors](../../contributors)


And a special thanks to [The King Creative](https://www.facebook.com/thkingcreative) for the logo ✨


## License

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
2 changes: 1 addition & 1 deletion src/BackupTablesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ protected function returnedBackupResponse($newTableName, $table): array
'newCreatedTables' => "Newly created table: $newTableName",
];

// to prevent duplicating message if you use generateBackup() twice in the same request event for different tables
// to prevent a duplicating message if you use generateBackup() twice in the same request event for different tables
Arr::forget($this->response, '0');

return $result;
Expand Down
12 changes: 9 additions & 3 deletions src/BackupTablesServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace WatheqAlshowaiter\BackupTables;

use Illuminate\Support\ServiceProvider;
use WatheqAlshowaiter\BackupTables\Commands\BackupTableCommand;

class BackupTablesServiceProvider extends ServiceProvider
{
Expand All @@ -13,9 +14,14 @@ public function register()

public function boot()
{
// This migration works only in the package test
if ($this->app->runningInConsole() && $this->app->environment() === 'testing') {
$this->loadMigrationsFrom(__DIR__.'/../tests/database/migrations');
if ($this->app->runningInConsole()) {
$this->commands([
BackupTableCommand::class,
]);

if ($this->app->environment() === 'testing') {
$this->loadMigrationsFrom(__DIR__.'/../tests/database/migrations');
}
}
}
}
38 changes: 38 additions & 0 deletions src/Commands/BackupTableCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace WatheqAlshowaiter\BackupTables\Commands;

use Illuminate\Console\Command;
use WatheqAlshowaiter\BackupTables\BackupTables;

class BackupTableCommand extends Command
{
const SUCCESS = 0;

const FAILURE = 1;

protected $signature = 'backup:tables {targets* : The table names or model classes to backup (space-separated)}';

protected $description = 'Backup a specific database table/s based on provided table names or model classes';

public function handle()
{
$tables = $this->argument('targets');

try {
$result = BackupTables::generateBackup($tables);

if (! $result) {
$this->error('Failed to backup table.');

return self::FAILURE;
}

return self::SUCCESS;
} catch (\Exception $e) {
$this->error("Error backing up table: {$e->getMessage()}");

return self::FAILURE;
}
}
}
115 changes: 115 additions & 0 deletions tests/BackupTableCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace WatheqAlshowaiter\BackupTables\Tests;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;
use WatheqAlshowaiter\BackupTables\Commands\BackupTableCommand;
use WatheqAlshowaiter\BackupTables\Tests\Models\Father;
use WatheqAlshowaiter\BackupTables\Tests\Models\Mother;

class BackupTableCommandTest extends TestCase
{
use RefreshDatabase;

/** @test */
public function it_can_backup_a_table()
{
Schema::create('test_table', function ($table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});

$this->artisan('backup:tables', ['targets' => 'test_table'])
->assertExitCode(BackupTableCommand::SUCCESS);

$backupTablePattern = 'test_table_backup_'.now()->format('Y_m_d_H_i_s');

$this->assertTrue(Schema::hasTable($backupTablePattern));
}

/** @test */
public function it_can_backup_a_table_by_classname()
{
Schema::create('test_table', function ($table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});

$this->artisan(BackupTableCommand::class, ['targets' => 'test_table'])
->assertExitCode(BackupTableCommand::SUCCESS);

$backupTablePattern = 'test_table_backup_'.now()->format('Y_m_d_H_i_s');

$this->assertTrue(Schema::hasTable($backupTablePattern));
}

/** @test */
public function it_fails_when_table_does_not_exist()
{
$this->artisan('backup:tables', ['targets' => 'non_existent_table'])
->assertExitCode(BackupTableCommand::FAILURE);
}

/** @test */
public function it_can_backup_multiple_tables()
{
$tables = ['test_table_1', 'test_table_2'];

foreach ($tables as $table) {
Schema::create($table, function ($table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}

$this->artisan('backup:tables', ['targets' => $tables])
->assertExitCode(BackupTableCommand::SUCCESS);

foreach ($tables as $table) {
$backupTablePattern = $table.'_backup_'.now()->format('Y_m_d_H_i_s');

$this->assertTrue(Schema::hasTable($backupTablePattern));
}

}

/** @test */
public function it_can_backup_multiple_models()
{
$models = [Father::class, Mother::class];
$now = now();

$this->artisan('backup:tables', ['targets' => $models])
->assertExitCode(BackupTableCommand::SUCCESS);

$backupTablePattern1 = 'fathers_backup_'.$now->format('Y_m_d_H_i_s');
$backupTablePattern2 = 'mothers_backup_'.$now->format('Y_m_d_H_i_s');

$this->assertTrue(Schema::hasTable($backupTablePattern1));

$this->assertTrue(Schema::hasTable($backupTablePattern2));
}

/** @test */
public function it_fails_when_any_table_does_not_exist_but_saves_corrected_tables()
{
Schema::create('existing_table', function ($table) {
$table->bigIncrements('id');
$table->timestamps();
});

$this->artisan('backup:tables', ['targets' => 'existing_table', 'non_existent_table'])
->assertExitCode(BackupTableCommand::SUCCESS);

$backupExistingTablePattern = 'existing_table_backup_'.now()->format('Y_m_d_H_i_s');
$backupNonExistingTablePattern = 'non_existent_table_backup_'.now()->format('Y_m_d_H_i_s');

$this->assertTrue(Schema::hasTable($backupExistingTablePattern));

$this->assertFalse(Schema::hasTable($backupNonExistingTablePattern));
}
}

0 comments on commit 9ee12f3

Please sign in to comment.