refactor: split monolithic MigrateCooperationCommand into dedicated migration commands for media orders, media cooperation, and reports
This commit is contained in:
parent
bce5776696
commit
198e936798
@ -1,320 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Enums\ApprovalStatus;
|
|
||||||
use App\Enums\CooperationStatus;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class MigrateCooperationCommand extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'migrate:cooperation';
|
|
||||||
|
|
||||||
protected $description = 'Migrate cooperation data from legacy database';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map: legacy media_cooperation id => new cooperation_id
|
|
||||||
* Dipakai untuk lookup saat migrate proposals
|
|
||||||
*/
|
|
||||||
private array $legacyCoopMap = [];
|
|
||||||
|
|
||||||
public function handle(): void
|
|
||||||
{
|
|
||||||
$this->migrateMediaOrders();
|
|
||||||
$this->migrateMediaCooperation();
|
|
||||||
$this->migrateProposals();
|
|
||||||
$this->migrateReports();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function migrateMediaOrders(): void
|
|
||||||
{
|
|
||||||
$this->info('Migrating media_orders → cooperations...');
|
|
||||||
|
|
||||||
$groups = DB::connection('mysql_second')
|
|
||||||
->table('media_orders')
|
|
||||||
->whereNull('deleted_at')
|
|
||||||
->orderBy('id')
|
|
||||||
->get()
|
|
||||||
->groupBy(fn ($item) => strtolower(trim($item->name)));
|
|
||||||
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
$first = $group->first();
|
|
||||||
$cooperationId = $this->insertCooperation(
|
|
||||||
name: $first->name,
|
|
||||||
description: $first->description,
|
|
||||||
initialDate: $group->min('begin'),
|
|
||||||
finalDate: $group->max('end'),
|
|
||||||
status: CooperationStatus::COMPLETED->value,
|
|
||||||
completedAt: $group->max('end'),
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->insertCooperationMedia(
|
|
||||||
group: $group,
|
|
||||||
cooperationId: $cooperationId,
|
|
||||||
approvalStatus: ApprovalStatus::ACCEPTED->value,
|
|
||||||
);
|
|
||||||
|
|
||||||
$startDate = Carbon::parse($group->max('end'))->addDay();
|
|
||||||
$endDate = $startDate->copy()->addDays(7);
|
|
||||||
|
|
||||||
DB::table('task_assignments')->insert([
|
|
||||||
'cooperation_id' => $cooperationId,
|
|
||||||
'start_date' => $startDate->toDateString(),
|
|
||||||
'end_date' => $endDate->toDateString(),
|
|
||||||
'task_description' => $first->description ?? '',
|
|
||||||
'report_amount' => $group->sum('amount_report') ?: 1,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$taskAssignmentId = (int) DB::getPdo()->lastInsertId();
|
|
||||||
|
|
||||||
$this->insertMediaTaskAssignment(
|
|
||||||
cooperationId: $cooperationId,
|
|
||||||
taskAssignmentId: $taskAssignmentId,
|
|
||||||
approvalStatus: ApprovalStatus::ACCEPTED->value,
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->info("Merged {$group->count()} rows → {$first->name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('media_orders migration done.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function migrateMediaCooperation(): void
|
|
||||||
{
|
|
||||||
$this->info('Migrating media_cooperation → cooperations...');
|
|
||||||
|
|
||||||
$groups = DB::connection('mysql_second')
|
|
||||||
->table('media_cooperation')
|
|
||||||
->whereNull('deleted_at')
|
|
||||||
->orderBy('id')
|
|
||||||
->get()
|
|
||||||
->groupBy(fn ($item) => strtolower(trim($item->name)));
|
|
||||||
|
|
||||||
foreach ($groups as $group) {
|
|
||||||
$first = $group->first();
|
|
||||||
$cooperationId = $this->insertCooperation(
|
|
||||||
name: $first->name,
|
|
||||||
description: $first->description,
|
|
||||||
initialDate: $group->min('begin'),
|
|
||||||
finalDate: $group->max('end'),
|
|
||||||
status: CooperationStatus::PENDING->value,
|
|
||||||
completedAt: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->insertCooperationMedia(
|
|
||||||
group: $group,
|
|
||||||
cooperationId: $cooperationId,
|
|
||||||
approvalStatus: ApprovalStatus::PENDING->value,
|
|
||||||
);
|
|
||||||
|
|
||||||
$startDate = Carbon::parse($group->max('end'))->addDay();
|
|
||||||
$endDate = $startDate->copy()->addDays(7);
|
|
||||||
|
|
||||||
DB::table('task_assignments')->insert([
|
|
||||||
'cooperation_id' => $cooperationId,
|
|
||||||
'start_date' => $startDate->toDateString(),
|
|
||||||
'end_date' => $endDate->toDateString(),
|
|
||||||
'task_description' => $first->description ?? '',
|
|
||||||
'report_amount' => 1,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$taskAssignmentId = (int) DB::getPdo()->lastInsertId();
|
|
||||||
|
|
||||||
$this->insertMediaTaskAssignment(
|
|
||||||
cooperationId: $cooperationId,
|
|
||||||
taskAssignmentId: $taskAssignmentId,
|
|
||||||
approvalStatus: ApprovalStatus::PENDING->value,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Simpan mapping legacy id → new cooperation_id untuk dipakai saat migrate proposals
|
|
||||||
foreach ($group as $item) {
|
|
||||||
$this->legacyCoopMap[$item->id] = $cooperationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info("Merged {$group->count()} rows → {$first->name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('media_cooperation migration done.');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function migrateProposals(): void
|
|
||||||
{
|
|
||||||
$this->info('Migrating propose_media_cooperation → cooperation_proposals...');
|
|
||||||
|
|
||||||
$proposals = DB::connection('mysql_second')
|
|
||||||
->table('propose_media_cooperation')
|
|
||||||
->whereNull('deleted_at')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($proposals as $proposal) {
|
|
||||||
$cooperationId = $this->legacyCoopMap[$proposal->media_cooperation_id] ?? null;
|
|
||||||
|
|
||||||
if (! $cooperationId) {
|
|
||||||
$this->warn("cooperation not found for proposal {$proposal->id} (media_cooperation_id: {$proposal->media_cooperation_id})");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$partnerMediaId = DB::table('partner_media')
|
|
||||||
->join('companies', 'partner_media.company_id', '=', 'companies.id')
|
|
||||||
->where('companies.user_id', $proposal->enhancer)
|
|
||||||
->value('partner_media.id');
|
|
||||||
|
|
||||||
if (! $partnerMediaId) {
|
|
||||||
$this->warn("partner_media not found for proposal {$proposal->id} (enhancer/user_id: {$proposal->enhancer})");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::table('cooperation_proposals')->insert([
|
|
||||||
'cooperation_id' => $cooperationId,
|
|
||||||
'partner_media_id' => $partnerMediaId,
|
|
||||||
'description' => null,
|
|
||||||
'e_catalog' => null,
|
|
||||||
'status' => $this->mapApprovalStatus($proposal->status),
|
|
||||||
'submitted_at' => $proposal->created_at ?? now(),
|
|
||||||
'responded_at' => $proposal->updated_at,
|
|
||||||
'created_at' => $proposal->created_at ?? now(),
|
|
||||||
'updated_at' => $proposal->updated_at ?? now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('proposals migration done.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// =========================================================
|
|
||||||
// Helper Methods
|
|
||||||
// =========================================================
|
|
||||||
|
|
||||||
private function insertCooperation(
|
|
||||||
string $name,
|
|
||||||
?string $description,
|
|
||||||
string $initialDate,
|
|
||||||
string $finalDate,
|
|
||||||
string $status,
|
|
||||||
?string $completedAt,
|
|
||||||
): int {
|
|
||||||
DB::table('cooperations')->insert([
|
|
||||||
'title' => $name,
|
|
||||||
'initial_submission_date' => $initialDate,
|
|
||||||
'final_submission_date' => $finalDate,
|
|
||||||
'description' => $description ?? '',
|
|
||||||
'payment_amount' => null,
|
|
||||||
'payment_date' => null,
|
|
||||||
'completed_at' => $completedAt,
|
|
||||||
'status' => $status,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (int) DB::getPdo()->lastInsertId();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function insertCooperationMedia(
|
|
||||||
Collection $group,
|
|
||||||
int $cooperationId,
|
|
||||||
string $approvalStatus,
|
|
||||||
): void {
|
|
||||||
$validMediaIds = DB::table('partner_media')->pluck('id')->toArray();
|
|
||||||
|
|
||||||
$mediaIds = collect();
|
|
||||||
|
|
||||||
foreach ($group as $item) {
|
|
||||||
if (! $item->media) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$mediaIds = $mediaIds->merge(explode(',', $item->media));
|
|
||||||
}
|
|
||||||
|
|
||||||
$mediaIds
|
|
||||||
->map(fn ($id) => (int) $id)
|
|
||||||
->filter(fn ($id) => in_array($id, $validMediaIds))
|
|
||||||
->unique()
|
|
||||||
->each(function ($mediaId) use ($cooperationId, $approvalStatus) {
|
|
||||||
DB::table('cooperation_media')->insert([
|
|
||||||
'cooperation_id' => $cooperationId,
|
|
||||||
'partner_media_id' => $mediaId,
|
|
||||||
'status' => $approvalStatus,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapApprovalStatus(string $legacy): string
|
|
||||||
{
|
|
||||||
return match ($legacy) {
|
|
||||||
'accepted' => ApprovalStatus::ACCEPTED->value,
|
|
||||||
'rejected' => ApprovalStatus::REJECTED->value,
|
|
||||||
default => ApprovalStatus::PENDING->value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function insertMediaTaskAssignment(
|
|
||||||
int $cooperationId,
|
|
||||||
int $taskAssignmentId,
|
|
||||||
string $approvalStatus,
|
|
||||||
): void {
|
|
||||||
$mediaIds = DB::table('cooperation_media')
|
|
||||||
->where('cooperation_id', $cooperationId)
|
|
||||||
->pluck('partner_media_id');
|
|
||||||
|
|
||||||
foreach ($mediaIds as $partnerMediaId) {
|
|
||||||
DB::table('media_task_assignment')->insert([
|
|
||||||
'task_assignment_id' => $taskAssignmentId,
|
|
||||||
'partner_media_id' => $partnerMediaId,
|
|
||||||
'status' => $approvalStatus,
|
|
||||||
'created_at' => now(),
|
|
||||||
'updated_at' => now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function migrateReports(): void
|
|
||||||
{
|
|
||||||
$this->info('Migrating reports → reports...');
|
|
||||||
|
|
||||||
$legacyReports = DB::connection('mysql_second')
|
|
||||||
->table('reports')
|
|
||||||
->whereNull('deleted_at')
|
|
||||||
->get();
|
|
||||||
|
|
||||||
foreach ($legacyReports as $legacy) {
|
|
||||||
// Cari media_task_assignment berdasarkan partner_media_id
|
|
||||||
// dan task_assignment yang berelasi ke cooperation dengan tanggal yang sesuai
|
|
||||||
$mediaTaskAssignment = DB::table('media_task_assignment')
|
|
||||||
->join('task_assignments', 'media_task_assignment.task_assignment_id', '=', 'task_assignments.id')
|
|
||||||
->where('media_task_assignment.partner_media_id', $legacy->media)
|
|
||||||
->whereDate('task_assignments.start_date', '<=', $legacy->broadcast)
|
|
||||||
->whereDate('task_assignments.end_date', '>=', $legacy->broadcast)
|
|
||||||
->select('media_task_assignment.id')
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (! $mediaTaskAssignment) {
|
|
||||||
$this->warn("media_task_assignment not found for report {$legacy->id}");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::table('reports')->insert([
|
|
||||||
'media_task_assignment_id' => $mediaTaskAssignment->id,
|
|
||||||
'title' => $legacy->title,
|
|
||||||
'publication_date' => $legacy->broadcast,
|
|
||||||
'link' => $legacy->link,
|
|
||||||
'description' => $legacy->proof ?? '',
|
|
||||||
'status' => $this->mapApprovalStatus($legacy->status),
|
|
||||||
'created_at' => $legacy->created_at ?? now(),
|
|
||||||
'updated_at' => $legacy->updated_at ?? now(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->info('reports migration done.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
310
app/Console/Commands/MigrateMediaCooperationCommand.php
Normal file
310
app/Console/Commands/MigrateMediaCooperationCommand.php
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Models\Cooperation;
|
||||||
|
use App\Models\CooperationProposal;
|
||||||
|
use App\Models\PartnerMedia;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class MigrateMediaCooperationCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'migrate:media-cooperation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Migrate media_cooperation from legacy database → cooperations and cooperation_proposals';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for migrated cooperation titles to avoid redundant processing.
|
||||||
|
*/
|
||||||
|
private array $migratedTitles = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for proposal media to avoid re-importing files.
|
||||||
|
*/
|
||||||
|
private array $migratedProposalIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for User ID to Partner Media ID mapping.
|
||||||
|
*/
|
||||||
|
private array $userToMediaMap = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$this->info('Starting migration: media_cooperation → cooperations...');
|
||||||
|
|
||||||
|
$legacyConn = DB::connection('mysql_second');
|
||||||
|
$query = $legacyConn->table('media_cooperation')->whereNull('deleted_at');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$totalCount = $query->count();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->error('Table media_cooperation not found or error accessing legacy database.');
|
||||||
|
$this->error($e->getMessage());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($totalCount === 0) {
|
||||||
|
$this->warn('No media_cooperation found in legacy database.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Found {$totalCount} legacy media_cooperation records.");
|
||||||
|
|
||||||
|
// Pre-cache migrated cooperation titles
|
||||||
|
$this->migratedTitles = DB::table('cooperations')
|
||||||
|
->pluck('title')
|
||||||
|
->map(fn ($t) => strtolower(trim($t)))
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Pre-cache proposal media
|
||||||
|
$this->migratedProposalIds = DB::table('media')
|
||||||
|
->where('model_type', CooperationProposal::class)
|
||||||
|
->where('collection_name', 'proposals')
|
||||||
|
->pluck('model_id')
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Pre-load User to Partner Media mapping
|
||||||
|
$this->loadUserToMediaMap();
|
||||||
|
|
||||||
|
// Load all records and group by name
|
||||||
|
$all = $query->orderBy('id')->get();
|
||||||
|
$groups = $all->groupBy(fn ($item) => strtolower(trim($item->name)));
|
||||||
|
|
||||||
|
$this->info("Grouped into {$groups->count()} unique cooperation group(s).");
|
||||||
|
|
||||||
|
foreach ($groups as $groupKey => $group) {
|
||||||
|
$this->processGroup($groupKey, $group);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->newLine();
|
||||||
|
$this->info('media_cooperation migration completed successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load mapping from User ID to Partner Media ID.
|
||||||
|
* Path: User -> Company -> PartnerMedia
|
||||||
|
*/
|
||||||
|
private function loadUserToMediaMap(): void
|
||||||
|
{
|
||||||
|
$this->userToMediaMap = DB::table('partner_media')
|
||||||
|
->join('companies', 'partner_media.company_id', '=', 'companies.id')
|
||||||
|
->whereNotNull('companies.user_id')
|
||||||
|
->pluck('partner_media.id', 'companies.user_id')
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single group of media_cooperation records.
|
||||||
|
*/
|
||||||
|
private function processGroup(string $groupKey, Collection $group): void
|
||||||
|
{
|
||||||
|
$first = $group->first();
|
||||||
|
$label = Str::limit($first->name, 40);
|
||||||
|
$this->output->write("<info>[group]</info> Processing: <comment>{$label}</comment>... ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if cooperation already exists by title
|
||||||
|
$existingCooperation = DB::table('cooperations')
|
||||||
|
->whereRaw('LOWER(TRIM(title)) = ?', [$groupKey])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($existingCooperation) {
|
||||||
|
$cooperationId = $existingCooperation->id;
|
||||||
|
$this->output->write('<info>JOINING EXISTING</info>... ');
|
||||||
|
} else {
|
||||||
|
$cooperationId = $this->insertCooperation(
|
||||||
|
name: $first->name,
|
||||||
|
description: $first->description,
|
||||||
|
initialDate: $group->min('begin'),
|
||||||
|
finalDate: $group->max('end'),
|
||||||
|
status: CooperationStatus::COMPLETED->value,
|
||||||
|
);
|
||||||
|
$this->output->write('<info>CREATED NEW</info>... ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: insert cooperation_media if the legacy table has a 'media' column with IDs
|
||||||
|
if (isset($first->media)) {
|
||||||
|
$this->insertCooperationMedia($group, $cooperationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate proposals belonging to this group's media_cooperation
|
||||||
|
$this->migrateProposals(
|
||||||
|
group: $group,
|
||||||
|
cooperationId: $cooperationId
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->output->writeln('<info>DONE</info>');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertCooperation(
|
||||||
|
string $name,
|
||||||
|
?string $description,
|
||||||
|
?string $initialDate,
|
||||||
|
?string $finalDate,
|
||||||
|
string $status
|
||||||
|
): int {
|
||||||
|
DB::table('cooperations')->insert([
|
||||||
|
'title' => $name,
|
||||||
|
'initial_submission_date' => $initialDate,
|
||||||
|
'final_submission_date' => $finalDate,
|
||||||
|
'description' => $description ?? '',
|
||||||
|
'status' => $status,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (int) DB::getPdo()->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertCooperationMedia(Collection $group, int $cooperationId): void
|
||||||
|
{
|
||||||
|
$validMediaIds = DB::table('partner_media')->pluck('id')->toArray();
|
||||||
|
$mediaStatusMap = [];
|
||||||
|
|
||||||
|
foreach ($group as $item) {
|
||||||
|
if (empty($item->media)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array_map('trim', explode(',', $item->media));
|
||||||
|
$statuses = array_map('trim', explode(',', $item->status ?? ''));
|
||||||
|
|
||||||
|
foreach ($ids as $index => $rawId) {
|
||||||
|
$mediaId = (int) $rawId;
|
||||||
|
if (! in_array($mediaId, $validMediaIds)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$legacyStatus = $statuses[$index] ?? '1';
|
||||||
|
$mediaStatusMap[$mediaId] = $this->mapApprovalStatus($legacyStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($mediaStatusMap as $mediaId => $approvalStatus) {
|
||||||
|
DB::table('cooperation_media')->insert([
|
||||||
|
'cooperation_id' => $cooperationId,
|
||||||
|
'partner_media_id' => $mediaId,
|
||||||
|
'status' => $approvalStatus,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function migrateProposals(Collection $group, int $cooperationId): void
|
||||||
|
{
|
||||||
|
$legacyConn = DB::connection('mysql_second');
|
||||||
|
$legacyIds = $group->pluck('id')->toArray();
|
||||||
|
|
||||||
|
$legacyProposals = $legacyConn->table('propose_media_cooperation')
|
||||||
|
->whereIn('media_cooperation_id', $legacyIds)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($legacyProposals as $legacyProposal) {
|
||||||
|
$this->processProposal($legacyProposal, $cooperationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processProposal(object $legacy, int $cooperationId): void
|
||||||
|
{
|
||||||
|
$userId = $legacy->enhancer;
|
||||||
|
$partnerMediaId = $this->userToMediaMap[$userId] ?? null;
|
||||||
|
|
||||||
|
if (! $partnerMediaId) {
|
||||||
|
// Fallback or skip if media not found
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this proposal already exists to avoid duplication
|
||||||
|
$exists = DB::table('cooperation_proposals')
|
||||||
|
->where('cooperation_id', $cooperationId)
|
||||||
|
->where('partner_media_id', $partnerMediaId)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($exists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::table('cooperation_proposals')->insert([
|
||||||
|
'cooperation_id' => $cooperationId,
|
||||||
|
'partner_media_id' => $partnerMediaId,
|
||||||
|
'status' => $this->mapApprovalStatus((string) $legacy->status),
|
||||||
|
'submitted_at' => $legacy->created_at ?? now(),
|
||||||
|
'created_at' => $legacy->created_at ?? now(),
|
||||||
|
'updated_at' => $legacy->updated_at ?? now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$proposalId = (int) DB::getPdo()->lastInsertId();
|
||||||
|
|
||||||
|
// Migrate offer_file if exists and not already migrated
|
||||||
|
if (! empty($legacy->offer_file) && ! isset($this->migratedProposalIds[$proposalId])) {
|
||||||
|
$this->importOfferFile($proposalId, $legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function importOfferFile(int $proposalId, object $legacy): void
|
||||||
|
{
|
||||||
|
$legacyPath = trim($legacy->offer_file);
|
||||||
|
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust path based on where these files are stored in S3
|
||||||
|
$sourcePath = 'old-data/proposals/'.$legacyPath;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Storage::disk('s3')->exists($sourcePath)) {
|
||||||
|
$proposal = CooperationProposal::find($proposalId);
|
||||||
|
if ($proposal) {
|
||||||
|
$proposal->addMediaFromDisk($sourcePath, 's3')
|
||||||
|
->withCustomProperties([
|
||||||
|
'feature' => 'proposals',
|
||||||
|
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||||
|
])
|
||||||
|
->preservingOriginal()
|
||||||
|
->toMediaCollection('proposals', 's3');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Log silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapApprovalStatus(string $legacyStatus): int
|
||||||
|
{
|
||||||
|
return match ($legacyStatus) {
|
||||||
|
'0' => ApprovalStatus::PENDING->value,
|
||||||
|
'1' => ApprovalStatus::ACCEPTED->value,
|
||||||
|
'2' => ApprovalStatus::REJECTED->value,
|
||||||
|
default => ApprovalStatus::ACCEPTED->value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
519
app/Console/Commands/MigrateMediaOrdersCommand.php
Normal file
519
app/Console/Commands/MigrateMediaOrdersCommand.php
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Enums\CooperationStatus;
|
||||||
|
use App\Models\Cooperation;
|
||||||
|
use App\Models\Report;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class MigrateMediaOrdersCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'migrate:media-orders';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Migrate media_orders from legacy database → cooperations, task assignments, and reports';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for migrated cooperation titles to avoid redundant processing.
|
||||||
|
*/
|
||||||
|
private array $migratedTitles = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for cooperation IDs that already have an offer file in media library.
|
||||||
|
*/
|
||||||
|
private array $migratedOfferIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for migrated report IDs that already have proof media.
|
||||||
|
*/
|
||||||
|
private array $migratedReportIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$this->info('Starting migration: media_orders → cooperations...');
|
||||||
|
|
||||||
|
$legacyConn = DB::connection('mysql_second');
|
||||||
|
$query = $legacyConn->table('media_orders')->whereNull('deleted_at');
|
||||||
|
|
||||||
|
$totalCount = $query->count();
|
||||||
|
if ($totalCount === 0) {
|
||||||
|
$this->warn('No media_orders found in legacy database.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Found {$totalCount} legacy media_order records.");
|
||||||
|
|
||||||
|
// Pre-cache migrated cooperation titles to skip already processed groups
|
||||||
|
$this->migratedTitles = DB::table('cooperations')
|
||||||
|
->pluck('title')
|
||||||
|
->map(fn ($t) => strtolower(trim($t)))
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Pre-cache cooperation IDs that already have an offer file
|
||||||
|
$this->migratedOfferIds = DB::table('media')
|
||||||
|
->where('model_type', Cooperation::class)
|
||||||
|
->where('collection_name', 'cooperations')
|
||||||
|
->pluck('model_id')
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Pre-cache report IDs that already have proof media (skip re-importing files)
|
||||||
|
$this->migratedReportIds = DB::table('media')
|
||||||
|
->where('model_type', Report::class)
|
||||||
|
->where('collection_name', 'reports')
|
||||||
|
->pluck('model_id')
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Load all records first (needed for groupBy), then process in chunks via collection
|
||||||
|
$all = $query->orderBy('id')->get();
|
||||||
|
$groups = $all->groupBy(fn ($item) => strtolower(trim($item->name)));
|
||||||
|
|
||||||
|
$this->info("Grouped into {$groups->count()} unique cooperation group(s).");
|
||||||
|
|
||||||
|
foreach ($groups->chunk(50) as $chunk) {
|
||||||
|
foreach ($chunk as $groupKey => $group) {
|
||||||
|
$this->processGroup($groupKey, $group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->newLine();
|
||||||
|
$this->info('media_orders migration completed successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single group of media_order records (one cooperation).
|
||||||
|
*/
|
||||||
|
private function processGroup(string $groupKey, Collection $group): void
|
||||||
|
{
|
||||||
|
$first = $group->first();
|
||||||
|
$label = Str::limit($first->name, 40);
|
||||||
|
$this->output->write("<info>[group]</info> Processing: <comment>{$label}</comment>... ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Skip if already migrated
|
||||||
|
if (isset($this->migratedTitles[$groupKey])) {
|
||||||
|
$this->output->writeln('<info>SKIP (already migrated)</info>');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$startDate = Carbon::parse($group->max('end'))->addDay();
|
||||||
|
$endDate = $startDate->copy()->addDays(7);
|
||||||
|
$paymentDate = $endDate->copy()->addDays(1);
|
||||||
|
$completedAt = $paymentDate->copy()->addDays(7);
|
||||||
|
|
||||||
|
$cooperationId = $this->insertCooperation(
|
||||||
|
name: $first->name,
|
||||||
|
description: $first->description,
|
||||||
|
initialDate: $group->min('begin'),
|
||||||
|
finalDate: $group->max('end'),
|
||||||
|
status: CooperationStatus::COMPLETED->value,
|
||||||
|
paymentDate: $paymentDate->toDateString(),
|
||||||
|
completedAt: $completedAt->toDateString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->insertCooperationMedia(
|
||||||
|
group: $group,
|
||||||
|
cooperationId: $cooperationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
DB::table('task_assignments')->insert([
|
||||||
|
'cooperation_id' => $cooperationId,
|
||||||
|
'start_date' => $startDate->toDateString(),
|
||||||
|
'end_date' => $endDate->toDateString(),
|
||||||
|
'task_description' => $first->description ?? '',
|
||||||
|
'report_amount' => $group->sum('amount_report'),
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$taskAssignmentId = (int) DB::getPdo()->lastInsertId();
|
||||||
|
|
||||||
|
$this->insertMediaTaskAssignment(
|
||||||
|
cooperationId: $cooperationId,
|
||||||
|
taskAssignmentId: $taskAssignmentId,
|
||||||
|
approvalStatus: ApprovalStatus::ACCEPTED->value,
|
||||||
|
);
|
||||||
|
|
||||||
|
// // Migrate offer_file_template documents for each item in the group
|
||||||
|
// if (! isset($this->migratedOfferIds[$cooperationId])) {
|
||||||
|
// $cooperation = Cooperation::find($cooperationId);
|
||||||
|
// if ($cooperation) {
|
||||||
|
// $this->migrateOfferFiles($cooperation, $group);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Migrate reports belonging to this group's media_orders
|
||||||
|
$this->migrateReports(
|
||||||
|
group: $group,
|
||||||
|
cooperationId: $cooperationId,
|
||||||
|
taskAssignmentId: $taskAssignmentId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cache so re-runs skip this title
|
||||||
|
$this->migratedTitles[$groupKey] = true;
|
||||||
|
|
||||||
|
$this->output->writeln('<info>DONE</info>');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Helper Methods
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
private function insertCooperation(
|
||||||
|
string $name,
|
||||||
|
?string $description,
|
||||||
|
string $initialDate,
|
||||||
|
string $finalDate,
|
||||||
|
string $status,
|
||||||
|
?string $paymentDate,
|
||||||
|
?string $completedAt,
|
||||||
|
): int {
|
||||||
|
DB::table('cooperations')->insert([
|
||||||
|
'title' => $name,
|
||||||
|
'initial_submission_date' => $initialDate,
|
||||||
|
'final_submission_date' => $finalDate,
|
||||||
|
'description' => $description ?? '',
|
||||||
|
'payment_amount' => null,
|
||||||
|
'payment_date' => $paymentDate,
|
||||||
|
'completed_at' => $completedAt,
|
||||||
|
'status' => $status,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (int) DB::getPdo()->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertCooperationMedia(
|
||||||
|
Collection $group,
|
||||||
|
int $cooperationId,
|
||||||
|
): void {
|
||||||
|
$validMediaIds = DB::table('partner_media')->pluck('id')->toArray();
|
||||||
|
|
||||||
|
// Build a map of mediaId → ApprovalStatus from all items in the group.
|
||||||
|
// Columns 'media' and 'status' are parallel comma-separated arrays:
|
||||||
|
// e.g. media="1,2,3" status="1,0,2" means media 1=accepted, 2=pending, 3=rejected
|
||||||
|
$mediaStatusMap = [];
|
||||||
|
|
||||||
|
foreach ($group as $item) {
|
||||||
|
if (! $item->media) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array_map('trim', explode(',', $item->media));
|
||||||
|
$statuses = array_map('trim', explode(',', $item->status ?? ''));
|
||||||
|
|
||||||
|
foreach ($ids as $index => $rawId) {
|
||||||
|
$mediaId = (int) $rawId;
|
||||||
|
$legacyStatus = $statuses[$index] ?? '0';
|
||||||
|
|
||||||
|
if (! in_array($mediaId, $validMediaIds)) {
|
||||||
|
$this->output->writeln(
|
||||||
|
" <fg=yellow> ↳ media #{$mediaId} SKIP (tidak ditemukan di tabel partner_media, report-nya akan ikut hilang)</>"
|
||||||
|
);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last status seen wins (latest record in group takes precedence)
|
||||||
|
$mediaStatusMap[$mediaId] = $this->mapApprovalStatus($legacyStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($mediaStatusMap as $mediaId => $approvalStatus) {
|
||||||
|
DB::table('cooperation_media')->insert([
|
||||||
|
'cooperation_id' => $cooperationId,
|
||||||
|
'partner_media_id' => $mediaId,
|
||||||
|
'status' => $approvalStatus,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate offer_file_template files for each item in the group to the Cooperation media library.
|
||||||
|
*/
|
||||||
|
private function migrateOfferFiles(Cooperation $cooperation, Collection $group): void
|
||||||
|
{
|
||||||
|
foreach ($group as $item) {
|
||||||
|
$legacyPath = trim($item->offer_file_template ?? '');
|
||||||
|
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourcePath = 'old-data/mediaorders/'.$legacyPath;
|
||||||
|
$this->importOfferFile($cooperation, $sourcePath, $item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single offer file to the Cooperation media library.
|
||||||
|
*/
|
||||||
|
private function importOfferFile(Cooperation $cooperation, string $sourcePath, object $item): 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 {
|
||||||
|
$cooperation->addMediaFromDisk($sourcePath, 's3')
|
||||||
|
->withCustomProperties([
|
||||||
|
'feature' => 'cooperations',
|
||||||
|
'date' => Carbon::parse($item->created_at ?? now())->toDateString(),
|
||||||
|
'doc_type' => 'proposal-template',
|
||||||
|
])
|
||||||
|
->preservingOriginal()
|
||||||
|
->toMediaCollection('cooperations', 's3');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Log quietly or handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function insertMediaTaskAssignment(
|
||||||
|
int $cooperationId,
|
||||||
|
int $taskAssignmentId,
|
||||||
|
string $approvalStatus,
|
||||||
|
): void {
|
||||||
|
$mediaIds = DB::table('cooperation_media')
|
||||||
|
->where('cooperation_id', $cooperationId)
|
||||||
|
->pluck('partner_media_id');
|
||||||
|
|
||||||
|
foreach ($mediaIds as $partnerMediaId) {
|
||||||
|
DB::table('media_task_assignment')->insert([
|
||||||
|
'task_assignment_id' => $taskAssignmentId,
|
||||||
|
'partner_media_id' => $partnerMediaId,
|
||||||
|
'status' => $approvalStatus,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate all legacy reports belonging to the given group of media_orders.
|
||||||
|
* Each item in the group can have multiple reports via reports.media_order = item.id.
|
||||||
|
*/
|
||||||
|
private function migrateReports(Collection $group, int $cooperationId, int $taskAssignmentId): void
|
||||||
|
{
|
||||||
|
$legacyConn = DB::connection('mysql_second');
|
||||||
|
|
||||||
|
// Collect all legacy media_order IDs in this group
|
||||||
|
$legacyOrderIds = $group->pluck('id')->toArray();
|
||||||
|
|
||||||
|
$legacyReports = $legacyConn
|
||||||
|
->table('reports')
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->whereIn('media_order', $legacyOrderIds)
|
||||||
|
->orderBy('id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($legacyReports->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($legacyReports as $legacyReport) {
|
||||||
|
$this->processReport(
|
||||||
|
legacyReport: $legacyReport,
|
||||||
|
cooperationId: $cooperationId,
|
||||||
|
taskAssignmentId: $taskAssignmentId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single legacy report record.
|
||||||
|
*/
|
||||||
|
private function processReport(object $legacyReport, int $cooperationId, int $taskAssignmentId): void
|
||||||
|
{
|
||||||
|
$partnerMediaId = (int) $legacyReport->media;
|
||||||
|
|
||||||
|
// Resolve media_task_assignment via partner_media_id = legacyReport.media
|
||||||
|
$mediaTaskAssignment = DB::table('media_task_assignment')
|
||||||
|
->where('task_assignment_id', $taskAssignmentId)
|
||||||
|
->where('partner_media_id', $partnerMediaId)
|
||||||
|
->select('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $mediaTaskAssignment) {
|
||||||
|
$this->output->writeln(
|
||||||
|
" <fg=yellow> ↳ report #{$legacyReport->id} SKIP (media #{$partnerMediaId} tidak terdaftar di kerja sama ini — tidak ada di media_orders.media)</>"
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$cleanTitle, $cleanLink] = $this->extractLinkData($legacyReport);
|
||||||
|
|
||||||
|
DB::table('reports')->insert([
|
||||||
|
'media_task_assignment_id' => $mediaTaskAssignment->id,
|
||||||
|
'title' => $cleanTitle,
|
||||||
|
'publication_date' => $legacyReport->broadcast,
|
||||||
|
'link' => $cleanLink,
|
||||||
|
'description' => '',
|
||||||
|
'status' => $this->mapApprovalStatus((string) $legacyReport->status),
|
||||||
|
'created_at' => $legacyReport->created_at ?? now(),
|
||||||
|
'updated_at' => $legacyReport->updated_at ?? now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newReportId = (int) DB::getPdo()->lastInsertId();
|
||||||
|
|
||||||
|
// // Migrate proof image if not already done
|
||||||
|
// if (! isset($this->migratedReportIds[$newReportId])) {
|
||||||
|
// $report = Report::find($newReportId);
|
||||||
|
// if ($report) {
|
||||||
|
// $this->migrateProof($report, $legacyReport);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ekstrak title & link bersih dari legacy report.
|
||||||
|
*
|
||||||
|
* Di DB lama kolom sering terbalik:
|
||||||
|
* - reports.title → berisi URL artikel
|
||||||
|
* - reports.link → berisi teks panjang (judul + isi + URL)
|
||||||
|
*
|
||||||
|
* Logika:
|
||||||
|
* 1. Jika title terdeteksi sebagai URL → title = baris pertama link (judul artikel)
|
||||||
|
* link = URL dari title
|
||||||
|
* 2. Jika tidak → ekstrak URL dari teks link, title tetap apa adanya (max 255 char)
|
||||||
|
*
|
||||||
|
* @return array{0: string, 1: string} [title, link]
|
||||||
|
*/
|
||||||
|
private function extractLinkData(object $legacyReport): array
|
||||||
|
{
|
||||||
|
$rawTitle = trim($legacyReport->title ?? '');
|
||||||
|
$rawLink = trim($legacyReport->link ?? '');
|
||||||
|
|
||||||
|
$titleIsUrl = (bool) preg_match('#^https?://#i', $rawTitle);
|
||||||
|
|
||||||
|
// Cari URL pertama di dalam teks rawLink
|
||||||
|
preg_match('#https?://\S+#', $rawLink, $urlMatches);
|
||||||
|
$extractedUrl = rtrim($urlMatches[0] ?? '', '.,;)');
|
||||||
|
|
||||||
|
if ($titleIsUrl) {
|
||||||
|
// title berisi URL → pakai sebagai link
|
||||||
|
$cleanLink = $rawTitle;
|
||||||
|
|
||||||
|
// Ambil judul dari baris pertama rawLink yang bukan URL
|
||||||
|
$lines = array_values(array_filter(
|
||||||
|
array_map('trim', preg_split('/[\r\n]+/', $rawLink))
|
||||||
|
));
|
||||||
|
$candidateTitle = '';
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (! preg_match('#^https?://#i', $line)) {
|
||||||
|
$candidateTitle = $line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cleanTitle = $candidateTitle !== ''
|
||||||
|
? mb_substr($candidateTitle, 0, 255)
|
||||||
|
: mb_substr($rawTitle, 0, 255);
|
||||||
|
} else {
|
||||||
|
// Normal: title sudah benar; ekstrak URL bersih dari link
|
||||||
|
$cleanLink = $extractedUrl ?: mb_substr($rawLink, 0, 2048);
|
||||||
|
$cleanTitle = mb_substr($rawTitle, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$cleanTitle, $cleanLink];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate the proof image file from S3 to the Report media library.
|
||||||
|
*/
|
||||||
|
private function migrateProof(Report $report, object $legacy): void
|
||||||
|
{
|
||||||
|
$legacyPath = trim($legacy->proof ?? '');
|
||||||
|
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourcePath = 'old-data/reports/'.$legacyPath;
|
||||||
|
|
||||||
|
$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 {
|
||||||
|
$report->addMediaFromDisk($sourcePath, 's3')
|
||||||
|
->withCustomProperties([
|
||||||
|
'feature' => 'reports',
|
||||||
|
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||||
|
'doc_type' => 'proof',
|
||||||
|
])
|
||||||
|
->preservingOriginal()
|
||||||
|
->toMediaCollection('reports', 's3');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Log quietly or handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map legacy status integer to ApprovalStatus enum value.
|
||||||
|
* Legacy: 0 = menunggu, 1 = menerima, 2 = menolak
|
||||||
|
*/
|
||||||
|
private function mapApprovalStatus(string $legacyStatus): int
|
||||||
|
{
|
||||||
|
return match ($legacyStatus) {
|
||||||
|
'0' => ApprovalStatus::PENDING->value,
|
||||||
|
'1' => ApprovalStatus::ACCEPTED->value,
|
||||||
|
'2' => ApprovalStatus::REJECTED->value,
|
||||||
|
default => ApprovalStatus::ACCEPTED->value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
230
app/Console/Commands/MigrateReportsCommand.php
Normal file
230
app/Console/Commands/MigrateReportsCommand.php
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Enums\ApprovalStatus;
|
||||||
|
use App\Models\Report;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class MigrateReportsCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'migrate:reports';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Migrate reports from legacy database → reports';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for migrated report IDs that already have proof media.
|
||||||
|
*/
|
||||||
|
private array $migratedIds = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache mapping legacy media_order.id → new cooperation_id for fast lookup.
|
||||||
|
*/
|
||||||
|
private array $mediaOrderToCooperation = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$this->info('Starting migration: reports → reports...');
|
||||||
|
|
||||||
|
$legacyConn = DB::connection('mysql_second');
|
||||||
|
$query = $legacyConn->table('reports')->whereNull('deleted_at');
|
||||||
|
|
||||||
|
$totalCount = $query->count();
|
||||||
|
if ($totalCount === 0) {
|
||||||
|
$this->warn('No reports found in legacy database.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Found {$totalCount} legacy report record(s).");
|
||||||
|
|
||||||
|
// Pre-cache report IDs that already have proof media (skip re-importing files)
|
||||||
|
$this->migratedIds = DB::table('media')
|
||||||
|
->where('model_type', Report::class)
|
||||||
|
->where('collection_name', 'reports')
|
||||||
|
->pluck('model_id')
|
||||||
|
->flip()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Build a lookup: legacy media_orders.id → new cooperations.id
|
||||||
|
// via: media_orders.name (lowercased) → cooperations.title (lowercased)
|
||||||
|
$legacyOrders = $legacyConn->table('media_orders')
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->select('id', 'name')
|
||||||
|
->get()
|
||||||
|
->keyBy('id');
|
||||||
|
|
||||||
|
$cooperationMap = DB::table('cooperations')
|
||||||
|
->select('id', 'title')
|
||||||
|
->get()
|
||||||
|
->keyBy(fn ($c) => strtolower(trim($c->title)));
|
||||||
|
|
||||||
|
foreach ($legacyOrders as $order) {
|
||||||
|
$key = strtolower(trim($order->name));
|
||||||
|
if (isset($cooperationMap[$key])) {
|
||||||
|
$this->mediaOrderToCooperation[$order->id] = $cooperationMap[$key]->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->orderBy('id')->chunk(50, function ($chunk) {
|
||||||
|
foreach ($chunk as $legacy) {
|
||||||
|
$this->processReport($legacy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->newLine();
|
||||||
|
$this->info('reports migration completed successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a single legacy report record.
|
||||||
|
*/
|
||||||
|
private function processReport(object $legacy): void
|
||||||
|
{
|
||||||
|
$idText = "[#{$legacy->id}]";
|
||||||
|
$this->output->write("<info>{$idText}</info> Processing: <comment>".Str::limit($legacy->title ?? '', 40).'</comment>... ');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Resolve media_task_assignment_id
|
||||||
|
// Chain: legacy.media_order → cooperation_id → task_assignment → media_task_assignment (by partner_media_id)
|
||||||
|
$cooperationId = $this->mediaOrderToCooperation[$legacy->media_order] ?? null;
|
||||||
|
|
||||||
|
if (! $cooperationId) {
|
||||||
|
$this->output->writeln('<fg=yellow>SKIP (cooperation not found for media_order #'.$legacy->media_order.')</>');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$taskAssignment = DB::table('task_assignments')
|
||||||
|
->where('cooperation_id', $cooperationId)
|
||||||
|
->select('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $taskAssignment) {
|
||||||
|
$this->output->writeln('<fg=yellow>SKIP (task_assignment not found for cooperation #'.$cooperationId.')</>');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mediaTaskAssignment = DB::table('media_task_assignment')
|
||||||
|
->where('task_assignment_id', $taskAssignment->id)
|
||||||
|
->where('partner_media_id', $legacy->media)
|
||||||
|
->select('id')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $mediaTaskAssignment) {
|
||||||
|
$this->output->writeln('<fg=yellow>SKIP (media_task_assignment not found for media #'.$legacy->media.')</>');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the report
|
||||||
|
DB::table('reports')->insert([
|
||||||
|
'media_task_assignment_id' => $mediaTaskAssignment->id,
|
||||||
|
'title' => $legacy->title ?? '',
|
||||||
|
'publication_date' => $legacy->broadcast,
|
||||||
|
'link' => $legacy->link ?? '',
|
||||||
|
'description' => '',
|
||||||
|
'status' => $this->mapApprovalStatus((string) $legacy->status),
|
||||||
|
'created_at' => $legacy->created_at ?? now(),
|
||||||
|
'updated_at' => $legacy->updated_at ?? now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$newReportId = (int) DB::getPdo()->lastInsertId();
|
||||||
|
|
||||||
|
// Migrate proof image if not already done
|
||||||
|
if (! isset($this->migratedIds[$newReportId])) {
|
||||||
|
$report = Report::find($newReportId);
|
||||||
|
if ($report) {
|
||||||
|
$this->migrateProof($report, $legacy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->output->writeln('<info>DONE</info>');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->output->writeln('<error>FAILED: '.$e->getMessage().'</error>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate the proof image file from S3 to the Report media library.
|
||||||
|
*/
|
||||||
|
private function migrateProof(Report $report, object $legacy): void
|
||||||
|
{
|
||||||
|
$legacyPath = trim($legacy->proof ?? '');
|
||||||
|
if (empty($legacyPath) || $legacyPath === '-' || $legacyPath === 'no-image.png') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourcePath = 'old-data/reports/'.$legacyPath;
|
||||||
|
$this->importProofFile($report, $sourcePath, $legacy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single proof image to the Report media library.
|
||||||
|
*/
|
||||||
|
private function importProofFile(Report $report, string $sourcePath, object $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 {
|
||||||
|
$report->addMediaFromDisk($sourcePath, 's3')
|
||||||
|
->withCustomProperties([
|
||||||
|
'feature' => 'reports',
|
||||||
|
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
|
||||||
|
'doc_type' => 'proof',
|
||||||
|
])
|
||||||
|
->preservingOriginal()
|
||||||
|
->toMediaCollection('reports', 's3');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Log quietly or handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map legacy reports.status integer to ApprovalStatus enum value.
|
||||||
|
* Legacy: 0 = menunggu, 1 = menerima, 2 = menolak
|
||||||
|
*/
|
||||||
|
private function mapApprovalStatus(string $legacyStatus): int
|
||||||
|
{
|
||||||
|
return match ($legacyStatus) {
|
||||||
|
'0' => ApprovalStatus::PENDING->value,
|
||||||
|
'1' => ApprovalStatus::ACCEPTED->value,
|
||||||
|
'2' => ApprovalStatus::REJECTED->value,
|
||||||
|
default => ApprovalStatus::PENDING->value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user