Administration & trash

Soft delete, restore, hard delete

Call Row Files
$upload->delete() deleted_at set; gone from uploads(), the route (404) and the statistics kept
$upload->restore() back untouched
$upload->forceDelete() removed the original, every conversion and every page are removed from the disk

The Livewire upload manager and the overview use delete(); only the trash view of the overview offers “Delete for good”. Deleting the owning model does not touch its uploads: add it to the model’s deleting event when you want that, with $product->uploads()->get()->each->delete() (trash) or ->each->forceDelete() (gone).

Pruning the trash

php artisan uploads:prune            # older than prune_after_days (30)
php artisan uploads:prune --days=7

The command force deletes, one upload at a time so the files are removed too, everything that has been in the trash longer than the given days. Schedule it:

// routes/console.php
use Illuminate\Support\Facades\Schedule;

Schedule::command('uploads:prune')->daily();

The model also implements Laravel’s Prunable with the same rule, so php artisan model:prune --model="Darvis\Uploads\Models\Upload" does the same thing.

Statistics

Darvis\Uploads\Support\UploadStatistics gives the numbers for a dashboard:

$statistics = app(UploadStatistics::class);

$statistics->total();          // ['files' => 120, 'bytes' => 734003200]
$statistics->trashed();        // the same for the trash
$statistics->perDisk();        // [['disk' => 'public', 'files' => 80, 'bytes' => …], …]
$statistics->perCollection();  // [['collection' => 'photos', 'files' => 70, 'bytes' => …], …]

The trash is left out of total(), perDisk() and perCollection(). Format the bytes with Illuminate\Support\Number::fileSize($bytes, precision: 1), as the overview does.

Your own overview

The overview component is a starting point; a project with its own table component only needs the query:

Upload::query()
    ->with(['uploadable', 'user'])
    ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
    ->when($collection, fn ($q) => $q->collection($collection))
    ->latest('id')
    ->paginate(25);

$upload->uploadable is the owning model, so class_basename($upload->uploadable) and $upload->uploadable->getKey() tell what the file belongs to. Eager load it, or every row runs a query.