Saving multiple images in laravel

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Mastering Multiple Image Uploads in Laravel: A Deep Dive into File Handling

Uploading multiple files is a common requirement in web applications, whether you are handling user profiles, product galleries, or content management systems. As developers working with Laravel, we frequently run into hurdles when trying to manage file uploads in bulk. The struggle you described—successfully moving files to the local disk but failing to persist the association in the database—is a classic pitfall.

This post will walk you through the correct, robust way to handle multiple image uploads in Laravel, ensuring that your file storage and database relationships are perfectly synchronized. We will fix the issue in your controller logic and implement best practices for handling file storage.

The Pitfall: Why Single Assignments Fail for Multiple Files

The core issue in your provided code lies in how you are attempting to map multiple uploaded files to a single database field. When you use foreach inside your file check, you are iterating over the files but only assigning the last processed filename to $content->image. Furthermore, storing multiple images directly in a single string column is generally poor database design; this screams for a relationship structure.

When dealing with multiple related items (one content item and many images), the correct approach involves using Eloquent relationships and saving each file as a separate record linked to the main model. This aligns perfectly with the principles of clean architecture championed by frameworks like Laravel, which emphasizes clear Model-View-Controller separation.

The Solution: Implementing Proper File Handling

To correctly save multiple images, we need to iterate through the uploaded files and create a new record for each image, associating it with the parent content item. This requires moving away from saving a single string path in the content table and instead creating a separate image or content_image table.

Step 1: Adjusting the Form Input

Your HTML form setup is actually correct for sending multiple files:

<input type="file" name="image[]" multiple="multiple">

The name="image[]" and multiple="multiple" ensure that the browser sends an array of files under the key image.

Step 2: The Corrected Controller Logic

Instead of trying to assign files to a single field, we will iterate through the request files and save each one individually. This requires assumptions about your database structure (e.g., you have a content table and an images table).

Here is how you would restructure your controller method:

use Illuminate\Http\Request;
use App\Models\Content; // Assuming you have a Content model
use App\Models\Image;   // Assuming you have an Image model

public function store(Request $request)
{
    $validated = $request->validate([
        'title' => 'required|string',
        'image' => 'required|array', // Expecting an array of files
    ]);

    // 1. Create the main content record first (if necessary)
    $content = new Content();
    $content->title = $request->input('title');
    $content->save();

    // 2. Handle multiple file uploads
    if ($request->hasFile('image')) {
        foreach ($request->file('image') as $imageFile) {
            // Define the destination path dynamically
            $destinationPath = 'public/uploads/'; 
            
            // Generate a unique filename to prevent collisions
            $filename = time() . '_' . $imageFile->getClientOriginalName();
            
            // Move the file to storage
            $imageFile->move($destinationPath, $filename);

            // 3. Save the image record to the database
            Image::create([
                'content_id' => $content->id,
                'path' => $filename, // Store the unique filename
                'filename' => $filename,
            ]);
        }
    }

    return redirect()->route('content.index')->with('success', 'Content and images saved successfully!');
}

Notice the critical change: we iterate over $request->file('image'), move each file individually, and then use a separate Eloquent model (Image) to create a relationship record linked to the main $content ID. This approach is far more scalable and adheres to the principles of data integrity expected in robust systems like those built on Laravel.

Best Practices: Leveraging Laravel Storage

While the above example uses the standard move() function for local storage, as a senior developer, I strongly advise using Laravel's built-in Filesystem abstraction. This allows you to easily switch between local disk, S3, or other cloud storage providers without rewriting your core file handling logic. For instance, when dealing with large media files, leveraging the storage system provided by Laravel is essential for scalability and reliability, which is a key focus of the development philosophy at https://laravelcompany.com.

Conclusion

Saving multiple images effectively requires treating each uploaded file as an independent entity that needs its own record in the database. By moving away from trying to cram all files into a single string field and instead utilizing loops with dedicated models, you achieve data integrity and scalability. Embrace Eloquent relationships, use Laravel's Filesystem helper, and you will build applications that are not only functional but also maintainable and robust.