feat: implement FixRawMaterialMediaKeys to correct S3 keys and create media records for RawMaterialPrice
This commit is contained in:
parent
ad4c6d47df
commit
0908576ef2
182
app/DataFixes/FixRawMaterialMediaKeys.php
Normal file
182
app/DataFixes/FixRawMaterialMediaKeys.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\DataFixes;
|
||||
|
||||
use App\Models\RawMaterialPrice;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class FixRawMaterialMediaKeys extends BaseDataFix
|
||||
{
|
||||
public function key(): string
|
||||
{
|
||||
return 'raw-material-media-keys';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Fix s3_key & buat media record untuk RawMaterialPrice';
|
||||
}
|
||||
|
||||
public function fix(): array
|
||||
{
|
||||
$disk = Storage::disk('s3');
|
||||
$client = $disk->getClient();
|
||||
$bucket = config('filesystems.disks.s3.bucket');
|
||||
|
||||
$this->info('Loading S3 file list...');
|
||||
$s3Files = [];
|
||||
$continuationToken = null;
|
||||
|
||||
do {
|
||||
$params = [
|
||||
'Bucket' => $bucket,
|
||||
'Prefix' => 'raw-material/images/',
|
||||
];
|
||||
if ($continuationToken) {
|
||||
$params['ContinuationToken'] = $continuationToken;
|
||||
}
|
||||
$result = $client->listObjectsV2($params);
|
||||
foreach ($result['Contents'] ?? [] as $obj) {
|
||||
$key = $obj['Key'];
|
||||
if (str_contains($key, '/conversions/')) {
|
||||
continue;
|
||||
}
|
||||
if (preg_match('#raw-material/images/\d{4}-\d{2}-\d{2}/(\d+)/[^/]+$#', $key, $m)) {
|
||||
$s3Files[(int) $m[1]] = $key;
|
||||
}
|
||||
}
|
||||
$continuationToken = $result['NextContinuationToken'] ?? null;
|
||||
} while ($continuationToken);
|
||||
|
||||
$this->info('Found ' . count($s3Files) . ' files in S3.');
|
||||
|
||||
// Build filename → s3_key map for matching
|
||||
$s3ByKey = [];
|
||||
foreach ($s3Files as $modelId => $s3Key) {
|
||||
$fileName = pathinfo($s3Key, PATHINFO_BASENAME);
|
||||
$s3ByKey[$modelId][$fileName] = $s3Key;
|
||||
}
|
||||
|
||||
$allPrices = RawMaterialPrice::all(['id', 'variant']);
|
||||
|
||||
$existingMediaAll = DB::table('media')
|
||||
->where('model_type', 'App\\Models\\RawMaterialPrice')
|
||||
->where('collection_name', 'images')
|
||||
->get(['id', 'model_id', 'file_name', 'custom_properties']);
|
||||
|
||||
$mediaByModel = [];
|
||||
foreach ($existingMediaAll as $m) {
|
||||
$props = json_decode($m->custom_properties, true);
|
||||
if (isset($props['module']) && $props['module'] === 'owner_verification_request') {
|
||||
continue;
|
||||
}
|
||||
$mediaByModel[$m->model_id][] = $m;
|
||||
}
|
||||
|
||||
$fixed = 0;
|
||||
$fixedPath = 0;
|
||||
$skipped = 0;
|
||||
$noFile = 0;
|
||||
$details = [];
|
||||
|
||||
foreach ($allPrices as $price) {
|
||||
if (isset($mediaByModel[$price->id])) {
|
||||
$allGood = true;
|
||||
|
||||
foreach ($mediaByModel[$price->id] as $media) {
|
||||
$currentKey = data_get(json_decode($media->custom_properties, true), 's3_key');
|
||||
|
||||
if ($currentKey && str_starts_with($currentKey, 'raw-material/images/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Match by filename first, fallback to model_id
|
||||
$correctKey = null;
|
||||
if (isset($s3ByKey[$price->id][$media->file_name])) {
|
||||
$correctKey = $s3ByKey[$price->id][$media->file_name];
|
||||
} elseif (isset($s3Files[$price->id])) {
|
||||
$correctKey = $s3Files[$price->id];
|
||||
}
|
||||
|
||||
if ($correctKey) {
|
||||
DB::table('media')
|
||||
->where('id', $media->id)
|
||||
->update([
|
||||
'custom_properties' => DB::raw("'".addslashes(json_encode(['s3_key' => $correctKey]))."'"),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$details[] = [
|
||||
'Price ID' => $price->id,
|
||||
'Variant' => $price->variant,
|
||||
'Action' => 'FIXED PATH',
|
||||
'S3 Key' => $correctKey,
|
||||
];
|
||||
$fixedPath++;
|
||||
$allGood = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($allGood) {
|
||||
$skipped++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! isset($s3Files[$price->id])) {
|
||||
$noFile++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$s3Key = $s3Files[$price->id];
|
||||
$fileName = pathinfo($s3Key, PATHINFO_BASENAME);
|
||||
|
||||
DB::table('media')->insert([
|
||||
'model_type' => 'App\\Models\\RawMaterialPrice',
|
||||
'model_id' => $price->id,
|
||||
'uuid' => Str::uuid()->toString(),
|
||||
'collection_name' => 'images',
|
||||
'name' => pathinfo($s3Key, PATHINFO_FILENAME),
|
||||
'file_name' => $fileName,
|
||||
'mime_type' => 'image/jpeg',
|
||||
'disk' => 's3',
|
||||
'conversions_disk' => 's3',
|
||||
'size' => 0,
|
||||
'manipulations' => '[]',
|
||||
'custom_properties' => json_encode(['s3_key' => $s3Key]),
|
||||
'generated_conversions' => '[]',
|
||||
'responsive_images' => '[]',
|
||||
'order_column' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$details[] = [
|
||||
'Price ID' => $price->id,
|
||||
'Variant' => $price->variant,
|
||||
'Action' => 'CREATED',
|
||||
'S3 Key' => $s3Key,
|
||||
];
|
||||
$fixed++;
|
||||
}
|
||||
|
||||
if (! empty($details)) {
|
||||
$this->command->newLine();
|
||||
$this->command->table(['Price ID', 'Variant', 'Action', 'S3 Key'], $details);
|
||||
}
|
||||
|
||||
$this->info("Created new media: {$fixed}");
|
||||
$this->info("Fixed s3_key path: {$fixedPath}");
|
||||
$this->info("Skipped (already correct): {$skipped}");
|
||||
$this->info("Skipped (no file in S3): {$noFile}");
|
||||
|
||||
return [
|
||||
'fixed' => $fixed + $fixedPath,
|
||||
'skipped' => $skipped,
|
||||
'total' => $allPrices->count(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user