How to Upload Multiple Images in Laravel in the best way?

Application development often requires to upload multiple images at the same time. This feature is supported by Laravel applications. In the previous tutorials, we discussed how to upload single image in Laravel. So, let us now look at a step by step example to upload multiple images in Laravel.

Step 1: Install and Create new Project.

Firstly, we will install Laravel 9 and create a new project. We will have a simple form with a file input field. You can use it to select multiple images. Prior to submitting the form we will store those images in a folder and database.

composer create-project laravel/laravel upload_multiple_images

Step 2: Create new Migrations

Secondly, to store multiple images and their paths in a single place, we will need a separate table to handle it. Therefore, we will create migrations for a new images table.

php artisan make:migration create_new_images_table

The resulting migrations file will look as shown below.

  public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
    }
  
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('images');
    }
};

The details above have the up() function to create a new table ‘images’ with the three fields specified in the create function. The down() function drops the ‘images’ table, if it exists in database. After adding the details as above, we will run the migrations.

php artisan migrate

Step 3: Create new Modal

Next step, we create an Images Modal.

php artisan make:model Images

The Modal will come under the app/Models directory and has the code as shown below.

<?php
  
namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
  
class Images extends Model
{
    use HasFactory;
  
    protected $fillable = [
        'name'
    ];
}

Step 4: Create new Routes

New routes in the routes/web.php file are created to link the Controller functions and blade views.

Route::controller(ImagesController::class)->group(function(){
    Route::get('images-upload', 'index');
    Route::post('images-upload', 'save')->name('images.store');
});

Step 5: Create new Controller

Now we create a controller that handles retrieving of images and uploading to the database.

php artisan make:controller ImagesController

We will then populate the Controller file with the code as shown below.

<?php
  
namespace App\Http\Controllers;
  
use Illuminate\Http\Request;
use App\Models\Image;
  
class ImagesController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('imagesUpload');
    }
      
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function save(Request $request)
    {
        $request->validate([
            'images' => 'required',
            'images.*' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);
      
        $images = [];
        if ($request->images){
            foreach($request->images as $key => $image)
            {
                $imageName = time().rand(1,99).'.'.$image->extension();  
                $image->move(public_path('images'), $imageName);
  
                $images[]['name'] = $imageName;
            }
        }
  
        foreach ($images as $key => $image) {
            Image::create($image);
        }
      
        return back()
                ->with('success','You have successfully upload image.')
                ->with('images', $images); 
    }
}

The index() function returns the view where a user can upload images. Image data from the view is processed in the save function. After validation, the image array processes the multiple images using a foreach loop. Therefore, each image is moved to the images location. For example, this can be the public path or an image storage folder. Locations of both paths are stated in upload single image tutorial. Consequently, a success message is returned if all steps go smoothly.

Step 6: Create new Blade file

Finally, the last step will be to create a blade file. This will make a user view the multi-image select and upload option.

<!DOCTYPE html>
<html>
<head>
    <title>Laravel 9 Upload  Multiple Images Example - scratchcoding.dev</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
      
<body>
<div class="container">
       
    <div class="panel panel-primary">
  
      <div class="panel-heading">
        <h2>Laravel 9 Upload  Multiple Images Example - scratchcoding.dev</h2>
      </div>
  
      <div class="panel-body">
       
        @if ($message = Session::get('success'))
            <div class="alert alert-success alert-dismissible fade show" role="alert">
              <strong>{{ $message }}</strong>
              <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            </div>
  
            @foreach(Session::get('images') as $image)
                <img src="images/{{ $image['name'] }}" width="300px">
            @endforeach
        @endif
      
        <form action="{{ route('images.store') }}" method="POST" enctype="multipart/form-data">
            @csrf
  
            <div class="mb-3">
                <label class="form-label" for="inputImage">Select Images:</label>
                <input 
                    type="file" 
                    name="images[]" 
                    id="inputImage"
                    multiple 
                    class="form-control @error('images') is-invalid @enderror">
  
                @error('images')
                    <span class="text-danger">{{ $message }}</span>
                @enderror
            </div>
   
            <div class="mb-3">
                <button type="submit" class="btn btn-success">Upload</button>
            </div>
       
        </form>
      
      </div>
    </div>
</div>
</body>
    
</html>

The blade file above views the upload images option to the user. In addition, it has two separate divs that pop up required messages. One for success and one for error. The error directive will display any error messages.