Skip to content

Commit 6c3040d

Browse files
author
Andrey Helldar
authored
Merge pull request #15 from Jagdish-J-P/main
Added New Options
2 parents 2fc5818 + 2ffc217 commit 6c3040d

File tree

10 files changed

+780
-29
lines changed

10 files changed

+780
-29
lines changed

.github/workflows/lint-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ jobs:
1111
uses: actions/checkout@v2
1212

1313
- name: Checking PHP Syntax
14-
uses: TheDragonCode/[email protected].5
14+
uses: TheDragonCode/[email protected].7

.github/workflows/lint-fixer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ jobs:
1313
uses: actions/checkout@v2
1414

1515
- name: Checking PHP Syntax
16-
uses: TheDragonCode/[email protected].6
16+
uses: TheDragonCode/[email protected].7
1717
with:
1818
fix: true

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
To get the latest version of `Migrate DB`, simply require the project using [Composer](https://getcomposer.org):
1616

1717
```bash
18-
$ composer require dragon-code/migrate-db --dev
18+
composer require dragon-code/migrate-db --dev
1919
```
2020

2121
Or manually update `require-dev` block of `composer.json` and run `composer update`.
@@ -49,15 +49,27 @@ the [config/database.php](https://github.com/laravel/laravel/blob/master/config/
4949
parameters:
5050

5151
```bash
52-
$ php artisan db:migrate --schema-from=foo --schema-to=bar
52+
php artisan db:migrate --schema-from=foo --schema-to=bar
53+
```
54+
55+
### Only Specific Tables
56+
57+
```bash
58+
php artisan db:migrate --schema-from=foo --schema-to=bar --tables=table1 --tables=table2 --tables=table3
59+
```
60+
61+
### Exclude Specific Tables
62+
63+
```bash
64+
php artisan db:migrate --schema-from=foo --schema-to=bar --exclude=table1 --exclude=table2 --exclude=table3
5365
```
5466

5567
where:
5668

5769
* `foo` - Source [connection](https://github.com/laravel/laravel/blob/master/config/database.php) name
5870
* `bar` - Target [connection](https://github.com/laravel/laravel/blob/master/config/database.php) name
5971

60-
The command will perform all migrations on the source and destination databases and transfer all records from the old to the new one.
72+
Follow on screen instructions and then command will perform all migrations on the source and destination databases and transfer all records from the old to the new one.
6173

6274
Enjoy 😊
6375

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"require": {
2727
"php": "^7.3 || ^8.0",
2828
"ext-pdo": "*",
29+
"doctrine/dbal": "^2.6 || ^3.0",
2930
"dragon-code/contracts": "^2.15",
3031
"dragon-code/support": "^5.6",
3132
"illuminate/contracts": "^8.0 || ^9.0",

src/Console/Migrate.php

Lines changed: 212 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010
use Illuminate\Database\Query\Builder as QueryBuilder;
1111
use Illuminate\Support\Collection;
1212
use Illuminate\Support\Facades\DB;
13+
use Illuminate\Support\Facades\Log;
14+
use Illuminate\Support\Facades\Schema;
1315

1416
class Migrate extends Command
1517
{
1618
protected $signature = 'db:migrate'
17-
. ' {--schema-from= : Source connection name}'
18-
. ' {--schema-to= : Target connection name}';
19+
. ' {--schema-from= : Source connection name}'
20+
. ' {--schema-to= : Target connection name}'
21+
. ' {--exclude=* : Comma separated table names to exclude}'
22+
. ' {--tables=* : Comma separated table names to migrate only}';
1923

2024
protected $description = 'Data transfer from one database to another';
2125

@@ -25,56 +29,198 @@ class Migrate extends Command
2529
/** @var \DragonCode\Contracts\MigrateDB\Builder */
2630
protected $target;
2731

32+
/** @var array */
33+
protected $tables;
34+
35+
/** @var array */
36+
protected $excludes;
37+
38+
/** @var bool */
39+
protected $retrieve_tables_from_target = false;
40+
41+
/** @var bool */
42+
protected $drop_target = false;
43+
44+
/** @var bool */
45+
protected $truncate = false;
46+
47+
/** @var string */
48+
protected $target_connection = 'target';
49+
50+
/** @var string */
51+
protected $source_connection = 'source';
52+
53+
/** @var string */
54+
protected $none = 'none';
55+
56+
/** @var array */
57+
protected $choices = ['target', 'source', 'none'];
58+
59+
/** @var array */
60+
protected $migrated = [];
61+
62+
/** @var array */
63+
protected $tables_not_exists = [];
64+
65+
/** @var array */
66+
protected $excluded = [];
67+
2868
public function handle()
2969
{
3070
$this->validateOptions();
3171
$this->resolveBuilders();
72+
$this->resolveOptions();
3273
$this->cleanTargetDatabase();
3374
$this->runMigrations();
3475

3576
$this->disableForeign();
3677
$this->runTransfer();
3778
$this->enableForeign();
79+
80+
$this->showStatus();
81+
}
82+
83+
protected function showStatus(): void
84+
{
85+
$this->displayMessage('Migrated Tables', $this->migrated);
86+
$this->displayMessage('Excluded Tables', $this->excluded);
87+
$this->displayMessage('Tables does not exist in source connection', $this->tables_not_exists);
88+
}
89+
90+
protected function displayMessage(string $message, array $context = []): void
91+
{
92+
$this->info($message);
93+
94+
if ($context) {
95+
$this->info(implode(',', $context));
96+
}
3897
}
3998

4099
protected function runTransfer(): void
41100
{
42-
$this->info('Transferring data...');
101+
$this->displayMessage('Transferring data...' . PHP_EOL);
43102

44103
$this->withProgressBar($this->tables(), function (string $table) {
104+
if (in_array($table, $this->excludes)) {
105+
$this->excluded[] = $table;
106+
107+
return;
108+
}
109+
110+
if ($this->doesntHasTable($this->source(), $table)) {
111+
$this->tables_not_exists[] = $table;
112+
113+
return;
114+
}
115+
116+
$this->truncateTable($table);
45117
$this->migrateTable($table, $this->source->getPrimaryKey($table));
46118
});
119+
120+
$this->displayMessage(PHP_EOL);
121+
}
122+
123+
protected function truncateTable(string $table): void
124+
{
125+
if ($this->truncate) {
126+
$this->builder($this->target(), $table)->truncate();
127+
}
47128
}
48129

49130
protected function migrateTable(string $table, string $column): void
50131
{
132+
Log::info('Transferring data from: ' . $table);
133+
51134
$this->builder($this->source(), $table)
135+
->when(
136+
$this->isSkippable($table, $column),
137+
function ($query) use ($table, $column) {
138+
$lastRecord = $this->builder($this->target(), $table)->max($column) ?: 0;
139+
140+
Log::info('last record: ' . $lastRecord);
141+
142+
return $query->where($column, '>', $lastRecord);
143+
}
144+
)
52145
->orderBy($column)
53146
->chunk(1000, function (Collection $items) use ($table) {
54147
$items = Arr::toArray($items);
55148

56149
$this->builder($this->target(), $table)->insert($items);
57150
});
151+
152+
$this->migrated[] = $table;
153+
}
154+
155+
protected function isSkippable(string $table, string $column): bool
156+
{
157+
return ! $this->truncate && $this->isNumericColumn($table, $column);
158+
}
159+
160+
protected function isNumericColumn(string $table, string $column): bool
161+
{
162+
return $this->getPrimaryKeyType($this->source(), $table, $column) !== 'string';
58163
}
59164

60165
protected function tables(): array
61166
{
62-
return $this->source->getAllTables();
167+
if ($this->tables) {
168+
return $this->tables;
169+
}
170+
171+
return $this->retrieve_tables_from_target
172+
? $this->target->getAllTables()
173+
: $this->source->getAllTables();
63174
}
64175

65176
protected function cleanTargetDatabase(): void
66177
{
67-
$this->info('Clearing the target database...');
178+
if (! $this->drop_target) {
179+
return;
180+
}
181+
182+
$this->displayMessage('Clearing the target database...');
68183

69184
$this->target->dropAllTables();
70185
}
71186

72187
protected function runMigrations(): void
73188
{
74-
$this->info('Run migrations on the databases...');
189+
$on = $this->getMigrationOption();
190+
191+
if ($this->isMigrationNotRequired($on)) {
192+
return;
193+
}
194+
195+
$this->displayMessage('Run migrations on the databases...');
75196

76-
$this->call('migrate', ['--database' => $this->source()]);
77-
$this->call('migrate', ['--database' => $this->target()]);
197+
if ($this->shouldRunOnSource($on)) {
198+
$this->migrate($this->source());
199+
}
200+
201+
if ($this->drop_target || $this->shouldRunOnSource($on) || $this->shouldRunOnTarget($on)) {
202+
$this->migrate($this->target());
203+
}
204+
}
205+
206+
protected function isMigrationNotRequired(string $on): bool
207+
{
208+
return $on === $this->none;
209+
}
210+
211+
protected function shouldRunOnTarget(string $on): bool
212+
{
213+
return $on === $this->target_connection;
214+
}
215+
216+
protected function shouldRunOnSource(string $on): bool
217+
{
218+
return $on === $this->source_connection;
219+
}
220+
221+
protected function migrate(string $connection): void
222+
{
223+
$this->call('migrate', ['--database' => $connection]);
78224
}
79225

80226
protected function disableForeign(): void
@@ -97,6 +243,36 @@ protected function target(): string
97243
return $this->validatedOption('schema-to');
98244
}
99245

246+
protected function getTablesOption(): array
247+
{
248+
return $this->option('tables');
249+
}
250+
251+
protected function getExcludeOption(): array
252+
{
253+
return $this->option('exclude');
254+
}
255+
256+
protected function getMigrationOption(): string
257+
{
258+
return $this->choice('Please choose option to run migration on which connection?', $this->choices, 0);
259+
}
260+
261+
protected function confirmTableListOption(): bool
262+
{
263+
return $this->confirm('Please confirm table list should be retrieved from target connection? (incase if source connection does not support it)', false);
264+
}
265+
266+
protected function confirmTruncateTableOption(): bool
267+
{
268+
return $this->confirm('Please confirm whether to truncate target table before transfer?', false);
269+
}
270+
271+
protected function confirmDropOption(): bool
272+
{
273+
return $this->confirm('Please choose whether to drop target tables before migration?', false);
274+
}
275+
100276
protected function validatedOption(string $key): string
101277
{
102278
if ($schema = $this->option($key)) {
@@ -123,8 +299,36 @@ protected function resolveBuilders(): void
123299
$this->target = $this->resolveBuilder($this->target());
124300
}
125301

302+
protected function resolveOptions(): void
303+
{
304+
$this->tables = $this->getTablesOption();
305+
$this->excludes = $this->getExcludeOption();
306+
307+
if (empty($this->tables) && $this->confirmTableListOption()) {
308+
$this->retrieve_tables_from_target = true;
309+
}
310+
311+
if ($this->confirmTruncateTableOption()) {
312+
$this->truncate = true;
313+
}
314+
315+
if (empty($this->tables) && empty($this->excludes) && $this->truncate && $this->confirmDropOption()) {
316+
$this->drop_target = true;
317+
}
318+
}
319+
126320
protected function builder(string $connection, string $table): QueryBuilder
127321
{
128322
return DB::connection($connection)->table($table);
129323
}
324+
325+
protected function doesntHasTable(string $connection, string $table): bool
326+
{
327+
return ! Schema::connection($connection)->hasTable($table);
328+
}
329+
330+
protected function getPrimaryKeyType(string $connection, string $table, string $column): string
331+
{
332+
return DB::connection($connection)->getDoctrineColumn($table, $column)->getType()->getName();
333+
}
130334
}

tests/Concerns/Database.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ trait Database
2727

2828
protected $table_baz = 'baz';
2929

30+
protected $choice_target = 'target';
31+
32+
protected $choice_source = 'source';
33+
34+
protected $choices = [
35+
'target',
36+
'source',
37+
'none',
38+
];
39+
3040
protected function setDatabases($app): void
3141
{
3242
$this->setDatabaseConnections($app);

0 commit comments

Comments
 (0)