feat: remove unused media collection clearing in destroy methods and add CleanupOrphanedMediaJob for orphaned media management
This commit is contained in:
parent
e1a0d25c3c
commit
1f41f04199
96
app/Jobs/CleanupOrphanedMediaJob.php
Normal file
96
app/Jobs/CleanupOrphanedMediaJob.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
class CleanupOrphanedMediaJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int $tries = 3;
|
||||
|
||||
public int $chunkSize = 100;
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$orphanedMedia = $this->getOrphanedMedia();
|
||||
|
||||
if ($orphanedMedia->isEmpty()) {
|
||||
Log::info('CleanupOrphanedMediaJob: Tidak ada media orphaned.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$totalDeleted = 0;
|
||||
|
||||
$orphanedMedia->each(function (Media $media) use (&$totalDeleted) {
|
||||
$this->deleteMedia($media);
|
||||
$totalDeleted++;
|
||||
});
|
||||
|
||||
Log::info("CleanupOrphanedMediaJob: Berhasil hapus {$totalDeleted} media orphaned.");
|
||||
}
|
||||
|
||||
private function getOrphanedMedia()
|
||||
{
|
||||
$allMedia = Media::select(['id', 'model_type', 'model_id', 'file_name', 'custom_properties', 'disk'])
|
||||
->get()
|
||||
->groupBy(fn (Media $m) => $m->model_type.'|'.$m->model_id);
|
||||
|
||||
$orphanedIds = [];
|
||||
|
||||
foreach ($allMedia as $key => $mediaItems) {
|
||||
[$modelType, $modelId] = explode('|', $key);
|
||||
|
||||
if ($this->isOrphaned($modelType, (int) $modelId)) {
|
||||
foreach ($mediaItems as $media) {
|
||||
$orphanedIds[] = $media->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($orphanedIds)) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return Media::whereIn('id', $orphanedIds)->get();
|
||||
}
|
||||
|
||||
private function isOrphaned(string $modelType, int $modelId): bool
|
||||
{
|
||||
if (! class_exists($modelType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$model = $modelType::withTrashed()->find($modelId);
|
||||
|
||||
if (! $model) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (method_exists($model, 'trashed') && $model->trashed()) {
|
||||
return $model->deleted_at->lt(now()->subMonth());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function deleteMedia(Media $media): void
|
||||
{
|
||||
$s3Key = $media->getCustomProperty('s3_key');
|
||||
|
||||
if ($s3Key && Storage::disk($media->disk)->exists($s3Key)) {
|
||||
Storage::disk($media->disk)->delete($s3Key);
|
||||
}
|
||||
|
||||
$media->forceDelete();
|
||||
}
|
||||
}
|
||||
@ -168,8 +168,6 @@ public function destroy(CashTransaction $transaction): bool
|
||||
Cache::forget("cash_transaction_receipt_{$media->id}");
|
||||
}
|
||||
|
||||
$transaction->clearMediaCollection('photos');
|
||||
|
||||
$deleted = $transaction->delete();
|
||||
|
||||
if ($deleted) {
|
||||
|
||||
@ -130,8 +130,6 @@ public function destroy(Expense $expense): bool
|
||||
Cache::forget("expense_receipt_{$media->id}");
|
||||
}
|
||||
|
||||
$expense->clearMediaCollection('photos');
|
||||
|
||||
$deleted = $expense->delete();
|
||||
|
||||
if ($deleted) {
|
||||
|
||||
@ -357,7 +357,6 @@ public function destroy(Cutting $cutting): bool
|
||||
}
|
||||
}
|
||||
|
||||
$cutting->clearMediaCollection('images');
|
||||
$cutting->cuttingResults()->delete();
|
||||
$cutting->cuttingMaterials()->delete();
|
||||
$cutting->cuttingMaterialCombinations()->delete();
|
||||
|
||||
@ -480,7 +480,6 @@ public function destroy(Purchase $purchase): bool
|
||||
}
|
||||
});
|
||||
|
||||
$purchase->clearMediaCollection('photos');
|
||||
$purchase->purchaseItems()->delete();
|
||||
$purchase->delete();
|
||||
|
||||
|
||||
@ -144,7 +144,6 @@ public function destroy(Restock $restock): bool
|
||||
});
|
||||
|
||||
$restock->restockItems()->delete();
|
||||
$restock->clearMediaCollection('photos');
|
||||
$restock->delete();
|
||||
|
||||
return true;
|
||||
|
||||
@ -299,7 +299,6 @@ public function destroy(Order $order): bool
|
||||
});
|
||||
|
||||
$order->orderItems()->delete();
|
||||
$order->clearMediaCollection('photos');
|
||||
$order->delete();
|
||||
|
||||
return true;
|
||||
|
||||
@ -404,7 +404,6 @@ public function destroy(Product $product): bool
|
||||
$result = DB::transaction(function () use ($product) {
|
||||
$product->productVariants->each(function (ProductVariant $variant) {
|
||||
$variant->productPrices()->delete();
|
||||
$variant->clearMediaCollection('images');
|
||||
$variant->delete();
|
||||
});
|
||||
|
||||
|
||||
@ -156,7 +156,6 @@ public function destroy(Product $product, ProductVariant $variant): bool
|
||||
|
||||
$result = DB::transaction(function () use ($variant) {
|
||||
$variant->productPrices()->delete();
|
||||
$variant->clearMediaCollection('images');
|
||||
|
||||
return $variant->delete();
|
||||
});
|
||||
|
||||
@ -213,7 +213,6 @@ public function destroy(RawMaterial $rawMaterial): bool
|
||||
{
|
||||
return DB::transaction(function () use ($rawMaterial) {
|
||||
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) {
|
||||
$price->clearMediaCollection('images');
|
||||
$price->delete();
|
||||
});
|
||||
|
||||
|
||||
@ -93,8 +93,6 @@ public function update(RawMaterialPrice $variant, array $data): RawMaterialPrice
|
||||
public function destroy(RawMaterial $rawMaterial, RawMaterialPrice $variant): bool
|
||||
{
|
||||
$result = DB::transaction(function () use ($variant) {
|
||||
$variant->clearMediaCollection('images');
|
||||
|
||||
return $variant->delete();
|
||||
});
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
use App\Console\Commands\GeneratePayrollCommand;
|
||||
use App\Jobs\CheckAttendancePenaltiesJob;
|
||||
use App\Jobs\CleanupOrphanedMediaJob;
|
||||
use Illuminate\Support\Facades\Schedule;
|
||||
|
||||
Schedule::command(GeneratePayrollCommand::class)->monthlyOn(1, '00:00');
|
||||
Schedule::job(new CheckAttendancePenaltiesJob)->dailyAt('00:00');
|
||||
Schedule::job(new CleanupOrphanedMediaJob)->monthlyOn(1, '01:00');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user