How to Upload Multiple Images in Laravel 11

In this small project, we will install and configure Laravel 11 with Bootstrap and jQuery. Then, we will create a form that can be populated and try to upload multiple images in a single go and save the names in the database.

Here is the step-by-step guide:

Step 1: Install and configure Laravel 11

composer create-project laravel/laravel multipleimages --prefer-dist

Go inside the folder and install the laravel/ui package:

composer require laravel/ui

Now, add the Bootstrap library using laravel/ui:

php artisan ui bootstrap

Run these commands step by step to your project terminal.

npm install

npm run dev

Go to the Laravel project root and start the development server using this command:

php artisan serve

Head to the browser and go to this link: https://localhost:8000

This is how Laravel 11 looks like this:

Home Page of Laravel 11

Step 2: Connecting Laravel with Database

Go to the .env file and add the database credentials.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=multipleimages
DB_USERNAME=root
DB_PASSWORD=root

In the next step, create an “Image” model and migration file.

php artisan make:model Image -m

It will create an Image.php model and migration file inside the database >> migrations >> create_images_table.php file.

Add the below code inside the up() function of the create_images_table.php file.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->string('filename');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('images');
    }
};

Now, create a table based on the above migration:

php artisan migrate

It will create a “images” table in the database.

Images table

We need to set up the controller for our application.

Type the following command to generate a controller.

php artisan make:controller ImageController

It will generate an ImageController.php file inside the App >> Http >> Controllers directory.

Step 3: Define routes in the web.php file.

Go to the routes  >>  web.php file, import the ImageController.php file at the head, and add two routes:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ImageController;


Route::get('images/upload', [ImageController::class, 'create'])->name("uploads.create");
Route::post('images/upload', [ImageController::class, 'store'])->name("uploads.store");

The “get” route displays the form, and the “post” route stores image details in the database and uploads them to the server.

In the ImageController, import the Image.php model and add the create() function with the below code.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Image;
class ImageController extends Controller
{
    public function create()
    {
        return view('create');
    }

    public function store(Request $request)
    {
    
    }
}

We will write the code for the store() function later!

Step 4: Create views

Let’s create a master view called layout.blade.php file inside the views folder.

<!DOCTYPE html>
<html lang="en">
<head> 
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Laravel Multiple Images Upload</title>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js"></script>
</head>
<body>
    <div class="container">
        @yield('content')
    </div>
 <!-- Scripts -->
    @vite(['resources/sass/app.scss', 'resources/js/app.js'])
</body>
</html>

We used the @vite directive to import CSS and JS assets into our project. We included jQuery separately!

Create a create.blade.php file inside the views folder and write the below code.

@extends('layout')

@section('content')
<h3>Laravel Multiple Images Upload</h3>
@if (count($errors) > 0)
    <div class="alert alert-danger">
        <strong>Whoops!</strong> There were some problems with your input.<br><br>
        <ul>
          @foreach ($errors->all() as $error)
              <li>{{ $error }}</li>
          @endforeach
        </ul>
    </div>
    @endif
    @if(session('success'))
        <div class="alert alert-success">
                {{ session('success') }}
        </div> 
    @endif
    <form method="post" action="{{ route('uploads.store') }}" enctype="multipart/form-data">
        @csrf
        <div class="input-group control-group increment" >
            <input type="file" name="filename[]" class="form-control">
            <div class="input-group-btn"> 
                    <button class="btn btn-success" type="button"><i class="glyphicon glyphicon-plus"></i>Add</button>
            </div>
        </div>
        <div class="clone hide">
            <div class="control-group input-group" style="margin-top:10px">
                <input type="file" name="filename[]" class="form-control">
                <div class="input-group-btn"> 
                    <button class="btn btn-danger" type="button"><i class="glyphicon glyphicon-remove"></i> Remove</button>
                </div>
            </div>
        </div>
        <button type="submit" class="btn btn-primary" style="margin-top:10px">
            Upload
        </button>
    </form>        
  </div>
@endsection

We are extending the master layout from the layout.blade.php file.

We added a simple web form to add multiple images. The form looks like this:

Form of uploading multiple images

However, we need functionality to populate the input field when we click the add button. So first, let us do that.

We will use the jQuery for that feature.

Step 5: Adding the jQuery code to populate the input field

Write the jQuery code to populate the form field inside the create.blade.php file.

@extends('layout')

@section('content')
<h3>Laravel Multiple Images Upload</h3>
@if (count($errors) > 0)
    <div class="alert alert-danger">
        <strong>Whoops!</strong> There were some problems with your input.<br><br>
        <ul>
          @foreach ($errors->all() as $error)
              <li>{{ $error }}</li>
          @endforeach
        </ul>
    </div>
    @endif
    @if(session('success'))
        <div class="alert alert-success">
                {{ session('success') }}
        </div> 
    @endif
    <form method="post" action="{{ route('uploads.store') }}" enctype="multipart/form-data">
        @csrf
        <div class="input-group control-group increment" >
            <input type="file" name="filename[]" class="form-control">
            <div class="input-group-btn"> 
                    <button class="btn btn-success" type="button"><i class="glyphicon glyphicon-plus"></i>Add</button>
            </div>
        </div>
        <div class="clone hide">
            <div class="control-group input-group" style="margin-top:10px">
                <input type="file" name="filename[]" class="form-control">
                <div class="input-group-btn"> 
                    <button class="btn btn-danger" type="button"><i class="glyphicon glyphicon-remove"></i> Remove</button>
                </div>
            </div>
        </div>
        <button type="submit" class="btn btn-primary" style="margin-top:10px">
            Upload
        </button>
    </form>        
  </div>
  <script type="text/javascript">
        $(document).ready(function() {
            // When the 'Add' button (btn-success) is clicked
            $(".btn-success").click(function() { 
                var clone = $(".clone").clone(true).removeClass('clone hide'); // Clone the hidden template element and remove 'clone' and 'hide' classes
                $(".increment").after(clone); // Insert the cloned element after the element with class 'increment'
            });

            // When the 'Remove' button (btn-danger) is clicked
            $("body").on("click", ".btn-danger", function() { 
                $(this).closest(".control-group").remove(); // Remove the closest parent with class 'control-group'
            });
        });
    </script>
@endsection

If you save this file and go to this URL: http://localhost:8000/images/upload, you can populate the fields like this:

Populating fields using jQuery

Step 6: Add Laravel Image validation.

We are inserting multiple images, so we need to create an array validation in our project.

Add the following code to validate our input file in an ImageController.php file.

public function store(Request $request)
{
       $request->validate([
           'filename' => 'required',
           'filename.*' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048'
       ]);
}

This code checks against the required field and the image type.

If the input file does not contain an image or jpg, png, gif, or svg, it throws an error, and laravel catches it and displays these errors in the front end.

We already wrote a code to display the errors, so let’s head to a web form and try to submit the data without any image and see the error:

Image field is required - validation

Let’s try uploading a PDF file to see if it validates the request. Here is the output:

Invalid file type validation error

Step 7: Uploading images and save their details to the database

After checking the validation, we must store the image names in our database.

Let’s write our final code to upload the images to the server and save the details in the database inside the ImageController’s store() function.

public function store(Request $request)
{
        $request->validate([
            'filename' => 'required',
            'filename.*' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048'
        ]);
        if ($request->hasfile('filename')) {
            foreach ($request->file('filename') as $image) {
                $name = $image->getClientOriginalName();
                $image->move(public_path() . '/images/', $name);
                $data[] = $name;
            }
        }
        $image = new Image();
        $image->filename = json_encode($data);
        $image->save();

        return back()->with('success', 'Your images has been successfully');
}

In this code, we are checking if the request has file input. Since the input is multiple images, we are iterating a loop to get the file’s original name, upload it to the public path of the server, and save the image details in the database in json format.

Let’s upload three images:

Uploading three images

Now, when I upload the images, I get the output like this:

Multiple images upload successfully

Let’s check the database and see if it saved the details:

Image details saved in the database

Now, let’s check out the public >> images folder and see if uploaded images exist!

Multiple file is uploaded to public : images folder

That’s it, fellow coders!

41 thoughts on “How to Upload Multiple Images in Laravel 11”

  1. Thanks for the post it’s very helpfull if possible provide the Demo option here.
    So after seeing the Demo we can save or time to it’s meeting or requirment or not.

    Reply
      • thats true Krunal, but I think I have an observation, I’m new to this, but I think that when you add an image the js you use has some error because they do not add the names of the images as it should, nor less able to select more than one and list the images that were selected. Could you help me to fixed?

        Reply
  2. how to retrieve the data of json encode?
    for example i want to display the every image what should i do? i have tried json_decode but its not working properly on index method.

    Reply
  3. I have the same issue why is it saving only one image in the database? i use laravel 5.6 do you think is the problem ? thanks a lot.

    Dominic

    Reply
  4. Thanks !
    but why when i’ve been uploaded 3 images, it’s only save one filename/json in the Database, but in public folder it’s work, they save all the 3 images ?

    Reply
  5. Below are some corrections i did , if anyone want files to be saved with different id , instead of array also added time() in $name so it will stop messing with different images with same name.

    // Controller

    public function store(Request $request)

    {

    $this->validate($request, [

    ‘filename’ => ‘required’,
    ‘filename.*’ => ‘image|mimes:jpeg,png,jpg,gif,svg|max:2048’

    ]);

    if($request->hasfile(‘filename’))
    {

    foreach($request->file(‘filename’) as $image)
    {
    $form= new Form();

    $name=time().’.’.$image->getClientOriginalName();
    $image->move(public_path().’/images/’, $name);
    $data = $name;
    $form->filename=$data;
    $form->save();
    }

    }

    return back()->with(‘success’, ‘Your images has been successfully’);
    }

    Reply
  6. again and again it showing image field is reuired
    ?can anyone help me

    Add

    Remove

    Add

    Remove

    public function store(Request $request)
    {
    $request->validate([
    ‘service_center’ => ‘required’,
    ‘outcome’ => ‘required’,
    ‘cbdm_images’ => ‘required|image|mimes:jpeg,png,jpg,svg|max:2048’,
    ‘cbdm_file’ => ‘required|mimes:pdf,doc,excel’,
    ]);

    $cbdm_info = new Cbdm_details([
    $cbdm_info->service_center =$request->get(‘service_center’),
    $cbdm_info->outcome = $request->get(‘outcome’),
    $cbdm_info->cbdm_images = $request->file(‘cbdm_images’),
    $cbdm_info->cbdm_file = $request->file(‘cbdm_file’)
    ]);
    if($file = $request->file(‘cbdm_images’)) {
    $name = time() . $file->getClientOriginalName();
    $file->move(‘/cbdm-img-file’, $name);
    $photo = Cbdm_details::create([‘file’=>$name]);
    $input[‘cbdm_images’] = $photo->id;

    dd($file);
    }
    $cbdm_info->save();
    return redirect(‘admin/cbdm’)->with(‘success’, ‘Successfully Added CBDM Details!’);

    Reply
    • Maybe you need to print your request object and see if every field is there! Try print_r() function and exit to stop the execution and print your request object.

      Reply
  7. In array only first file data is storing , what the issues please let me know its urgent

    Add

    Remove

    ‘cbdm_images.*’ => ‘image|mimes:jpeg,png,jpg,gif,svg|max:2048′,
    //’cbdm_file’ => ‘mimes:pdf,doc,.xlsx’
    ]);

    if($request->hasFile(‘cbdm_images’)){
    foreach($request->file(‘cbdm_images’) as $image){
    $filenameWithExt = $image->getClientOriginalName();
    $filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);
    $extension = $image->getClientOriginalExtension();
    $fileNameToStore = $filename.’_’.time().’.’.$extension;
    $path = $image->storeAs(‘cbdm’,$fileNameToStore);
    $data[] = $fileNameToStore;
    dd($data);
    }
    }

    $cbdm_info->cbdm_images = json_encode($data);
    $cbdm_info->save();

    Reply
  8. nice. would be good to see how this saves in the dbase. does it create a row for each image with an id or does it save all the filenames in an array in 1 row?

    Reply
  9. $name=$image->getClientOriginalName();
    $image->move(public_path().’/properties/’, $name);
    $url= URL::to(‘/’) . ‘/properties/’ . $image->getClientOriginalName();
    $data[] = $url;
    Am storing the image location url to my database column call images.
    and the are in this form…
    [“http:\/\/kizflex.local\/properties\/10.jpg”,”http:\/\/kizflex.local\/properties\/11.jpg”]
    So how can iterate through this particular column so as to get each address and display it in my view?

    Reply
  10. Hello, in what way could I show the images stored in a view?
    and tried:
    product-> image [0]
    product-> image [1]
    Without any result

    Reply
  11. excuse me sir, i have a question,
    is possible to add some text field like a title image and the value automatically generate by file name?
    if possible, can you help me?

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.