feat: implement old data migration command and related migration classes
This commit is contained in:
parent
72848d51d8
commit
c063cde967
115
app/Console/Commands/MigrateOldDataCommand.php
Normal file
115
app/Console/Commands/MigrateOldDataCommand.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\OldDataMigrations\BaseOldDataMigration;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
|
||||
class MigrateOldDataCommand extends Command
|
||||
{
|
||||
protected $signature = 'app:migrate-old-data {--only=} {--skip=} {--force}';
|
||||
|
||||
protected $description = 'Migrate data from old database to new database';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$filesystem = new Filesystem;
|
||||
$migrationPath = app_path('OldDataMigrations');
|
||||
|
||||
require_once $migrationPath.'/BaseOldDataMigration.php';
|
||||
|
||||
$files = collect($filesystem->glob($migrationPath.'/*.php'))
|
||||
->map(fn ($file) => basename($file))
|
||||
->filter(fn ($file) => $file !== 'BaseOldDataMigration.php')
|
||||
->toArray();
|
||||
|
||||
$migrations = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
require_once $migrationPath.'/'.$file;
|
||||
|
||||
$className = 'App\\OldDataMigrations\\'.str_replace('.php', '', $file);
|
||||
|
||||
if (! class_exists($className)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$instance = new $className;
|
||||
|
||||
if ($instance instanceof BaseOldDataMigration) {
|
||||
$migrations[] = $instance;
|
||||
}
|
||||
}
|
||||
|
||||
usort($migrations, fn ($a, $b) => $a->priority() <=> $b->priority());
|
||||
|
||||
$only = $this->option('only') ? explode(',', $this->option('only')) : null;
|
||||
$skip = $this->option('skip') ? explode(',', $this->option('skip')) : [];
|
||||
BaseOldDataMigration::$truncateBeforeInsert = $this->option('force');
|
||||
|
||||
$this->newLine();
|
||||
$this->info('=== Migrate Old Data ===');
|
||||
$this->newLine();
|
||||
|
||||
DB::connection('mysql')->getPdo()->exec('SET FOREIGN_KEY_CHECKS=0');
|
||||
$this->warn('Foreign key checks disabled.');
|
||||
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
|
||||
$totalRows = 0;
|
||||
$tableSummary = [];
|
||||
|
||||
foreach ($migrations as $migration) {
|
||||
if ($only !== null) {
|
||||
$results = $migration->migrate();
|
||||
$groupTableNames = array_keys($results);
|
||||
$hasOverlap = false;
|
||||
foreach ($only as $t) {
|
||||
if (in_array($t, $groupTableNames)) {
|
||||
$hasOverlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! $hasOverlap) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$className = (new \ReflectionClass($migration))->getShortName();
|
||||
$this->info("Running: {$className}");
|
||||
|
||||
$results = $migration->migrate();
|
||||
|
||||
foreach ($results as $table => $count) {
|
||||
if (in_array($table, $skip)) {
|
||||
$this->line(" SKIP {$table}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$tableSummary[$table] = $count;
|
||||
$totalRows += $count;
|
||||
$this->line(" OK {$table}: {$count} rows");
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
DB::connection('mysql')->getPdo()->exec('SET FOREIGN_KEY_CHECKS=1');
|
||||
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
||||
$this->warn('Foreign key checks re-enabled.');
|
||||
|
||||
$this->newLine();
|
||||
$this->info('=== Summary ===');
|
||||
$this->info('Total tables: '.count($tableSummary));
|
||||
$this->info("Total rows: {$totalRows}");
|
||||
$this->newLine();
|
||||
|
||||
$this->table(['Table', 'Rows'], collect($tableSummary)->map(fn ($count, $table) => [$table, $count])->values()->toArray());
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
101
app/OldDataMigrations/BaseOldDataMigration.php
Normal file
101
app/OldDataMigrations/BaseOldDataMigration.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
abstract class BaseOldDataMigration
|
||||
{
|
||||
protected $old;
|
||||
|
||||
protected $new;
|
||||
|
||||
public static bool $truncateBeforeInsert = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->old = DB::connection('mysql_old');
|
||||
$this->new = DB::connection('mysql');
|
||||
}
|
||||
|
||||
abstract public function migrate(): array;
|
||||
|
||||
abstract public function priority(): int;
|
||||
|
||||
protected function oldQuery(string $table)
|
||||
{
|
||||
return $this->old->table($table);
|
||||
}
|
||||
|
||||
protected function truncateIfNeeded(string $table): void
|
||||
{
|
||||
if (static::$truncateBeforeInsert) {
|
||||
$this->new->table($table)->truncate();
|
||||
}
|
||||
}
|
||||
|
||||
protected function insertBatch(string $table, array $rows, int $chunkSize = 500): int
|
||||
{
|
||||
if (empty($rows)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
|
||||
foreach (array_chunk($rows, $chunkSize) as $chunk) {
|
||||
$this->new->table($table)->insert($chunk);
|
||||
$total += count($chunk);
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
|
||||
protected function migrateTable(string $table, ?callable $transform = null, ?array $columns = null): int
|
||||
{
|
||||
$this->truncateIfNeeded($table);
|
||||
|
||||
$rows = $this->oldQuery($table)->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$rows = $rows->map(function ($row) use ($columns) {
|
||||
$data = (array) $row;
|
||||
if ($columns !== null) {
|
||||
return array_intersect_key($data, array_flip($columns));
|
||||
}
|
||||
|
||||
return $data;
|
||||
})->toArray();
|
||||
|
||||
if ($transform !== null) {
|
||||
$rows = array_map($transform, $rows);
|
||||
}
|
||||
|
||||
return $this->insertBatch($table, $rows);
|
||||
}
|
||||
|
||||
protected function migrateTableWithoutColumns(string $table, array $skipColumns, ?callable $transform = null): int
|
||||
{
|
||||
$this->truncateIfNeeded($table);
|
||||
|
||||
$rows = $this->oldQuery($table)->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$rows = $rows->map(function ($row) use ($skipColumns) {
|
||||
$data = (array) $row;
|
||||
|
||||
return array_diff_key($data, array_flip($skipColumns));
|
||||
})->toArray();
|
||||
|
||||
if ($transform !== null) {
|
||||
$rows = array_map($transform, $rows);
|
||||
}
|
||||
|
||||
return $this->insertBatch($table, $rows);
|
||||
}
|
||||
}
|
||||
22
app/OldDataMigrations/CashFinanceMigration.php
Normal file
22
app/OldDataMigrations/CashFinanceMigration.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class CashFinanceMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['cash_accounts'] = $this->migrateTable('cash_accounts');
|
||||
$results['cash_transactions'] = $this->migrateTable('cash_transactions');
|
||||
$results['expenses'] = $this->migrateTable('expenses');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
27
app/OldDataMigrations/CoreAuthMigration.php
Normal file
27
app/OldDataMigrations/CoreAuthMigration.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class CoreAuthMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['users'] = $this->migrateTable('users');
|
||||
$results['user_profiles'] = $this->migrateTable('user_profiles');
|
||||
$results['employees'] = $this->migrateTable('employees');
|
||||
$results['permissions'] = $this->migrateTable('permissions');
|
||||
$results['roles'] = $this->migrateTable('roles');
|
||||
$results['model_has_permissions'] = $this->migrateTable('model_has_permissions');
|
||||
$results['model_has_roles'] = $this->migrateTable('model_has_roles');
|
||||
$results['role_has_permissions'] = $this->migrateTable('role_has_permissions');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
29
app/OldDataMigrations/CuttingMigration.php
Normal file
29
app/OldDataMigrations/CuttingMigration.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class CuttingMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['cuttings'] = $this->migrateTable('cuttings');
|
||||
$results['cutting_material_combinations'] = $this->migrateTable('cutting_material_combinations');
|
||||
$results['cutting_materials'] = $this->migrateTable('cutting_materials', function ($row) {
|
||||
if (isset($row['material_usage']) && is_string($row['material_usage'])) {
|
||||
$row['material_usage'] = (int) round($row['material_usage']);
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
$results['cutting_results'] = $this->migrateTable('cutting_results');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
26
app/OldDataMigrations/HrMigration.php
Normal file
26
app/OldDataMigrations/HrMigration.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class HrMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['employee_advances'] = $this->migrateTable('employee_advances');
|
||||
$results['employee_advance_payments'] = $this->migrateTable('employee_advance_payments');
|
||||
$results['attendances'] = $this->migrateTable('attendances');
|
||||
$results['leave_requests'] = $this->migrateTable('leave_requests');
|
||||
$results['payroll_periods'] = $this->migrateTable('payroll_periods');
|
||||
$results['payrolls'] = $this->migrateTable('payrolls');
|
||||
$results['payroll_adjustments'] = $this->migrateTable('payroll_adjustments');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
34
app/OldDataMigrations/InventoryMigration.php
Normal file
34
app/OldDataMigrations/InventoryMigration.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class InventoryMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['retail_stock_histories'] = $this->migrateTable('retail_stock_histories');
|
||||
$results['stok_opnames'] = $this->migrateTable('stok_opnames');
|
||||
$results['stok_opname_items'] = $this->migrateTable('stok_opname_items');
|
||||
$results['restocks'] = $this->migrateTable('restocks');
|
||||
$results['restock_items'] = $this->migrateTable('restock_items');
|
||||
$results['stock_mutations'] = $this->migrateTable('stock_mutations', function ($row) {
|
||||
$fields = ['quantity', 'stock_before', 'stock_after'];
|
||||
foreach ($fields as $field) {
|
||||
if (isset($row[$field]) && is_string($row[$field])) {
|
||||
$row[$field] = (int) round($row[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
22
app/OldDataMigrations/MasterDataMigration.php
Normal file
22
app/OldDataMigrations/MasterDataMigration.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class MasterDataMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['categories'] = $this->migrateTable('categories');
|
||||
$results['suppliers'] = $this->migrateTable('suppliers');
|
||||
$results['customers'] = $this->migrateTable('customers');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
25
app/OldDataMigrations/MediaMigration.php
Normal file
25
app/OldDataMigrations/MediaMigration.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class MediaMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['media'] = $this->migrateTable('media', function ($row) {
|
||||
$row['disk'] = 's3';
|
||||
$row['conversions_disk'] = 's3';
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
40
app/OldDataMigrations/OrdersMigration.php
Normal file
40
app/OldDataMigrations/OrdersMigration.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class OrdersMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['orders'] = $this->migrateTable('orders', function ($row) {
|
||||
$row['cogs'] = $row['cogs'] ?? 0;
|
||||
|
||||
$row['price_type'] = match ($row['price_type']) {
|
||||
'grosir' => 'wholesale',
|
||||
'harga_modal' => 'capital',
|
||||
default => $row['price_type'],
|
||||
};
|
||||
|
||||
return $row;
|
||||
});
|
||||
|
||||
$results['order_items'] = $this->migrateTable('order_items', function ($row) {
|
||||
$row['stock_quality'] = match ($row['stock_quality']) {
|
||||
'retail' => 'good',
|
||||
default => $row['stock_quality'],
|
||||
};
|
||||
|
||||
return $row;
|
||||
});
|
||||
$results['activity_log'] = $this->migrateTable('activity_log');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
36
app/OldDataMigrations/ProductsMigration.php
Normal file
36
app/OldDataMigrations/ProductsMigration.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class ProductsMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['products'] = $this->migrateTable('products');
|
||||
$results['product_categories'] = $this->migrateTable('product_categories');
|
||||
$results['product_variants'] = $this->migrateTable('product_variants');
|
||||
|
||||
$results['product_prices'] = $this->migrateTableWithoutColumns(
|
||||
'product_prices',
|
||||
['deleted_at'],
|
||||
function ($row) {
|
||||
$row['type'] = match ($row['type']) {
|
||||
'grosir' => 'wholesale',
|
||||
'harga_modal' => 'capital',
|
||||
default => $row['type'],
|
||||
};
|
||||
|
||||
return $row;
|
||||
},
|
||||
);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
21
app/OldDataMigrations/PurchasesMigration.php
Normal file
21
app/OldDataMigrations/PurchasesMigration.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class PurchasesMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['purchases'] = $this->migrateTable('purchases');
|
||||
$results['purchase_items'] = $this->migrateTable('purchase_items');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
28
app/OldDataMigrations/RawMaterialsMigration.php
Normal file
28
app/OldDataMigrations/RawMaterialsMigration.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class RawMaterialsMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['raw_materials'] = $this->migrateTable('raw_materials', function ($row) {
|
||||
$row['unit'] = match ($row['unit']) {
|
||||
'kilogram' => 'kg',
|
||||
default => $row['unit'],
|
||||
};
|
||||
|
||||
return $row;
|
||||
});
|
||||
$results['raw_material_prices'] = $this->migrateTable('raw_material_prices');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
32
app/OldDataMigrations/SystemConfigMigration.php
Normal file
32
app/OldDataMigrations/SystemConfigMigration.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\OldDataMigrations;
|
||||
|
||||
class SystemConfigMigration extends BaseOldDataMigration
|
||||
{
|
||||
public function priority(): int
|
||||
{
|
||||
return 11;
|
||||
}
|
||||
|
||||
public function migrate(): array
|
||||
{
|
||||
$results = [];
|
||||
|
||||
$results['system_configurations'] = $this->migrateTable('system_configurations');
|
||||
$results['push_subscriptions'] = $this->migrateTable('push_subscriptions', function ($row) {
|
||||
if (isset($row['user_id']) && $row['user_id'] !== null) {
|
||||
$row['subscribable_type'] = 'App\\Models\\User';
|
||||
$row['subscribable_id'] = $row['user_id'];
|
||||
}
|
||||
unset($row['user_id']);
|
||||
|
||||
return $row;
|
||||
});
|
||||
$results['homepage_configurations'] = $this->migrateTable('homepage_configurations');
|
||||
$results['owner_verification_requests'] = $this->migrateTable('owner_verification_requests');
|
||||
$results['notifications'] = $this->migrateTable('notifications');
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user