refactor: optimize legacy data migration commands with chunking, progress tracking, and S3 media handling
This commit is contained in:
parent
28e01c6148
commit
83caa33768
@ -5,6 +5,7 @@
|
||||
use App\Enums\AnnouncementType;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateAnnouncementCommand extends Command
|
||||
{
|
||||
@ -20,29 +21,48 @@ class MigrateAnnouncementCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate announcement data from legacy news table where category = 1';
|
||||
protected $description = 'Migrate announcement data from legacy news table where category = 1 with optimized processing';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting announcement migration...');
|
||||
$this->info('Starting optimized announcement migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('news')->where('category', '1');
|
||||
|
||||
// Get legacy news with category 1 (Announcements)
|
||||
$legacyAnnouncements = $legacyConn->table('news')
|
||||
->where('category', '1')
|
||||
->get();
|
||||
|
||||
if ($legacyAnnouncements->isEmpty()) {
|
||||
$this->warn('No announcements found with category 1.');
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No announcements found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyAnnouncements, function ($legacy) {
|
||||
$this->info("Found {$totalCount} legacy announcement records.");
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processAnnouncement($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Announcement migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy announcement record.
|
||||
*/
|
||||
private function processAnnouncement($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->title, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('announcements')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -54,9 +74,10 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Announcement migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateClassificationCommand extends Command
|
||||
{
|
||||
@ -20,55 +21,113 @@ class MigrateClassificationCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate classification data from legacy database';
|
||||
protected $description = 'Migrate classification and sub-classification data from legacy database with optimized processing';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting classification migration...');
|
||||
$this->info('Starting optimized classification migration...');
|
||||
|
||||
$this->migrateClassifications();
|
||||
$this->newLine();
|
||||
$this->migrateSubClassifications();
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Classification migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate main classifications.
|
||||
*/
|
||||
private function migrateClassifications(): void
|
||||
{
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$newConn = DB::connection('mysql');
|
||||
$query = $legacyConn->table('classifications');
|
||||
|
||||
// Migrate Classifications
|
||||
$this->info('Migrating classifications...');
|
||||
$legacyClassifications = $legacyConn->table('classifications')->get();
|
||||
$totalCount = $query->count();
|
||||
$this->info("Found {$totalCount} legacy classification records.");
|
||||
|
||||
$this->withProgressBar($legacyClassifications, function ($legacy) {
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processClassification($legacy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy classification record.
|
||||
*/
|
||||
private function processClassification($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} (CLA) Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('classifications')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'name' => $legacy->name,
|
||||
'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value,
|
||||
'created_at' => $legacy->created_at,
|
||||
'updated_at' => $legacy->updated_at,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate sub-classifications.
|
||||
*/
|
||||
private function migrateSubClassifications(): void
|
||||
{
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('sub_classifications');
|
||||
|
||||
$totalCount = $query->count();
|
||||
$this->info("Found {$totalCount} legacy sub-classification records.");
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processSubClassification($legacy);
|
||||
}
|
||||
});
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
// Migrate Sub Classifications
|
||||
$this->info('Migrating sub classifications...');
|
||||
$legacySubClassifications = $legacyConn->table('sub_classifications')->get();
|
||||
/**
|
||||
* Process a single legacy sub-classification record.
|
||||
*/
|
||||
private function processSubClassification($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->withProgressBar($legacySubClassifications, function ($legacy) {
|
||||
$this->output->write("{$idOutput} (SUB) Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('sub_classifications')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'classification_id' => $legacy->classification,
|
||||
'classification_id' => $legacy->classification, // Foreign key to classifications
|
||||
'name' => $legacy->name,
|
||||
'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value,
|
||||
'created_at' => $legacy->created_at,
|
||||
'updated_at' => $legacy->updated_at,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,12 @@
|
||||
use App\Enums\MediaType;
|
||||
use App\Enums\RoleEnum;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateCompanyCommand extends Command
|
||||
@ -27,39 +29,78 @@ class MigrateCompanyCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate companies, partner media, and verification requests from legacy database';
|
||||
protected $description = 'Migrate companies, partner media, and all verification documents from legacy database';
|
||||
|
||||
/**
|
||||
* Cache for migrated IDs with media to avoid redundant processing.
|
||||
*/
|
||||
private array $migratedIds = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting company migration...');
|
||||
$this->info('Starting optimized company migration with document support...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$legacyCompanies = $legacyConn->table('companies')
|
||||
->whereNull('deleted_at')
|
||||
->get();
|
||||
$query = $legacyConn->table('companies');
|
||||
|
||||
if ($legacyCompanies->isEmpty()) {
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No companies found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyCompanies, function ($legacy) use ($legacyConn) {
|
||||
// Find user in NEW database. Legcy enhancer is the user_id.
|
||||
$newUser = DB::table('users')->where('id', $legacy->enhancer)->first();
|
||||
$legacyUser = $legacyConn->table('users')->where('id', $legacy->enhancer)->first();
|
||||
$this->info("Found {$totalCount} legacy company records.");
|
||||
|
||||
// If not found by ID, try by email (sometimes IDs change during user migration if not careful)
|
||||
// Pre-cache migrated IDs using their associated media records
|
||||
$this->migratedIds = DB::table('media')
|
||||
->where('model_type', Company::class)
|
||||
->where('collection_name', 'companies')
|
||||
->pluck('model_id')
|
||||
->flip()
|
||||
->all();
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processCompany($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Company migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy company record.
|
||||
*/
|
||||
private function processCompany($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
|
||||
// Find user
|
||||
$newUser = DB::table('users')->where('id', $legacy->enhancer)->first();
|
||||
if (! $newUser) {
|
||||
$newUser = DB::table('users')->where('email', $legacy->email)->first();
|
||||
}
|
||||
|
||||
if (! $newUser) {
|
||||
$this->output->writeln('<error>USER NOT FOUND</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Migrate Company
|
||||
$legacyUser = $legacyConn->table('users')->where('id', $legacy->enhancer)->first();
|
||||
|
||||
// 1. Update/Insert Company
|
||||
DB::table('companies')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -70,73 +111,176 @@ public function handle()
|
||||
'address' => $legacy->address,
|
||||
'director_name' => $legacy->director,
|
||||
'director_nik' => $legacy->nick_director,
|
||||
'deed_incorporation' => str()->limit($legacy->deed_of_establishment, 150, ''),
|
||||
'trade_license' => str()->limit($legacy->trade_license, 150, ''),
|
||||
'tax_id_number' => str()->limit($legacy->tax_id_number, 150, ''),
|
||||
'taxable_enterprise' => str()->limit($legacy->taxable_enterprise, 150, ''),
|
||||
'annual_tax_return' => str()->limit($legacy->annual_tax_statement, 150, ''),
|
||||
'domicile_certificate' => str()->limit($legacy->domicile, 150, ''),
|
||||
'profile' => str()->limit($legacy->profile ?? '-', 150, ''),
|
||||
'deed_incorporation' => Str::limit($legacy->deed_of_establishment, 150, ''),
|
||||
'trade_license' => Str::limit($legacy->trade_license, 150, ''),
|
||||
'tax_id_number' => Str::limit($legacy->tax_id_number, 150, ''),
|
||||
'taxable_enterprise' => Str::limit($legacy->taxable_enterprise, 150, ''),
|
||||
'annual_tax_return' => Str::limit($legacy->annual_tax_statement, 150, ''),
|
||||
'domicile_certificate' => Str::limit($legacy->domicile, 150, ''),
|
||||
'profile' => Str::limit($legacy->profile ?? '-', 150, ''),
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
|
||||
// 2. Migrate Verification Request
|
||||
$status = $this->mapVerificationStatus($legacyUser->status);
|
||||
DB::table('verification_requests')->updateOrInsert(
|
||||
['company_id' => $legacy->id],
|
||||
[
|
||||
'submitted_by' => $newUser->id,
|
||||
'status' => $status,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
]
|
||||
);
|
||||
// 2. Verification Request & Review
|
||||
$this->handleVerification($legacy, $legacyUser, $newUser);
|
||||
|
||||
// 3. Migrate Verification Review
|
||||
$decision = $this->mapDecision($legacyUser->status);
|
||||
// 3. Partner Media
|
||||
$this->handlePartnerMedia($legacy);
|
||||
|
||||
// 4. Media Documents Migration (Skip if already has media in 'companies' collection)
|
||||
if (isset($this->migratedIds[$legacy->id])) {
|
||||
$this->output->writeln('<info>DONE (SKIP DOCS)</info>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$company = Company::withTrashed()->find($legacy->id);
|
||||
if ($company) {
|
||||
$this->migrateAllDocuments($company, $legacy);
|
||||
}
|
||||
|
||||
$this->output->writeln('<info>DONE + DOCS</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle verification logic.
|
||||
*/
|
||||
private function handleVerification($legacy, $legacyUser, $newUser): void
|
||||
{
|
||||
$status = $this->mapVerificationStatus($legacyUser->status);
|
||||
|
||||
DB::table('verification_requests')->updateOrInsert(
|
||||
['company_id' => $legacy->id],
|
||||
[
|
||||
'submitted_by' => $newUser->id,
|
||||
'status' => $status,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
]
|
||||
);
|
||||
|
||||
$decision = $this->mapDecision($legacyUser->status);
|
||||
if ($decision !== DecisionAdmin::NEED_REVISION->value) {
|
||||
$verificationRequest = DB::table('verification_requests')->where('company_id', $legacy->id)->first();
|
||||
if ($decision !== DecisionAdmin::NEED_REVISION->value) {
|
||||
$adminUser = User::role(RoleEnum::ADMINISTRATOR)->first();
|
||||
|
||||
if ($verificationRequest && $adminUser) {
|
||||
DB::table('verification_reviews')->updateOrInsert(
|
||||
['verification_request_id' => $verificationRequest->id],
|
||||
[
|
||||
'reviewer_id' => User::role(RoleEnum::ADMINISTRATOR)->first()->id,
|
||||
'reviewer_id' => $adminUser->id,
|
||||
'decision' => $decision,
|
||||
'note' => $legacy->reject_message,
|
||||
'created_at' => Carbon::parse($legacy->validation_date)->setTime(23, 0, 0),
|
||||
'created_at' => $legacy->validation_date ? Carbon::parse($legacy->validation_date)->setTime(23, 0, 0) : now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Migrate Partner Media (from legacy 'media' table matching company ID)
|
||||
$legacyMediaList = $legacyConn->table('media')->where('company', $legacy->id)->get();
|
||||
foreach ($legacyMediaList as $legacyMedia) {
|
||||
DB::table('partner_media')->updateOrInsert(
|
||||
['id' => $legacyMedia->id],
|
||||
[
|
||||
'company_id' => $legacy->id,
|
||||
'name' => $legacyMedia->name,
|
||||
'address' => $legacyMedia->address,
|
||||
'link' => $this->extractMediaLink($legacyMedia->link),
|
||||
'type' => $this->mapMediaType($legacyMedia->type),
|
||||
'classification' => $this->mapMediaClassification($legacyMedia->classification),
|
||||
'journalism_organization' => $legacyMedia->organization,
|
||||
'press_council_certificate' => $legacyMedia->certificate,
|
||||
'created_at' => $legacyMedia->created_at ?? now(),
|
||||
'updated_at' => $legacyMedia->updated_at ?? now(),
|
||||
'deleted_at' => $legacyMedia->deleted_at,
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Company migration completed successfully!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle partner media.
|
||||
*/
|
||||
private function handlePartnerMedia($legacy): void
|
||||
{
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$legacyMediaList = $legacyConn->table('media')->where('company', $legacy->id)->get();
|
||||
|
||||
foreach ($legacyMediaList as $legacyMedia) {
|
||||
DB::table('partner_media')->updateOrInsert(
|
||||
['id' => $legacyMedia->id],
|
||||
[
|
||||
'company_id' => $legacy->id,
|
||||
'name' => $legacyMedia->name,
|
||||
'address' => $legacyMedia->address,
|
||||
'link' => $this->extractMediaLink($legacyMedia->link),
|
||||
'type' => $this->mapMediaType($legacyMedia->type),
|
||||
'classification' => $this->mapMediaClassification($legacyMedia->classification),
|
||||
'journalism_organization' => $legacyMedia->organization,
|
||||
'press_council_certificate' => $legacyMedia->certificate,
|
||||
'created_at' => $legacyMedia->created_at ?? now(),
|
||||
'updated_at' => $legacyMedia->updated_at ?? now(),
|
||||
'deleted_at' => $legacyMedia->deleted_at,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate all company documents from S3.
|
||||
*/
|
||||
private function migrateAllDocuments(Company $company, $legacy): void
|
||||
{
|
||||
$docMapping = [
|
||||
'doc_of_nick_director' => 'director_nik',
|
||||
'doc_of_deed_of_establishment' => 'deed_incorporation',
|
||||
'doc_of_trade_license' => 'trade_license',
|
||||
'doc_of_tax_id_number' => 'tax_id_number',
|
||||
'doc_of_taxable_enterprise' => 'taxable_enterprise',
|
||||
'doc_of_annual_tax_statement' => 'annual_tax_return',
|
||||
'doc_of_domicile' => 'domicile_certificate',
|
||||
'doc_of_profile' => 'profile',
|
||||
];
|
||||
|
||||
foreach ($docMapping as $legacyField => $docTypeSuffix) {
|
||||
$legacyPath = trim($legacy->{$legacyField} ?? '');
|
||||
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Path mapping: old data/companies/ + path from db
|
||||
$sourcePath = 'old data/companies/'.$legacyPath;
|
||||
|
||||
$this->importOneDocument($company, $sourcePath, $docTypeSuffix, $legacy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single document to Media Library.
|
||||
*/
|
||||
private function importOneDocument(Company $company, string $sourcePath, string $docType, $legacy): void
|
||||
{
|
||||
$attempts = 0;
|
||||
$maxAttempts = 3;
|
||||
$exists = false;
|
||||
|
||||
while ($attempts < $maxAttempts) {
|
||||
try {
|
||||
$exists = Storage::disk('s3')->exists($sourcePath);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
$attempts++;
|
||||
usleep(200000);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$company->addMediaFromDisk($sourcePath, 's3')
|
||||
->withCustomProperties([
|
||||
'feature' => 'companies',
|
||||
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||
'doc_type' => $docType, // Match CompanySaveService slug pattern
|
||||
])
|
||||
->preservingOriginal()
|
||||
->toMediaCollection('companies', 's3');
|
||||
} catch (\Exception $e) {
|
||||
// Log quietly or handle error
|
||||
}
|
||||
}
|
||||
|
||||
// ... mapping methods (mapVerificationStatus, mapDecision, mapMediaType, mapMediaClassification, extractMediaLink) remain as previously defined
|
||||
|
||||
private function mapVerificationStatus($legacyStatus): int
|
||||
{
|
||||
return match ((string) $legacyStatus) {
|
||||
@ -182,33 +326,19 @@ private function extractMediaLink(?string $link): ?string
|
||||
if (empty($link)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find all URLs (starts with http or https)
|
||||
preg_match_all('/(https?:\/\/[^\s]+)/i', $link, $matches);
|
||||
|
||||
// If no URL found, return the original string truncated
|
||||
if (empty($matches[0])) {
|
||||
return Str::limit($link, 50, '');
|
||||
return Str::limit($link, 255, '');
|
||||
}
|
||||
|
||||
// Try to find the main media website URL
|
||||
foreach ($matches[0] as $url) {
|
||||
$url = rtrim($url, ','); // clean trailing comma if any
|
||||
|
||||
// Skip common non-media-website URLs if there's an alternative
|
||||
if (count($matches[0]) > 1 && (
|
||||
str_contains($url, 'google.') ||
|
||||
str_contains($url, 'drive.') ||
|
||||
str_contains($url, 'share.') ||
|
||||
str_contains($url, 'bit.ly')
|
||||
)) {
|
||||
$url = rtrim($url, ',');
|
||||
if (count($matches[0]) > 1 && (str_contains($url, 'google.') || str_contains($url, 'drive.') || str_contains($url, 'share.') || str_contains($url, 'bit.ly'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Str::limit($url, 50, '');
|
||||
return Str::limit($url, 255, '');
|
||||
}
|
||||
|
||||
// Fallback to the first URL found
|
||||
return Str::limit($matches[0][0], 50, '');
|
||||
return Str::limit($matches[0][0], 255, '');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,8 +6,11 @@
|
||||
use App\Enums\ContentType;
|
||||
use App\Enums\IsActive;
|
||||
use App\Enums\SocialMedia;
|
||||
use App\Models\ContentRecap;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateContentRecapCommand extends Command
|
||||
@ -24,72 +27,107 @@ class MigrateContentRecapCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate content recap data from legacy database';
|
||||
protected $description = 'Migrate content recap data and classifications with S3 images and optimized processing';
|
||||
|
||||
/**
|
||||
* Cache for migrated IDs with media to avoid redundant processing.
|
||||
*/
|
||||
private array $migratedIds = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting content recap migration...');
|
||||
$this->info('Starting optimized content recap migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
|
||||
// 1. Migrate Classifications first
|
||||
$this->info('Migrating classifications for content recap...');
|
||||
$legacyRecapClasses = $legacyConn->table('content_recap_classifications')->get();
|
||||
$classMapping = [];
|
||||
// 1. Migrate Classifications first (Small table, can handle simply)
|
||||
$this->migrateClassifications();
|
||||
|
||||
foreach ($legacyRecapClasses as $legacyClass) {
|
||||
// Check by name to avoid duplicates in classifications table
|
||||
$existing = DB::table('classifications')->where('name', $legacyClass->name)->first();
|
||||
// 2. Pre-cache migrated IDs using their associated media records
|
||||
// Using 'content-recaps' collection to match the (typo) name in ContentRecapForm
|
||||
$this->migratedIds = DB::table('media')
|
||||
->where('model_type', ContentRecap::class)
|
||||
->where('collection_name', 'content-recaps')
|
||||
->pluck('model_id')
|
||||
->flip()
|
||||
->all();
|
||||
|
||||
if ($existing) {
|
||||
$classMapping[$legacyClass->id] = $existing->id;
|
||||
} else {
|
||||
$newId = DB::table('classifications')->insertGetId([
|
||||
'name' => $legacyClass->name,
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
'created_at' => $legacyClass->created_at ?? now(),
|
||||
'updated_at' => $legacyClass->updated_at ?? now(),
|
||||
'deleted_at' => $legacyClass->deleted_at,
|
||||
]);
|
||||
$classMapping[$legacyClass->id] = $newId;
|
||||
}
|
||||
}
|
||||
// 3. Migrate Content Recaps
|
||||
$query = $legacyConn->table('content_recap');
|
||||
$totalCount = $query->count();
|
||||
|
||||
// 2. Migrate Content Recaps
|
||||
$this->info('Migrating content recaps...');
|
||||
$legacyRecaps = $legacyConn->table('content_recap')->get();
|
||||
|
||||
if ($legacyRecaps->isEmpty()) {
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No content recaps found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyRecaps, function ($legacy) use ($classMapping) {
|
||||
$classificationId = $classMapping[$legacy->classification_id] ?? null;
|
||||
$this->info("Found {$totalCount} legacy content recap records.");
|
||||
|
||||
// Fallback for classification if not found in master table mapping
|
||||
if (! $classificationId && $legacy->classification) {
|
||||
$classificationId = DB::table('classifications')
|
||||
->where('name', $legacy->classification)
|
||||
->value('id');
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processRecap($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Content recap migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate classifications related to content recaps.
|
||||
*/
|
||||
private function migrateClassifications(): void
|
||||
{
|
||||
$this->info('Migrating classifications...');
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$legacyRecapClasses = $legacyConn->table('content_recap_classifications')->get();
|
||||
|
||||
foreach ($legacyRecapClasses as $legacyClass) {
|
||||
DB::table('classifications')->updateOrInsert(
|
||||
['name' => $legacyClass->name],
|
||||
[
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
'created_at' => $legacyClass->created_at ?? now(),
|
||||
'updated_at' => $legacyClass->updated_at ?? now(),
|
||||
'deleted_at' => $legacyClass->deleted_at,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy content recap record.
|
||||
*/
|
||||
private function processRecap($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->title, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
// Find classification in master table
|
||||
$classificationId = DB::table('classifications')
|
||||
->where('name', $legacy->classification)
|
||||
->value('id');
|
||||
|
||||
// If still no classification, skip or use a default one?
|
||||
// The table requires classification_id so we must have one.
|
||||
if (! $classificationId) {
|
||||
return; // Skip if no classification
|
||||
$this->output->writeln('<error>CLASS NOT FOUND</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Update/Insert record
|
||||
DB::table('content_recaps')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'classification_id' => $classificationId,
|
||||
'title' => $legacy->title,
|
||||
'link' => Str::limit($legacy->link, 50, ''),
|
||||
'link' => Str::limit($legacy->link, 255, ''),
|
||||
'posting_date' => $legacy->posting_date === '0000-00-00' ? now()->toDateString() : $legacy->posting_date,
|
||||
'type' => $this->mapContentType($legacy->content_type),
|
||||
'channel' => $this->mapChannel($legacy->channel),
|
||||
@ -99,10 +137,68 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Content recap migration completed successfully!');
|
||||
// Step 2: Media Migration
|
||||
if (isset($this->migratedIds[$legacy->id])) {
|
||||
$this->output->writeln('<info>SKIP (Media Exists)</info>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! empty($legacy->image) && $legacy->image !== 'no-image.png') {
|
||||
$recap = ContentRecap::withTrashed()->find($legacy->id);
|
||||
if ($recap) {
|
||||
$this->migrateMedia($recap, $legacy);
|
||||
}
|
||||
} else {
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle S3 media migration.
|
||||
*/
|
||||
private function migrateMedia(ContentRecap $recap, $legacy): void
|
||||
{
|
||||
$filename = trim(basename($legacy->image));
|
||||
$sourcePath = 'old data/content-recaps/'.$filename;
|
||||
|
||||
$attempts = 0;
|
||||
$maxAttempts = 3;
|
||||
$exists = false;
|
||||
|
||||
while ($attempts < $maxAttempts) {
|
||||
try {
|
||||
$exists = Storage::disk('s3')->exists($sourcePath);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
$attempts++;
|
||||
usleep(200000);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $exists) {
|
||||
$this->output->writeln('<error>IMAGE MISSING</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$recap->addMediaFromDisk($sourcePath, 's3')
|
||||
->withCustomProperties([
|
||||
'feature' => 'content-recaps',
|
||||
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||
])
|
||||
->preservingOriginal()
|
||||
->toMediaCollection('content-recaps', 's3'); // Match typo in ContentRecapForm
|
||||
|
||||
$this->output->writeln('<info>DONE + IMG</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>S3 ERROR: '.Str::limit($e->getMessage(), 50).'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
private function mapContentType(?string $legacyType): ?int
|
||||
|
||||
@ -21,22 +21,56 @@ class MigrateDepartmentCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate department data from legacy agencies table';
|
||||
protected $description = 'Migrate department data from legacy agencies table with optimized processing';
|
||||
|
||||
/**
|
||||
* Store migrated record IDs to optimize processing.
|
||||
*/
|
||||
private array $migratedIds = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting department migration...');
|
||||
$this->info('Starting optimized department migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('agencies');
|
||||
|
||||
// Migrate Departments (Agencies)
|
||||
$this->info('Migrating departments...');
|
||||
$legacyAgencies = $legacyConn->table('agencies')->get();
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No departments found in legacy database.');
|
||||
|
||||
$this->withProgressBar($legacyAgencies, function ($legacy) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info("Found {$totalCount} legacy department records.");
|
||||
|
||||
// Cache existing IDs for faster processing
|
||||
$this->migratedIds = DB::table('departments')->pluck('id')->flip()->all();
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processDepartment($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Department migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy department record.
|
||||
*/
|
||||
private function processDepartment($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('departments')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -48,9 +82,10 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,27 +23,49 @@ class MigrateIssueManagementCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate issue management data from legacy database';
|
||||
protected $description = 'Migrate issue management data from legacy database with optimized processing';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting issue management migration...');
|
||||
$this->info('Starting optimized issue management migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('issue_management');
|
||||
|
||||
// Get legacy issue management
|
||||
$legacyIssues = $legacyConn->table('issue_management')->get();
|
||||
|
||||
if ($legacyIssues->isEmpty()) {
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No issue management records found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyIssues, function ($legacy) {
|
||||
$this->info("Found {$totalCount} legacy issue management records.");
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processIssue($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Issue management migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy issue management record.
|
||||
*/
|
||||
private function processIssue($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->description, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
// Step 1: Insert/Update main record
|
||||
DB::table('issue_management')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -61,46 +83,57 @@ public function handle()
|
||||
]
|
||||
);
|
||||
|
||||
// Handle Departments (Legacy 'agency' field is string, can be multiple separated by comma)
|
||||
$agencyString = trim($legacy->agency);
|
||||
if (! empty($agencyString)) {
|
||||
$agencyNames = explode(',', $agencyString);
|
||||
// Step 2: Handle Departments pivot mapping
|
||||
$this->handleDepartments($legacy);
|
||||
|
||||
foreach ($agencyNames as $agencyName) {
|
||||
$agencyName = trim($agencyName);
|
||||
if (empty($agencyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$department = Department::firstOrCreate(
|
||||
['name' => $agencyName],
|
||||
[
|
||||
'alias' => Str::limit($agencyName, 20, ''),
|
||||
'is_active' => IsActive::ACTIVE,
|
||||
]
|
||||
);
|
||||
|
||||
DB::table('department_issue_management')->updateOrInsert(
|
||||
[
|
||||
'department_id' => $department->id,
|
||||
'issue_management_id' => $legacy->id,
|
||||
],
|
||||
[
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Issue management migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
private function mapSentiment(string $legacyValue): int
|
||||
/**
|
||||
* Handle department pivot table mapping.
|
||||
*/
|
||||
private function handleDepartments($legacy): void
|
||||
{
|
||||
return match (strtolower($legacyValue)) {
|
||||
$agencyString = trim($legacy->agency);
|
||||
if (empty($agencyString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$agencyNames = array_filter(array_map('trim', explode(',', $agencyString)));
|
||||
|
||||
foreach ($agencyNames as $agencyName) {
|
||||
// Ensure department exists in master table
|
||||
$department = Department::firstOrCreate(
|
||||
['name' => $agencyName],
|
||||
[
|
||||
'alias' => Str::limit($agencyName, 20, ''),
|
||||
'is_active' => IsActive::ACTIVE,
|
||||
]
|
||||
);
|
||||
|
||||
// Link to pivot table
|
||||
DB::table('department_issue_management')->updateOrInsert(
|
||||
[
|
||||
'department_id' => $department->id,
|
||||
'issue_management_id' => $legacy->id,
|
||||
],
|
||||
[
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map sentiment strings to IssueSentiment Enum values.
|
||||
*/
|
||||
private function mapSentiment(?string $legacyValue): int
|
||||
{
|
||||
return match (strtolower($legacyValue ?? '')) {
|
||||
'positif' => IssueSentiment::POSITIVE->value,
|
||||
'negatif' => IssueSentiment::NEGATIVE->value,
|
||||
'netral' => IssueSentiment::NEUTRAL->value,
|
||||
|
||||
@ -2,8 +2,11 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Journalist;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateJournalistCommand extends Command
|
||||
@ -20,34 +23,70 @@ class MigrateJournalistCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate journalists from legacy database';
|
||||
protected $description = 'Migrate journalists and their documents from legacy database';
|
||||
|
||||
/**
|
||||
* Cache for migrated IDs with media to avoid redundant processing.
|
||||
*/
|
||||
private array $migratedIds = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting journalist migration...');
|
||||
$this->info('Starting optimized journalist migration with document support...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$legacyJournalists = $legacyConn->table('journalists')->get();
|
||||
$query = $legacyConn->table('journalists');
|
||||
|
||||
if ($legacyJournalists->isEmpty()) {
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No journalists found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyJournalists, function ($legacy) {
|
||||
// Find corresponding partner media (outlet) in the NEW database
|
||||
// legacy 'media' column is the partner_media_id
|
||||
$partnerMedia = DB::table('partner_media')->where('id', $legacy->media)->first();
|
||||
$this->info("Found {$totalCount} legacy journalist records.");
|
||||
|
||||
// Pre-cache migrated IDs
|
||||
$this->migratedIds = DB::table('media')
|
||||
->where('model_type', Journalist::class)
|
||||
->where('collection_name', 'journalists')
|
||||
->pluck('model_id')
|
||||
->flip()
|
||||
->all();
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processJournalist($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Journalist migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy journalist record.
|
||||
*/
|
||||
private function processJournalist($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
// Find corresponding partner media
|
||||
$partnerMedia = DB::table('partner_media')->where('id', $legacy->media)->first();
|
||||
if (! $partnerMedia) {
|
||||
// If partner media doesn't exist, we skip as it's required
|
||||
$this->output->writeln('<error>OUTLET NOT FOUND</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Update/Insert Journalist
|
||||
DB::table('journalists')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -62,9 +101,82 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Journalist migration completed successfully!');
|
||||
// 2. Media Documents Migration (Skip if already has media)
|
||||
if (isset($this->migratedIds[$legacy->id])) {
|
||||
$this->output->writeln('<info>DONE (SKIP DOCS)</info>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$journalist = Journalist::withTrashed()->find($legacy->id);
|
||||
if ($journalist) {
|
||||
$this->migrateJournalistDocuments($journalist, $legacy);
|
||||
}
|
||||
|
||||
$this->output->writeln('<info>DONE + DOCS</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate journalist documents from S3.
|
||||
*/
|
||||
private function migrateJournalistDocuments(Journalist $journalist, $legacy): void
|
||||
{
|
||||
$docMapping = [
|
||||
'doc_of_press_card' => 'press-card',
|
||||
'doc_of_certificate' => 'ukw-certificate',
|
||||
];
|
||||
|
||||
foreach ($docMapping as $legacyField => $docType) {
|
||||
$legacyPath = trim($legacy->{$legacyField} ?? '');
|
||||
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Path mapping: old data/companies/ + path from db (based on legacy structure)
|
||||
$sourcePath = 'old data/companies/'.$legacyPath;
|
||||
|
||||
$this->importOneDocument($journalist, $sourcePath, $docType, $legacy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a single document to Media Library.
|
||||
*/
|
||||
private function importOneDocument(Journalist $journalist, string $sourcePath, string $docType, $legacy): void
|
||||
{
|
||||
$attempts = 0;
|
||||
$maxAttempts = 3;
|
||||
$exists = false;
|
||||
|
||||
while ($attempts < $maxAttempts) {
|
||||
try {
|
||||
$exists = Storage::disk('s3')->exists($sourcePath);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
$attempts++;
|
||||
usleep(200000);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$journalist->addMediaFromDisk($sourcePath, 's3')
|
||||
->withCustomProperties([
|
||||
'feature' => 'journalists',
|
||||
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||
'doc_type' => $docType,
|
||||
])
|
||||
->preservingOriginal()
|
||||
->toMediaCollection('journalists', 's3');
|
||||
} catch (\Exception $e) {
|
||||
// Log quietly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateLocationCommand extends Command
|
||||
{
|
||||
@ -20,54 +21,113 @@ class MigrateLocationCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate location data from legacy database';
|
||||
protected $description = 'Migrate location and sub-location data from legacy database with optimized processing';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting location migration...');
|
||||
$this->info('Starting optimized location migration...');
|
||||
|
||||
$this->migrateLocations();
|
||||
$this->newLine();
|
||||
$this->migrateSubLocations();
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Location migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate main locations.
|
||||
*/
|
||||
private function migrateLocations(): void
|
||||
{
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('locuses');
|
||||
|
||||
// Migrate Locations (Locuses)
|
||||
$this->info('Migrating locations...');
|
||||
$legacyLocations = $legacyConn->table('locuses')->get();
|
||||
$totalCount = $query->count();
|
||||
$this->info("Found {$totalCount} legacy location records.");
|
||||
|
||||
$this->withProgressBar($legacyLocations, function ($legacy) {
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processLocation($legacy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy location record.
|
||||
*/
|
||||
private function processLocation($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} (LOC) Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('locations')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'name' => $legacy->name,
|
||||
'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value,
|
||||
'created_at' => $legacy->created_at,
|
||||
'updated_at' => $legacy->updated_at,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate sub-locations.
|
||||
*/
|
||||
private function migrateSubLocations(): void
|
||||
{
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('sub_locuses');
|
||||
|
||||
$totalCount = $query->count();
|
||||
$this->info("Found {$totalCount} legacy sub-location records.");
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processSubLocation($legacy);
|
||||
}
|
||||
});
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
// Migrate Sub Locations (Sub Locuses)
|
||||
$this->info('Migrating sub locations...');
|
||||
$legacySubLocations = $legacyConn->table('sub_locuses')->get();
|
||||
/**
|
||||
* Process a single legacy sub-location record.
|
||||
*/
|
||||
private function processSubLocation($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->withProgressBar($legacySubLocations, function ($legacy) {
|
||||
$this->output->write("{$idOutput} (SUB) Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('sub_locations')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'location_id' => $legacy->locus,
|
||||
'location_id' => $legacy->locus, // Foreign key to locations
|
||||
'name' => $legacy->name,
|
||||
'is_active' => $legacy->status == '1' ? IsActive::ACTIVE->value : IsActive::INACTIVE->value,
|
||||
'created_at' => $legacy->created_at,
|
||||
'updated_at' => $legacy->updated_at,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,11 @@
|
||||
|
||||
use App\Enums\Channel;
|
||||
use App\Enums\IsActive;
|
||||
use App\Models\MediaMonitoring;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateMediaMonitoringCommand extends Command
|
||||
@ -22,81 +25,194 @@ class MigrateMediaMonitoringCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate media monitoring data from legacy database';
|
||||
protected $description = 'Migrate media monitoring data with S3 images and optimized processing';
|
||||
|
||||
/**
|
||||
* Cache for migrated IDs to prevent redundant processing.
|
||||
*/
|
||||
private array $migratedIds = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting media monitoring migration...');
|
||||
$this->info('Starting optimized media monitoring migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('media_monitorings');
|
||||
|
||||
// Get legacy monitorings
|
||||
$legacyMonitorings = $legacyConn->table('media_monitorings')->get();
|
||||
|
||||
if ($legacyMonitorings->isEmpty()) {
|
||||
$this->warn('No media monitorings found in legacy database.');
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No media monitoring found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyMonitorings, function ($legacy) {
|
||||
// Main record
|
||||
DB::table('media_monitorings')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'code' => Str::limit($legacy->code, 10, ''),
|
||||
'media_name' => Str::limit($legacy->media_name, 50, ''),
|
||||
'title' => $legacy->title,
|
||||
'channel' => $this->mapChannel($legacy->channel),
|
||||
'writter' => Str::limit($legacy->writter, 50, ''),
|
||||
'link' => $legacy->link,
|
||||
'quote' => $legacy->quote,
|
||||
'content' => $legacy->content,
|
||||
'influencer' => Str::limit($legacy->influencer, 50, ''),
|
||||
'keyword' => Str::limit($legacy->keyword, 100, ''),
|
||||
'release_date' => $legacy->release_date,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
$this->info("Found {$totalCount} legacy media monitoring records.");
|
||||
|
||||
// Handle Theme (Insert into themes table if not exists)
|
||||
$themeName = trim($legacy->theme);
|
||||
if (! empty($themeName)) {
|
||||
$themeName = Str::limit($themeName, 50, '');
|
||||
// Pre-cache migrated IDs using their associated media records
|
||||
$this->migratedIds = DB::table('media')
|
||||
->where('model_type', MediaMonitoring::class)
|
||||
->where('collection_name', 'media-monitorings')
|
||||
->pluck('model_id')
|
||||
->flip()
|
||||
->all();
|
||||
|
||||
// Ensure theme exists in master table
|
||||
DB::table('themes')->updateOrInsert(
|
||||
['name' => $themeName],
|
||||
[
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
$themeId = DB::table('themes')->where('name', $themeName)->value('id');
|
||||
|
||||
// Link to pivot table
|
||||
if ($themeId) {
|
||||
DB::table('media_monitoring_theme')->updateOrInsert(
|
||||
[
|
||||
'media_monitoring_id' => $legacy->id,
|
||||
'theme_id' => $themeId,
|
||||
]
|
||||
);
|
||||
}
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processMonitoring($legacy);
|
||||
}
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Media monitoring migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy media monitoring record.
|
||||
*/
|
||||
private function processMonitoring($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->title, 40).'</comment>... ');
|
||||
|
||||
// Step 1: Basic data update
|
||||
DB::table('media_monitorings')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
'code' => Str::limit($legacy->code, 10, ''),
|
||||
'media_name' => Str::limit($legacy->media_name, 50, ''),
|
||||
'title' => $legacy->title,
|
||||
'channel' => $this->mapChannel($legacy->channel),
|
||||
'writter' => Str::limit($legacy->writter, 50, ''),
|
||||
'link' => $legacy->link,
|
||||
'quote' => $legacy->quote,
|
||||
'content' => $legacy->content,
|
||||
'influencer' => Str::limit($legacy->influencer, 50, ''),
|
||||
'keyword' => Str::limit($legacy->keyword, 100, ''),
|
||||
'release_date' => $legacy->release_date,
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
|
||||
// Handle Theme
|
||||
$this->handleTheme($legacy);
|
||||
|
||||
// Step 2: Skip heavy operations (media) if already has media
|
||||
if (isset($this->migratedIds[$legacy->id])) {
|
||||
$this->output->writeln('<info>SKIP (Media Exists)</info>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$monitoring = MediaMonitoring::withTrashed()->find($legacy->id);
|
||||
if (! $monitoring) {
|
||||
$this->output->writeln('<error>NOT FOUND</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Handle Media Migration
|
||||
if (! empty($legacy->image) && $legacy->image !== 'no-image.png') {
|
||||
$this->migrateMedia($monitoring, $legacy);
|
||||
} else {
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle theme pivot and master data.
|
||||
*/
|
||||
private function handleTheme($legacy): void
|
||||
{
|
||||
$themeName = trim($legacy->theme);
|
||||
if (empty($themeName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$themeName = Str::limit($themeName, 50, '');
|
||||
|
||||
// Ensure theme exists
|
||||
DB::table('themes')->updateOrInsert(
|
||||
['name' => $themeName],
|
||||
[
|
||||
'is_active' => IsActive::ACTIVE->value,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
$themeId = DB::table('themes')->where('name', $themeName)->value('id');
|
||||
|
||||
if ($themeId) {
|
||||
DB::table('media_monitoring_theme')->updateOrInsert(
|
||||
[
|
||||
'media_monitoring_id' => $legacy->id,
|
||||
'theme_id' => $themeId,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate media from S3 to Spatie Media Library.
|
||||
*/
|
||||
private function migrateMedia(MediaMonitoring $monitoring, $legacy): void
|
||||
{
|
||||
$filename = trim(basename($legacy->image));
|
||||
$sourcePath = 'old data/mediamonitorings/'.$filename;
|
||||
|
||||
$exists = false;
|
||||
$attempts = 0;
|
||||
$maxAttempts = 3;
|
||||
|
||||
while ($attempts < $maxAttempts) {
|
||||
try {
|
||||
$exists = Storage::disk('s3')->exists($sourcePath);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
$attempts++;
|
||||
if ($attempts >= $maxAttempts) {
|
||||
$actualError = $e->getPrevious() ? $e->getPrevious()->getMessage() : $e->getMessage();
|
||||
$this->output->writeln("<error>S3 ERROR ({$attempts}x): ".Str::limit($actualError, 50).'</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
usleep(200000);
|
||||
}
|
||||
}
|
||||
|
||||
if (! $exists) {
|
||||
$this->output->writeln('<error>IMAGE MISSING</error>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$date = Carbon::parse($legacy->created_at ?? now())->toDateString();
|
||||
|
||||
$monitoring->addMediaFromDisk($sourcePath, 's3')
|
||||
->withCustomProperties([
|
||||
'feature' => 'media-monitorings',
|
||||
'date' => $date,
|
||||
])
|
||||
->preservingOriginal()
|
||||
->toMediaCollection('media-monitorings', 's3');
|
||||
|
||||
$this->output->writeln('<info>DONE + IMG</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map channel string to Enum value.
|
||||
*/
|
||||
private function mapChannel(?string $legacyChannel): int
|
||||
{
|
||||
return match (strtolower($legacyChannel ?? '')) {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
use App\Enums\IsActive;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class MigrateThemeCommand extends Command
|
||||
{
|
||||
@ -20,27 +21,48 @@ class MigrateThemeCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate theme data from legacy proof_airing_themes table';
|
||||
protected $description = 'Migrate theme data from legacy proof_airing_themes table with optimized processing';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting theme migration...');
|
||||
$this->info('Starting optimized theme migration...');
|
||||
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('proof_airing_themes');
|
||||
|
||||
// Get legacy themes
|
||||
$legacyThemes = $legacyConn->table('proof_airing_themes')->get();
|
||||
|
||||
if ($legacyThemes->isEmpty()) {
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No themes found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->withProgressBar($legacyThemes, function ($legacy) {
|
||||
$this->info("Found {$totalCount} legacy theme records.");
|
||||
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processTheme($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$this->newLine();
|
||||
$this->info('Theme migration completed successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single legacy theme record.
|
||||
*/
|
||||
private function processTheme($legacy): void
|
||||
{
|
||||
$idText = "[#{$legacy->id}]";
|
||||
$idOutput = $legacy->deleted_at ? "<fg=red>{$idText}</>" : "<info>{$idText}</info>";
|
||||
|
||||
$this->output->write("{$idOutput} Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
try {
|
||||
DB::table('themes')->updateOrInsert(
|
||||
['id' => $legacy->id],
|
||||
[
|
||||
@ -51,9 +73,10 @@ public function handle()
|
||||
'deleted_at' => $legacy->deleted_at,
|
||||
]
|
||||
);
|
||||
});
|
||||
$this->newLine();
|
||||
|
||||
$this->info('Theme migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,53 +24,89 @@ class MigrateUserCommand extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Migrate users from legacy database';
|
||||
protected $description = 'Migrate users from legacy database with optimized processing and detailed output';
|
||||
|
||||
/**
|
||||
* Cache for existing user IDs and emails to prevent redundant queries.
|
||||
*/
|
||||
private array $existingUserIds = [];
|
||||
|
||||
private array $existingUserEmails = [];
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('Starting user migration...');
|
||||
$this->info('Starting optimized user migration...');
|
||||
|
||||
$legacyUsers = DB::connection('mysql_second')->table('users')->where('deleted_at', null)->get();
|
||||
$legacyConn = DB::connection('mysql_second');
|
||||
$query = $legacyConn->table('users')->whereNull('deleted_at');
|
||||
|
||||
if ($legacyUsers->isEmpty()) {
|
||||
$totalCount = $query->count();
|
||||
if ($totalCount === 0) {
|
||||
$this->warn('No users found in legacy database.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($legacyUsers as $legacy) {
|
||||
$existingById = User::where('id', $legacy->id)->first();
|
||||
$existingByEmail = User::where('email', $legacy->email)->first();
|
||||
$this->info("Found {$totalCount} legacy user records.");
|
||||
|
||||
$roleName = $this->mapRole($legacy->role);
|
||||
// Pre-cache existing data to speed up individual checks
|
||||
$this->existingUserIds = User::pluck('id')->flip()->all();
|
||||
$this->existingUserEmails = User::pluck('email')->flip()->all();
|
||||
|
||||
if (! $existingById && ! $existingByEmail) {
|
||||
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||
foreach ($chunk as $legacy) {
|
||||
$this->processUser($legacy);
|
||||
}
|
||||
});
|
||||
|
||||
$username = $this->generateUniqueUsername($legacy->name, $legacy->email);
|
||||
$this->newLine();
|
||||
$this->info('User migration completed successfully!');
|
||||
}
|
||||
|
||||
$userId = DB::table('users')->insertGetId([
|
||||
'id' => $legacy->id,
|
||||
'name' => Str::limit($legacy->name, 100),
|
||||
'email' => $legacy->email,
|
||||
'username' => $username,
|
||||
'password' => $legacy->password,
|
||||
'is_active' => $legacy->status == '2'
|
||||
? IsActive::INACTIVE->value
|
||||
: IsActive::ACTIVE->value,
|
||||
'email_verified_at' => now(),
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
]);
|
||||
/**
|
||||
* Process a single legacy user record.
|
||||
*/
|
||||
private function processUser($legacy): void
|
||||
{
|
||||
$this->output->write("<info>[#{$legacy->id}]</info> Processing: <comment>".Str::limit($legacy->name, 40).'</comment>... ');
|
||||
|
||||
$user = User::find($userId);
|
||||
// Check for duplicates by ID or Email
|
||||
if (isset($this->existingUserIds[$legacy->id]) || isset($this->existingUserEmails[$legacy->email])) {
|
||||
$this->output->writeln('<info>SKIP (Already Exists)</info>');
|
||||
|
||||
if ($user && $roleName) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$username = $this->generateUniqueUsername($legacy->name, $legacy->email);
|
||||
|
||||
$id = DB::table('users')->insertGetId([
|
||||
'id' => $legacy->id,
|
||||
'name' => Str::limit($legacy->name, 100),
|
||||
'email' => $legacy->email,
|
||||
'username' => $username,
|
||||
'password' => $legacy->password,
|
||||
'is_active' => $legacy->status == '2'
|
||||
? IsActive::INACTIVE->value
|
||||
: IsActive::ACTIVE->value,
|
||||
'email_verified_at' => now(),
|
||||
'created_at' => $legacy->created_at ?? now(),
|
||||
'updated_at' => $legacy->updated_at ?? now(),
|
||||
]);
|
||||
|
||||
$user = User::find($id);
|
||||
|
||||
if ($user) {
|
||||
// Assign Role
|
||||
$roleName = $this->mapRole($legacy->role);
|
||||
if ($roleName) {
|
||||
$user->syncRoles([$roleName]);
|
||||
}
|
||||
|
||||
// Initialize User Settings if missing
|
||||
if (! $user->settings()->exists()) {
|
||||
$user->settings()->create([
|
||||
'notif_style' => NotifStyle::FORMAL,
|
||||
@ -81,18 +117,21 @@ public function handle()
|
||||
'top_navigation' => false,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$this->info('User already exists: '.$legacy->email);
|
||||
}
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
$this->info('User migration completed successfully!');
|
||||
$this->output->writeln('<info>DONE</info>');
|
||||
} else {
|
||||
$this->output->writeln('<error>FAILED TO RETRIEVE</error>');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||
}
|
||||
}
|
||||
|
||||
private function generateUniqueUsername($name, $email)
|
||||
/**
|
||||
* Generate a unique username based on name or email.
|
||||
*/
|
||||
private function generateUniqueUsername($name, $email): string
|
||||
{
|
||||
// Try name first
|
||||
$base = Str::limit(Str::slug($name, ''), 10, '');
|
||||
if (empty($base)) {
|
||||
$base = Str::limit(explode('@', $email)[0], 10, '');
|
||||
@ -101,7 +140,6 @@ private function generateUniqueUsername($name, $email)
|
||||
$username = $base;
|
||||
$counter = 1;
|
||||
|
||||
// Ensure unique within 10 chars
|
||||
while (User::where('username', $username)->exists()) {
|
||||
$suffix = (string) $counter;
|
||||
$maxBaseLength = 10 - strlen($suffix);
|
||||
@ -110,12 +148,15 @@ private function generateUniqueUsername($name, $email)
|
||||
|
||||
if ($counter > 999) {
|
||||
break;
|
||||
} // Safety
|
||||
}
|
||||
}
|
||||
|
||||
return $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map legacy role IDs to RoleEnum.
|
||||
*/
|
||||
private function mapRole($legacyRole): ?string
|
||||
{
|
||||
return match ((int) $legacyRole) {
|
||||
|
||||
@ -92,7 +92,7 @@ public static function configure(Schema $schema): Schema
|
||||
->disk(config('filesystems.default'))
|
||||
->acceptedFileTypes(['image/*'])
|
||||
->maxSize(1024 * 3)
|
||||
->collection('content-recpaps')
|
||||
->collection('content-recaps')
|
||||
->customProperties(fn (): array => [
|
||||
'feature' => 'content-recaps',
|
||||
'date' => now()->toDateString(),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user