refactor: streamline media import process across migration commands by replacing S3 checks with direct database inserts and enhancing mime type handling

This commit is contained in:
Yoga Pangestu 2026-04-22 11:10:09 +07:00
parent 198e936798
commit ab96ca6278
10 changed files with 475 additions and 397 deletions

View File

@ -13,8 +13,6 @@
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
{
@ -94,7 +92,7 @@ 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>... ');
$this->output->write("{$idOutput} Processing: <comment>".str()->limit($legacy->name, 40).'</comment>... ');
try {
$legacyConn = DB::connection('mysql_second');
@ -124,13 +122,13 @@ private function processCompany($legacy): void
'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,
@ -240,13 +238,13 @@ private function handlePartnerMedia($legacy): void
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_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',
];
@ -264,40 +262,37 @@ private function migrateAllDocuments(Company $company, $legacy): void
}
/**
* Import a single document to Media Library.
* Import a single document to Media Library (insert record only, no file copy).
*/
private function importOneDocument(Company $company, string $sourcePath, string $docType, $legacy): void
{
$attempts = 0;
$maxAttempts = 3;
$exists = false;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath); // e.g. old-data/companies/subdir
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
}
DB::table('media')->insert([
'model_type' => Company::class,
'model_id' => $company->id,
'uuid' => (string) str()->uuid(),
'collection_name' => 'companies',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'companies',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'doc_type' => $docType,
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
/**
@ -324,40 +319,49 @@ private function migrateMediaDocuments(PartnerMedia $media, $legacyMedia): void
}
/**
* Import a single media document to Media Library.
* Import a single media document to Media Library (insert record only, no file copy).
*/
private function importOneMediaDocument(PartnerMedia $media, string $sourcePath, string $docType, $legacyMedia): void
{
$attempts = 0;
$maxAttempts = 3;
$exists = false;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath); // e.g. old-data/media/subdir
while ($attempts < $maxAttempts) {
try {
$exists = Storage::disk('s3')->exists($sourcePath);
break;
} catch (\Exception $e) {
$attempts++;
usleep(200000);
}
}
DB::table('media')->insert([
'model_type' => PartnerMedia::class,
'model_id' => $media->id,
'uuid' => (string) str()->uuid(),
'collection_name' => 'partnerMedia',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'media',
'date' => Carbon::parse($legacyMedia->created_at ?? now())->toDateString(),
'doc_type' => $docType,
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
if (! $exists) {
return;
}
try {
$media->addMediaFromDisk($sourcePath, 's3')
->withCustomProperties([
'feature' => 'media',
'date' => Carbon::parse($legacyMedia->created_at ?? now())->toDateString(),
'doc_type' => $docType, // Match MediaSaveService slug pattern
])
->preservingOriginal()
->toMediaCollection('partnerMedia', 's3');
} catch (\Exception $e) {
// Log quietly or handle error
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
// ... mapping methods (mapVerificationStatus, mapDecision, mapMediaType, mapMediaClassification, extractMediaLink) remain as previously defined
@ -408,7 +412,7 @@ private function extractMediaLink(?string $link): ?string
}
preg_match_all('/(https?:\/\/[^\s]+)/i', $link, $matches);
if (empty($matches[0])) {
return Str::limit($link, 255, '');
return str()->limit($link, 255, '');
}
foreach ($matches[0] as $url) {
$url = rtrim($url, ',');
@ -416,9 +420,9 @@ private function extractMediaLink(?string $link): ?string
continue;
}
return Str::limit($url, 255, '');
return str()->limit($url, 255, '');
}
return Str::limit($matches[0][0], 255, '');
return str()->limit($matches[0][0], 255, '');
}
}

View File

@ -10,7 +10,6 @@
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
@ -165,40 +164,45 @@ private function migrateMedia(ContentRecap $recap, $legacy): void
{
$filename = trim(basename($legacy->image));
$sourcePath = 'old-data/content-recaps/'.$filename;
$legacyDir = dirname($sourcePath);
$attempts = 0;
$maxAttempts = 3;
$exists = false;
DB::table('media')->insert([
'model_type' => ContentRecap::class,
'model_id' => $recap->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'content-recaps', // Match typo in ContentRecapForm
'name' => pathinfo($filename, PATHINFO_FILENAME),
'file_name' => $filename,
'mime_type' => $this->guessMime($filename),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'content-recaps',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
while ($attempts < $maxAttempts) {
try {
$exists = Storage::disk('s3')->exists($sourcePath);
break;
} catch (\Exception $e) {
$attempts++;
usleep(200000);
}
}
$this->output->writeln('<info>DONE + IMG</info>');
}
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 guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
private function mapContentType(?string $legacyType): ?int

View File

@ -6,7 +6,6 @@
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
@ -143,40 +142,46 @@ private function migrateJournalistDocuments(Journalist $journalist, $legacy): vo
}
}
/**
* Import a single document to Media Library.
*/
private function importOneDocument(Journalist $journalist, string $sourcePath, string $docType, $legacy): void
{
$attempts = 0;
$maxAttempts = 3;
$exists = false;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath); // e.g. old-data/companies/subdir
while ($attempts < $maxAttempts) {
try {
$exists = Storage::disk('s3')->exists($sourcePath);
break;
} catch (\Exception $e) {
$attempts++;
usleep(200000);
}
}
DB::table('media')->insert([
'model_type' => Journalist::class,
'model_id' => $journalist->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'journalists',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'journalists',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'doc_type' => $docType,
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
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
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
}

View File

@ -11,7 +11,6 @@
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
@ -279,23 +278,44 @@ private function importOfferFile(int $proposalId, object $legacy): void
// Adjust path based on where these files are stored in S3
$sourcePath = 'old-data/proposals/'.$legacyPath;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath);
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
}
DB::table('media')->insert([
'model_type' => CooperationProposal::class,
'model_id' => $proposalId,
'uuid' => (string) Str::uuid(),
'collection_name' => 'proposals',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'proposals',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
private function mapApprovalStatus(string $legacyStatus): int

View File

@ -8,7 +8,6 @@
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
@ -166,48 +165,45 @@ private function migrateMedia(MediaMonitoring $monitoring, $legacy): void
{
$filename = trim(basename($legacy->image));
$sourcePath = 'old-data/mediamonitorings/'.$filename;
$legacyDir = dirname($sourcePath);
$exists = false;
$attempts = 0;
$maxAttempts = 3;
DB::table('media')->insert([
'model_type' => MediaMonitoring::class,
'model_id' => $monitoring->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'media-monitorings',
'name' => pathinfo($filename, PATHINFO_FILENAME),
'file_name' => $filename,
'mime_type' => $this->guessMime($filename),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'media-monitorings',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
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>');
$this->output->writeln('<info>DONE + IMG</info>');
}
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>');
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
/**

View File

@ -10,7 +10,6 @@
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
@ -278,36 +277,33 @@ private function migrateOfferFiles(Cooperation $cooperation, Collection $group):
*/
private function importOfferFile(Cooperation $cooperation, string $sourcePath, object $item): void
{
$attempts = 0;
$maxAttempts = 3;
$exists = false;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath);
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
}
DB::table('media')->insert([
'model_type' => Cooperation::class,
'model_id' => $cooperation->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'cooperations',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'cooperations',
'date' => Carbon::parse($item->created_at ?? now())->toDateString(),
'doc_type' => 'proposal-template',
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
private function insertMediaTaskAssignment(
@ -470,37 +466,45 @@ private function migrateProof(Report $report, object $legacy): void
}
$sourcePath = 'old-data/reports/'.$legacyPath;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath);
$attempts = 0;
$maxAttempts = 3;
$exists = false;
DB::table('media')->insert([
'model_type' => Report::class,
'model_id' => $report->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'reports',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'reports',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'doc_type' => 'proof',
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
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
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
/**

View File

@ -7,7 +7,6 @@
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class MigrateNewsCommand extends Command
@ -131,47 +130,44 @@ private function migrateMedia(News $news, $legacy): void
{
$filename = trim(basename($legacy->image));
$sourcePath = 'old-data/news/'.$filename;
$legacyDir = dirname($sourcePath);
$exists = false;
$attempts = 0;
$maxAttempts = 3;
DB::table('media')->insert([
'model_type' => News::class,
'model_id' => $news->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'news',
'name' => pathinfo($filename, PATHINFO_FILENAME),
'file_name' => $filename,
'mime_type' => $this->guessMime($filename),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'news',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
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): ".$actualError.'</error>');
$this->output->writeln('<info>DONE + IMG</info>');
}
return;
}
usleep(200000);
}
}
if (! $exists) {
$this->output->writeln('<error>IMAGE MISSING</error>');
return;
}
try {
$date = Carbon::parse($legacy->created_at ?? now())->toDateString();
$news->addMediaFromDisk($sourcePath, 's3')
->withCustomProperties([
'feature' => 'news',
'date' => $date,
])
->preservingOriginal()
->toMediaCollection('news', 's3');
$this->output->writeln('<info>DONE + IMG</info>');
} catch (\Exception $e) {
$this->output->writeln("<error>FAILED: {$e->getMessage()}</error>");
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
}

View File

@ -7,7 +7,6 @@
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
@ -182,36 +181,45 @@ private function migrateProof(Report $report, object $legacy): void
*/
private function importProofFile(Report $report, string $sourcePath, object $legacy): void
{
$attempts = 0;
$maxAttempts = 3;
$exists = false;
$fileName = basename($sourcePath);
$legacyDir = dirname($sourcePath);
while ($attempts < $maxAttempts) {
try {
$exists = Storage::disk('s3')->exists($sourcePath);
break;
} catch (\Exception $e) {
$attempts++;
usleep(200000);
}
}
DB::table('media')->insert([
'model_type' => Report::class,
'model_id' => $report->id,
'uuid' => (string) Str::uuid(),
'collection_name' => 'reports',
'name' => pathinfo($fileName, PATHINFO_FILENAME),
'file_name' => $fileName,
'mime_type' => $this->guessMime($fileName),
'disk' => 's3',
'conversions_disk' => 's3',
'size' => 0,
'manipulations' => '[]',
'custom_properties' => json_encode([
'feature' => 'reports',
'date' => Carbon::parse($legacy->created_at ?? now())->toDateString(),
'doc_type' => 'proof',
'legacy_dir' => $legacyDir,
]),
'generated_conversions' => '[]',
'responsive_images' => '[]',
'order_column' => 1,
'created_at' => now(),
'updated_at' => now(),
]);
}
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
}
private function guessMime(string $fileName): string
{
return match (strtolower(pathinfo($fileName, PATHINFO_EXTENSION))) {
'pdf' => 'application/pdf',
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
default => 'application/octet-stream',
};
}
/**

View File

@ -92,46 +92,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.director_nik')
->hiddenLabel(),
TextEntry::make('company.director_nik_docs')
->hiddenLabel()
->html()
->getStateUsing(function (User $record): string {
$media = $record->company
?->getMedia('companies')
->where('custom_properties.doc_type', 'director-nik')
->sortByDesc('created_at')
->first();
if (! $media) {
return '';
}
$url = $media->getUrl();
$mime = $media->mime_type;
$downloadLink = '<a href="'.$url.'" download target="_blank" class="mt-2 flex items-center gap-1 text-sm font-medium text-primary-600 transition hover:text-primary-500">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
Unduh Dokumen
</a>';
if (str_contains($mime, 'image')) {
return '<div class="flex flex-col">'.
'<img src="'.$url.'" alt="Dokumen NIK Direktur" style="max-width: 100%; height: auto; border-radius: 0.5rem;" />'.
$downloadLink.
'</div>';
}
if (str_contains($mime, 'pdf')) {
return '<div class="flex flex-col">'.
'<iframe src="'.$url.'" style="width: 100%; height: 500px; border: none; border-radius: 0.5rem;"></iframe>'.
$downloadLink.
'</div>';
}
return $downloadLink;
}),
...self::fileEntries('company.director_nik'),
]),
Section::make('Akta Pendirian')
@ -139,7 +100,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.deed_incorporation')
->hiddenLabel(),
self::fileEntry('company.deed_incorporation'),
...self::fileEntries('company.tax_id_number'),
]),
Section::make('SIUP / NIB')
@ -147,7 +108,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.trade_license')
->hiddenLabel(),
self::fileEntry('company.trade_license'),
...self::fileEntries('company.trade_license'),
]),
Section::make('NPWP')
@ -155,7 +116,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.tax_id_number')
->hiddenLabel(),
self::fileEntry('company.tax_id_number'),
...self::fileEntries('company.tax_id_number'),
]),
Section::make('PKP')
@ -163,7 +124,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.taxable_enterprise')
->hiddenLabel(),
self::fileEntry('company.taxable_enterprise'),
...self::fileEntries('company.taxable_enterprise'),
]),
Section::make('SPT Tahunan')
@ -171,7 +132,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.annual_tax_return')
->hiddenLabel(),
self::fileEntry('company.annual_tax_return'),
...self::fileEntries('company.annual_tax_return'),
]),
Section::make('Suket Domisili')
@ -179,7 +140,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.domicile_certificate')
->hiddenLabel(),
self::fileEntry('company.domicile_certificate'),
...self::fileEntries('company.domicile_certificate'),
]),
Section::make('Profil Perusahaan')
@ -187,7 +148,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.profile')
->hiddenLabel(),
self::fileEntry('company.profile'),
...self::fileEntries('company.profile'),
]),
]),
]),
@ -224,7 +185,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.partnerMedia.journalism_organization')
->hiddenLabel(),
self::fileEntry('company.partnerMedia.journalism_organization', 'company.partnerMedia', 'partnerMedia'),
...self::fileEntries('company.partnerMedia.journalism_organization', 'company.partnerMedia', 'partnerMedia'),
]),
Section::make('Sertifikat Dewan Pers')
@ -232,7 +193,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('company.partnerMedia.press_council_certificate')
->hiddenLabel(),
self::fileEntry('company.partnerMedia.press_council_certificate', 'company.partnerMedia', 'partnerMedia'),
...self::fileEntries('company.partnerMedia.press_council_certificate', 'company.partnerMedia', 'partnerMedia'),
]),
]),
]),
@ -261,7 +222,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('press_card')
->hiddenLabel(),
self::fileEntry('press_card', null, 'journalists'),
...self::fileEntries('press_card', null, 'journalists'),
]),
Section::make('Sertifikat UKW')
@ -269,7 +230,7 @@ public static function configure(Schema $schema): Schema
TextEntry::make('ukw_certificate')
->hiddenLabel(),
self::fileEntry('ukw_certificate', null, 'journalists'),
...self::fileEntries('ukw_certificate', null, 'journalists'),
]),
]),
]),
@ -278,29 +239,103 @@ public static function configure(Schema $schema): Schema
->columns(1);
}
private static function fileEntry(
private static function fileEntries(
string $name,
?string $mediaOwner = 'company',
string $collection = 'companies'
): PdfViewerEntry {
return PdfViewerEntry::make($name)
->hiddenLabel()
->getStateUsing(function (Model $record) use ($name, $mediaOwner, $collection): ?string {
$owner = $mediaOwner ? data_get($record, $mediaOwner) : $record;
): array {
return [
PdfViewerEntry::make($name.'_pdf')
->hiddenLabel()
->visible(
fn (Model $record) => self::isPdf($record, $name, $mediaOwner, $collection)
)
->getStateUsing(
fn (Model $record) => self::getFileUrl($record, $name, $mediaOwner, $collection)
),
if (! $owner) {
return null;
}
TextEntry::make($name.'_image')
->hiddenLabel()
->html()
->visible(
fn (Model $record) => self::isImage($record, $name, $mediaOwner, $collection)
)
->getStateUsing(function (Model $record) use ($name, $mediaOwner, $collection) {
$media = self::getMedia($record, $name, $mediaOwner, $collection);
$url = $media?->getUrl();
$docType = str($name)
->afterLast('.')
->slug('-');
if (! $url) {
return '';
}
return $owner->getMedia($collection)
->where('custom_properties.doc_type', (string) $docType)
->sortByDesc('created_at')
->first()
?->getUrl();
});
$downloadUrl = $url;
if ($media && in_array($media->disk, ['s3', 'minio'])) {
$downloadUrl = $media->getTemporaryUrl(now()->addMinutes(30), '', [
'ResponseContentDisposition' => 'attachment; filename="'.$media->file_name.'"',
]);
}
$downloadLink = '<a href="'.$downloadUrl.'" download="dokumen" class="mt-2 flex items-center gap-1 text-sm font-medium text-primary-600 transition hover:text-primary-500">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
Unduh Dokumen
</a>';
return '<div class="flex flex-col">'.
'<img src="'.$url.'" alt="Dokumen" style="max-width: 100%; height: auto; border-radius: 0.5rem;" />'.
$downloadLink.
'</div>';
}),
];
}
private static function getMedia(
Model $record,
string $name,
?string $mediaOwner,
string $collection
) {
$owner = $mediaOwner ? data_get($record, $mediaOwner) : $record;
if (! $owner) {
return null;
}
$docType = str($name)->afterLast('.')->slug('-');
return $owner->getMedia($collection)
->where('custom_properties.doc_type', (string) $docType)
->sortByDesc('created_at')
->first();
}
private static function getFileUrl(
Model $record,
string $name,
?string $mediaOwner,
string $collection
): ?string {
return self::getMedia($record, $name, $mediaOwner, $collection)?->getUrl();
}
private static function getMime(
Model $record,
string $name,
?string $mediaOwner,
string $collection
): ?string {
return self::getMedia($record, $name, $mediaOwner, $collection)?->mime_type;
}
private static function isPdf(...$args): bool
{
return str_contains(self::getMime(...$args) ?? '', 'pdf');
}
private static function isImage(...$args): bool
{
return str_contains(self::getMime(...$args) ?? '', 'image');
}
}

View File

@ -9,6 +9,12 @@ class CustomPathGenerator implements PathGenerator
{
public function getPath(Media $media): string
{
// Data lama: arahkan ke path legacy di S3
$legacyDir = $media->getCustomProperty('legacy_dir');
if ($legacyDir) {
return rtrim($legacyDir, '/').'/';
}
$feature = $media->getCustomProperty('feature', 'misc');
$date = $media->getCustomProperty('date', $media->created_at?->toDateString() ?? now()->toDateString());
$docType = $media->getCustomProperty('doc_type');