Adding multi-image uploads to a Laravel CRUD looks simple but quickly turns hairy when you add edit-mode handling, reordering, and deletion. Here's the clean, production-ready pattern we use on every project at Dovio.
The Data Model
Two tables: a parent (e.g. blogs) and a child blog_images with FK + sort_order.
Schema::create('blog_images', function (Blueprint $table) {
$table->id();
$table->foreignId('blog_id')->constrained()->cascadeOnDelete();
$table->string('image_path');
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
});
The Model
class Blog extends Model {
public function images() {
return $this->hasMany(BlogImage::class)->orderBy('sort_order');
}
}
The Controller (Store)
if ($request->hasFile('images')) {
foreach ($request->file('images') as $i => $file) {
$path = $file->store('blogs/gallery', 'public');
BlogImage::create([
'blog_id' => $blog->id,
'image_path' => $path,
'sort_order' => $i,
]);
}
}
The Controller (Update — Append + Remove)
In edit mode you need to handle three cases: keeping existing, removing some, adding new.
ALSO READ|Building Fast Laravel APIs in 2026: Complete GuideThe Blade Form
Standard <input type="file" name="images[]" multiple> for adding, then a list of existing images with checkbox-marked deletion.
Cleanup Hook
Add a deleting event on the parent model to remove files from disk on force-delete.