Upload Multiple files using Dropzone and Laravel Medialibrary

Multiple file upload with dropzone js and laravel medialibrary Package 👨🏿‍🏫

80 / 100

Multiple File upload with Dropzone js and Laravel Medialibrary. Multiple or single File Upload with Dropzone and Laravel MediaLibrary Package. 2 forms or a single form use as dropzone form and then upload files article.

Hi, my name is Hassam. I am a Full Stack Developer and in this tutorial, we create A simple project to add a Product with thumbnail image and Gallery images.

Before starting you need to know a few things. Product form requires a <form> and dropzone also require a <form> means we can’t use <form> inside a form tag. so, How do we implement this because we are going to add two dropzone forms? The answer is we will create three forms or maybe two its depends on your requirement but I’ll cover both scenarios because most of the developers have issues with how to upload multiple images with other form fields.

Source code: https://github.com/hassamulhaq/dropzone-laravel-medialibrary-fileupload-demo [visit]

Preview

Gif Preview

Live preview of Multiple Images upload using dropzone with Laravel medialibrary
Live preview of Multiple Images upload using dropzone with Laravel medialibrary

Queries we will solve

  • We want to use dropzone because of its nice UI.
  • But dropzone works under Ajax request so how we can pass other form data?
    • dropzone require a <form> but create product also require a <form> so, how we can do it? as we know we can’t use <form> inside a <form>
    • we also use laravel-medialibrary package, and this package require a Model_Id to save data in media table against that model_id. but when we also use dropzone and it sends a Ajax request but on that time we have no model_id (product_id) because after creating product we will get a model_id and using that modal_id we can use laravel-medialibrary. so, when we are on /create-product/ page we don’t know the model_id because product is not saved yet. how to handle this?
  • We create two or three forms.
    • one <form> is for create product (product_name, description, price)
    • second <form> is for dropzone multiple images upload
    • third <form> is for thumbnail (optional)
  • dropzone Multiple file upload form working is
    • when users drag-and-drop images on dropzone form an Ajax request send by dropzone, in backend we’ll store those images in temporary folder with a unique name as per file, and in returns, we will return an array of images with path. and using javascript we will create or append new input fields in create-product form like <input type=”hidden” name=gallery[] value=”1452_hassam_profile.png”>, if user upload 2 images then 2 new hidden inputs will be append in create-product form.
    • and when a user submits the create-product form the hidden input data is also sent to the backend, and in backend we first save product, and after saving the product we get last inserted-model-id using $product->id. and finally, loop on hidden fields get the value of input and trigger laravel-medialibrary function. now we have both model_id and image. laravel-medialibrary pick the image from /tmp/images/ and move it in storage and also made an entry in the database against model_id (product_id).

Download the javascript and CSS files I used in this tutorial. [ DOWNLOAD ]

Install Fresh laravel

composer create-project laravel/laravel laradropzone
npm install
composer require laravel/breeze --dev

Create a database and made an entry in .env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laradropzonedb
DB_USERNAME=root
DB_PASSWORD=

Set database engine to InnoDB

config/database.php

'mysql' => [
    'engine' => 'InnoDB',
],

set defaultStringLength is 191 in AppServiceProvider.php

app/providers/AppServiceProvider.php

public function boot()
{
    Schema::defaultStringLength(191);
}

Install Breez package and config it

php artisan breeze:install
 
php artisan migrate
npm install
npm run dev

Install Flowbite and configure it. Why flowbite?

You can also use bootstrap instead of flowbite. I use flowbite because it is based on tailwindCSS and I need to trigger a Modal Popup for a Modal I need to write a separate js code but flowbite can do it for me. If you use bootstrap then you can skip the installation of flowbite, or if you have a javascript code to popup a Modal then also skip the installation of flowbite.

you can also visit flowbite website for configuration flowbite in Laravel. https://flowbite.com/docs/getting-started/laravel/ link.

Download flowbite and dropzone assets => Download

npm install -D tailwindcss postcss autoprefixer flowbite
npx tailwindcss init

Add the view paths and require Flowbite as a plugin inside tailwind.config.js

const defaultTheme = require('tailwindcss/defaultTheme');

/** @type {import('tailwindcss').Config} */
module.exports = {
    content: [
        "./resources/**/*.blade.php",
        "./resources/**/*.js",
        "./resources/**/*.vue",
        "./node_modules/flowbite/**/*.js"
    ],

    theme: {
        extend: {
            fontFamily: {
                sans: ['Nunito', ...defaultTheme.fontFamily.sans],
            },
        },
    },

    plugins: [
        require('@tailwindcss/forms'),
        require('flowbite/plugin')
    ],
};

Add flowbite js in the layout /resources/views/layouts/app.blade.php

You can use either CDN or stand-alone javascript. assets are provided => download them and add in…
I pasted flowbite.js in public/plugins/flowbite@1.5.3/
Add below code in resources/views/layouts/app.blade.php

		<script src="{{ asset('plugins/flowbite@1.5.3/flowbite.js') }}"></script>
		{{--<script src="https://unpkg.com/flowbite@1.5.3/dist/flowbite.js"></script>--}}

		@stack('footer_scripts')
    </body>
</html>

@stack('footer_scripts') here we can inject javascript code from child components using @push

Create Product Model, Controller and MediaController

we will add the code of the controller and modal later. MediaController will take care of files uploaded by dropzone form.
and Product Model and Controller is for handling product create data.

Product // Model
ProductsController
MediaController

Migration code of Products table.

return new class extends Migration {
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name')->nullable();
            $table->text('description')->nullable();
            $table->string('price')->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('products');
    }
};

Add routes in web.php

Route::get('products', [ProductsController::class, 'index'])->middleware('auth:sanctum')->name('products.index');
Route::get('products/create', [ProductsController::class, 'create'])->middleware('auth:sanctum')->name('products.create');
Route::post('products/store', [ProductsController::class, 'store'])->middleware('auth:sanctum')->name('products.store');


Route::post('upload-media', MediaController::class)->middleware('auth:sanctum')->name('upload-media');

Also read: 20 API-related terms, How to add sticky menu in footer flatsome theme

Create a product folder in views

/views/products/

index.blade.php
create.blade.php

Return view from ProductsController.php

public function create()
{
    return view('products.create');
}

Product create blade code

in this create blade we have

  • Product title input
  • Product description textarea input
  • Product price input
  • and Dropzone two forms
    • Multiple file upload form (it’s a Modal Popup)
    • Single file upload form (also a Modal popup)

Now we need to 2 Popup Modals for dropzone
But
first I config the multiple files upload dropzone and in last we’ll cover single file upload functionality.

views/products/create.blade.php


<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Create Product') }}
        </h2>
    </x-slot>

    <div class="bg-white mt-4 max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
        <form method="post" action="{{ route('products.store') }}" enctype="multipart/form-data" autocomplete="off">
            @csrf

            <div class="mb-6">
                <label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Product Title</label>
                <input type="text" id="title" name="title" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
            </div>
            <div class="mt-4 mb-6">
                <label for="description" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Description</label>
                <textarea id="description" rows="6" name="description" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"></textarea>
            </div>
            <div class="mb-6">
                <label for="price" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">Price</label>
                <input type="number" id="price" name="price" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
            </div>

            <div class="flex">
                <div class="mt-4 mr-2 mb-2 w-full">
                    <!-- #singleMediaDropzoneModal is in component media-form-single-dropzone -->
                    <button type="button" data-modal-toggle="singleMediaDropzoneModal" class="text-sm px-5 py-12 text-center block w-full text-gray-700 bg-gray-100 border border-gray-300 hover:bg-gray-200 focus:ring-2 focus:outline-none focus:ring-gray-300 font-medium rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
                        Thumbnail
                    </button>
                </div>
                <div class="mt-4 ml-2 mb-2 w-full">
                    <button type="button" data-modal-toggle="multiMediaDropzoneModal" class="text-sm px-5 py-12 text-center block w-full text-gray-700 bg-gray-100 border border-gray-300 hover:bg-gray-200 focus:ring-2 focus:outline-none focus:ring-gray-300 font-medium rounded-lg dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
                        Upload Gallery
                    </button>
                </div>
            </div>

            <div class="mt-2">
                <button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit</button>
            </div>
        </form>
    </div>




    <!-- dropzone multiple modal popup -->
    <div id="multiMediaDropzoneModal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full">
        <div class="relative p-4 w-full max-w-2xl h-full md:h-auto">
            <!-- Modal content -->
            <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
                <!-- Modal header -->
                <div class="flex justify-between items-center items-start p-4 rounded-t border-b dark:border-gray-600">
                    <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
                        Upload Media/s
                    </h3>
                    <button type="button" data-modal-toggle="multiMediaDropzoneModal" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
                        <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
                        <span class="sr-only">Close modal</span>
                    </button>
                </div>
                <!-- Modal body -->
                <div class="p-6 space-y-6">
                    <form action="{{ route('upload-media') }}" method="post" id="mediaFormMultipleDropzone" class="dropzone" enctype="multipart/form-data">
                        @csrf
                    </form>
                </div>
                <!-- Modal footer -->
                <div class="flex items-center p-6 space-x-2 rounded-b border-t border-gray-200 dark:border-gray-600">
                    <input id="submit-dropzone" type="submit" name="submitDropzone" value="Upload Gallery" class="cursor-pointer py-2 px-3 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"/>
                </div>
            </div>
        </div>
    </div>


    <!-- dropzone single modal popup -->
    <div id="singleMediaDropzoneModal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 w-full md:inset-0 h-modal md:h-full">
        <div class="relative p-4 w-full max-w-2xl h-full md:h-auto">
            <!-- Modal content -->
            <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
                <!-- Modal header -->
                <div class="flex justify-between items-center items-start p-4 rounded-t border-b dark:border-gray-600">
                    <h3 class="text-xl font-semibold text-gray-900 dark:text-white">
                        Upload Thumbnail
                    </h3>
                    <button type="button" data-modal-toggle="singleMediaDropzoneModal" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
                        <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
                        <span class="sr-only">Close modal</span>
                    </button>
                </div>
                <!-- Modal body -->
                <div class="p-6 space-y-6">
                    <form action="{{ route('upload-media') }}" method="post" id="mediaFormSingleDropzone" class="dropzone" enctype="multipart/form-data">
                        @csrf
                    </form>
                </div>
                <!-- Modal footer -->
                <div class="flex items-center p-6 space-x-2 rounded-b border-t border-gray-200 dark:border-gray-600">
                    <input id="submit-single-dropzone" type="submit" name="submitSingleDropzone" value="Upload Thumbnail" class="cursor-pointer py-2 px-3 text-sm font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"/>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
create product blade preview
create product blade preview

Now adding jquery and dropzone bundle

https://www.dropzone.dev/js/ for details.

add dropzone js/css in resources/views/layouts/app.blade.php before <head>

<link rel="stylesheet" href="{{ asset("/plugins/dropzone@6.0.0-beta.2/dropzone.css") }}">
<script src="{{ asset("plugins/dropzone@6.0.0-beta.2/dropzone-min.js") }}"></script>

Now we need to config dropzone form.

in /resources/views/products/create.blade.php add the below code before </x-app-layout>
Do you remember we added @stack('footer_scripts') in /resources/views/layouts/app.blade.php so, now we will inject the js code of dropzone using @push()

Few configuration for dropzone setup

  • paramName: “files” ( $request->input(‘files’) )
  • uploadMultiple: true,
  • maxFiles: 5,
  • myDropzone.on(“successmultiple” …) (in single file we use Dropzone.on(“success”)
// Multiple file upload code of dropzone form

@push('footer_scripts')
<script>
    /*
    * Dropzone script
    * */
    // disable autodiscover
    Dropzone.autoDiscover = false;

    //const filesize = 5;
    const allowMaxFilesize = 5;
    const allowMaxFiles = 5;

    const myDropzone = new Dropzone("#mediaFormMultipleDropzone", {
        url: "{{ route('upload-media') }}",
        method: "POST",
        paramName: "files",
        autoProcessQueue: false,
        acceptedFiles: ".jpeg,.jpg,.png,.gif",
        maxFiles: allowMaxFiles,
        maxFilesize: allowMaxFilesize, // MB
        uploadMultiple: true,
        parallelUploads: 100, // use it with uploadMultiple
        createImageThumbnails: true,
        thumbnailWidth: 120,
        thumbnailHeight: 120,
        addRemoveLinks: true,
        timeout: 180000,
        dictRemoveFileConfirmation: "Are you Sure?", // ask before removing file
        // Language Strings
        dictFileTooBig: `File is to big. Max allowed file size is ${allowMaxFilesize}mb`,
        dictInvalidFileType: "Invalid File Type",
        dictCancelUpload: "Cancel",
        dictRemoveFile: "Remove",
        dictMaxFilesExceeded: `Only ${allowMaxFiles} files are allowed`,
        dictDefaultMessage: "Drop files here to upload",
    });

    myDropzone.on("addedfile", function(file) {
        // while file is drag or add in dropzone form maybe you want some other functionality
        // as per file then you can write code in this block
        //console.log(file);
    });

    myDropzone.on("removedfile", function(file) {
        // while file is remove from dropzone form maybe you want some other functionality
        // as per file remove then you can write code in this block
        // console.log(file);
    });

    // Add more data to send along with the file as POST data. (optional)
    // I commented below code why? when the file is send maybe you want to pass some other form input data so, you
    // you can pass it in this block. BUT in this tutorial I'm using dropzone form only for images so, i commented below code block.
    /*myDropzone.on("sending", function(file, xhr, formData) {
        formData.append("dropzone", "1"); // $_POST["dropzone"]
        formData.append("productId", "10"); // $_POST["productId"]
    });*/

    myDropzone.on("error", function(file, response) {
        console.log(response);
    });

    // on success
    myDropzone.on("successmultiple", function(file, response) {
        // get response from successful ajax request
        // response includes what you you return from php side
        console.log(response);
        $.each(response, function( key, value ) {
            $('form#product_create_form').append('<input type="text" name="gallery[]" value="'+value.name+'">')
        });


        /* submit the form after images upload
        (if u want to submit rest of the inputs in the form)
        I have no other inputs so, I commented below line. */
        //document.getElementById("mediaFormMultipleDropzone").submit();
    });

    // button trigger for processingQueue
    const submitDropzone = document.getElementById("submit-dropzone");
    submitDropzone.addEventListener("click", function(e) {
        // Make sure that the form isn't actually being sent.
        e.preventDefault();
        e.stopPropagation();

        if (myDropzone.files !== "") {
            // console.log(myDropzone.files);
            myDropzone.processQueue();
        } else {
            // if no file submit the form
            document.getElementById("dropzone-form").submit();
        }
    });
    // multiple
</script>
@endpush

A shorter version of the above Dropzone options code

@push('footer_scripts')
<script>
const myDropzone = new Dropzone("#mediaFormMultipleDropzone", {
    url: "{{ route('upload-media') }}",
    autoProcessQueue: false,
    uploadMultiple: true,
    addRemoveLinks: true,
    parallelUploads: 10,
    headers: {
        'X-CSRF-TOKEN': "{{ csrf_token() }}"
    },
    myDropzone.on("successmultiple", function(file, response) {
        // get response from successful ajax request
        // response includes what you you return from php controller side
        console.log(response);
        $.each(response, function( key, value ) {
            $('form#product_create_form').append('<input type="text" name="gallery[]" value="'+value.name+'">')
        });
    });

    // button trigger for processingQueue
    const submitDropzone = document.getElementById("submit-dropzone");
    submitDropzone.addEventListener("click", function(e) {
        // Make sure that the form isn't actually being sent.
        e.preventDefault();
        e.stopPropagation();

        if (myDropzone.files !== "") {
            // console.log(myDropzone.files);
            myDropzone.processQueue();
        } else {
            // if no file submit the form
            document.getElementById("dropzone-form").submit();
        }
    });
    // multiple
</script>
@endpush

If you images in dropzone gallery form then hidden inputs create like shown in the below image.

new_input_created_and_append_in_form
new input created and append in product form

New /tmp/upload/ directory will create under /storage/

Files add in newly created directory under /storage/tmp/upload/
Files add in newly created directory under /storage/tmp/upload/
gallery images input created
Gallery images input created <input type=”text” name=”gallery[]” value=”….”>

If I return die and dump dd() then the results will be

dd_data_of_product_form
result of dd() $request->toArray() in ProductController::store

Code of files uploaded through Dropzone, add it in MediaController

in MediaController we have a __invoke method whenever we call MdeiaController the __invoke the method will auto call (invoked) you can create a method like uploadFile() if you go with some other method then made a change in web.php e.g

// __invoke route
Route::post('upload-media', MediaController::class)->middleware('auth:sanctum')->name('upload-media');

// or if you write some method like uploadFile then route would be
Route::post('upload-media', [MediaController::class, 'uploadFile'])->middleware('auth:sanctum')->name('upload-media');
class MediaController extends Controller
{

    public function __invoke(Request $request)
    {
        $response = [];

        $path = storage_path('tmp/uploads');
        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }

        $files = $request->file('files');
        if (is_array($files)) {
            foreach($files as $index => $file) {
                $name = uniqid() . '_' . trim($file->getClientOriginalName());
                $file->move($path, $name);


                $response[$index]['name'] = $name;
                $response[$index]['original_name'] = $file->getClientOriginalName();
            }
        }

        return response()->json($response);
    }
}

The files uploaded through dropzone form will be moved in /storage/tmp/images/ and __invoke method returns the path and file names moved by dropzone and we create inputs like <input type="hidden" name="gallery[]"> if the user uploads 2 images then 2 gallery[] inputs will be created and appended on the product form which is the form we will submit. the hidden input like this <input type="hidden" name="gallery[]" value="4782_imagename.png"> and now we are going to install Spatie Media-Library package this package helps to save gallery images in the database.

Spatie Laravel-medialibrary Installation

https://spatie.be/docs/laravel-medialibrary/v10/introduction

This package can associate all sorts of files with Eloquent models. It provides a simple, fluent API to work with.

Media Library can be installed via Composer:

composer require "spatie/laravel-medialibrary:^10.4.5"

Preparing the database migration for Laravel-Medialibrary

You need to publish the migration to create the media table:

php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"

// After that, you need to run migrations.
php artisan migrate
Spatie Laravel-medialibrary table after run migration
Spatie Laravel-medialibrary table after run migration

Modify Product Model

add HasMedia interface in the Product Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Product extends Model implements HasMedia
{
    use InteractsWithMedia;

    protected $fillable = [
        'title',
        'name',
        'price',
        'description'
    ];
}

Before submitting the create product form here is last step

// ProductController.php

public function store(Request $request)
{
    $product = Product::create([
        "name" => $request->input('name'),
        "description" => $request->input('description'),
        "price" => $request->input('price'),
    ]);


    // move gallery media
    if ($request->has('gallery')) {
        foreach ($request->input('gallery', []) as $file) {
            $product->addMedia(storage_path('tmp/uploads/' . $file))->toMediaCollection('gallery');
        }
    }

    $response = [
        'status' => 'success',
        'message' => 'Product added'
    ];
    return \response()->json($response);
}

Finally, Submit the create product form

Upload a few images in dropzone form, hit upload, and close the modal. fill create product fields like name, description, and price and submit the form. There will be two database tables modified one is product table and the second is media table. In media table, we will have ProductId (Model Id), type, and other info.

After submitting the success result
After submitting the success result
Product data with media saved
Product data with media saved in media table
Entry saved in product table
Entry saved in the product table

Upload a Single image (thumbnail) using dropzone. Two dropzone forms on single page

In this step, I am going to cover how to upload a single image.

Remember we have two dropzone forms added in create.blade.php above we cover how to upload multiple images and here we will see how to upload a single image. To upload a single image we need to modify a few dropzone options like those below.

  • paramName: “thumbnail” ( $request->input(‘thumbnail’) )
  • maxFiles: 1,
  • uploadMultiple: false,
  • singleDropzone.on(“success” … (in multiple files we use Dropzone.on(“successmultiple”)

Now config thumbnail form working with dropzonejs

add below code inside @push('footer_scripts')

set-paramName-and get-data-in-php
paramName: “thumbnail” in controller we get get data using $request->input(“thumbnail”)
...
...
...


// single
const singleDropzone = new Dropzone("#mediaFormSingleDropzone", {
    url: "{{ route('upload-media') }}",
    method: "POST",
    paramName: "thumbnail",
    autoProcessQueue: false,
    acceptedFiles: ".jpeg,.jpg,.png,.gif",
    maxFiles: 1,
    maxFilesize: allowMaxFilesize, // MB
    uploadMultiple: false,
    parallelUploads: 100, // use it with uploadMultiple
    createImageThumbnails: true,
    thumbnailWidth: 120,
    thumbnailHeight: 120,
    addRemoveLinks: true,
    timeout: 180000,
    dictRemoveFileConfirmation: "Are you Sure?", // ask before removing file
    // Language Strings
    dictFileTooBig: `File is to big. Max allowed file size is ${allowMaxFilesize}mb`,
    dictInvalidFileType: "Invalid File Type",
    dictCancelUpload: "Cancel",
    dictRemoveFile: "Remove",
    dictMaxFilesExceeded: `Only ${allowMaxFiles} files are allowed`,
    dictDefaultMessage: "Drop files here to upload",
});

singleDropzone.on("addedfile", function(file) {
    //console.log(file);
});

singleDropzone.on("removedfile", function(file) {
    // console.log(file);
});

// Add more data to send along with the file as POST data. (optional)
/*singleDropzone.on("sending", function(file, xhr, formData) {
    formData.append("dropzone", "1"); // $_POST["dropzone"]
    formData.append("productId", "10"); // $_POST["productId"]
    formData.append("userId", "7"); // $_POST["productId"]
});*/

singleDropzone.on("error", function(file, response) {
    console.log(response);
});

// on success
singleDropzone.on("success", function(file, response) {
    // get response from successful ajax request
    // response includes what you you return from php side
    console.log(response);
    $.each(response, function( key, value ) {
        $('form#product_create_form').append('<input type="text" name="thumbnail" value="'+value.name+'">')
    });

    /* submit the form after images upload
    (if u want to submit rest of the inputs in the form)
    I have no other inputs so, I commented below line. */
    //document.getElementById("mediaFormMultipleDropzone").submit();
});

// button trigger for processingQueue
const submitSingleDropzone = document.getElementById("submit-single-dropzone");
submitSingleDropzone.addEventListener("click", function(e) {
    // Make sure that the form isn't actually being sent.
    e.preventDefault();
    e.stopPropagation();

    if (singleDropzone.files !== "") {
        // console.log(myDropzone.files);
        singleDropzone.processQueue();
    } else {
        // if no file submit the form
        document.getElementById("dropzone-single-form").submit();
    }
});
// single

Made an entry of thumbnail upload in MediaController.php

if (is_object($request->file('thumbnail'))) {
    $thumb = $request->file('thumbnail');
    $name = uniqid() . '_' . trim($thumb->getClientOriginalName());
    $thumb->move($path, $name);

    $response[0]['name'] = $name;
    $response[0]['original_name'] = $thumb->getClientOriginalName();
}


// Fill controller will be like below
class MediaController extends Controller
{

    public function __invoke(Request $request)
    {
        $response = [];

        $path = storage_path('tmp/uploads');
        if (!file_exists($path)) {
            mkdir($path, 0777, true);
        }

        $files = $request->file('files');
        if (is_array($files)) {
            foreach($files as $index => $file) {
                $name = uniqid() . '_' . trim($file->getClientOriginalName());
                $file->move($path, $name);


                $response[$index]['name'] = $name;
                $response[$index]['original_name'] = $file->getClientOriginalName();
            }
        }

        if (is_object($request->file('thumbnail'))) {
            $thumb = $request->file('thumbnail');
            $name = uniqid() . '_' . trim($thumb->getClientOriginalName());
            $thumb->move($path, $name);

            $response[0]['name'] = $name;
            $response[0]['original_name'] = $thumb->getClientOriginalName();
        }

        return response()->json($response);
    }
}

and add thumbnail media code in ProductsController.php

// single image or thumbnail code
if ($request->has('thumbnail')) {
    $product->addMedia(storage_path('tmp/uploads/' . $request->input('thumbnail')))->toMediaCollection('thumbnail');
}


// ProductController store method will be like
    public function store(Request $request)
    {
        $product = Product::create([
            "name" => $request->input('name'),
            "description" => $request->input('description'),
            "price" => $request->input('price'),
        ]);


        // thumbnail
        if ($request->has('thumbnail')) {
            $product->addMedia(storage_path('tmp/uploads/' . $request->input('thumbnail')))->toMediaCollection('thumbnail');
        }


        // move media
        if ($request->has('gallery')) {
            foreach ($request->input('gallery', []) as $file) {
                $product->addMedia(storage_path('tmp/uploads/' . $file))->toMediaCollection('gallery');
            }
        }

        $response = [
            'status' => 'success',
            'message' => 'Product added'
        ];
        return \response()->json($response);
    }

So, finally, we cover how to upload multiple files using dropzone, single file upload using dropzone and how to implement two dropzone forms in a single blade and handle both forms separately.

If you’ll have any queries then comment below or email me at hello@hassam.dev

Hassam
Hassam

I am passionate about open-source software, privacy, best practices, standards, user experience, and developer experience. My favorite stack these days are Laravel, Vue, and Tailwind. And focusing on Solid Principles, Data Structures, and Design Patterns.

Articles: 23

Leave a Reply

Your email address will not be published. Required fields are marked *