Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

How to Pass a Folder Name via Javascript to Laravel Controller

I use a component to implement my CKEditor on numerous pages throughout my project.

<x-ckeditor folderName="posts" />

This component code includes a block about uploading an image to an ImageUploadContoller. When the image is uploaded via javascript, several versions of the image are created. I want the foldername that I am passing into the compoent to direct the location where the images should be saved. this is my component:

<script src="{{ asset('/plugins/ckeditor5/ckeditor.js') }}"></script>
<script>
    class MyUploadAdapter {
        constructor(loader) {
            // The file loader instance to use during the upload.
            this.loader = loader;
            this.folderName = '{{ $folderName }}'; // Add folderName property
        }

        // Starts the upload process.
        upload() {
            return this.loader.file
                .then(file => new Promise((resolve, reject) => {
                    this._initRequest();
                    this._initListeners(resolve, reject, file);
                    this._sendRequest(file);
                }));
        }

        // Aborts the upload process.
        abort() {
            if (this.xhr) {
                this.xhr.abort();
            }
        }

        // Initializes the XMLHttpRequest object using the URL passed to the constructor.
        _initRequest() {
            const xhr = this.xhr = new XMLHttpRequest();

            // Note that your request may look different. It is up to you and your editor
            // integration to choose the right communication channel. This example uses
            // a POST request with JSON as a data structure but your configuration
            // could be different.
            xhr.open('POST', "{{ route('image.upload', ['_token' => csrf_token()]) }}", true);
            xhr.responseType = 'json';
        }

        // Initializes XMLHttpRequest listeners.
        _initListeners(resolve, reject, file) {
            const xhr = this.xhr;
            const loader = this.loader;
            const genericErrorText = `Couldn't upload file: ${ file.name }.`;

            xhr.addEventListener('error', () => reject(genericErrorText));
            xhr.addEventListener('abort', () => reject());
            xhr.addEventListener('load', () => {
                const response = xhr.response;

                // This example assumes the XHR server's "response" object will come with
                // an "error" which has its own "message" that can be passed to reject()
                // in the upload promise.
                //
                // Your integration may handle upload errors in a different way so make sure
                // it is done properly. The reject() function must be called when the upload fails.
                if (!response || response.error) {
                    return reject(response && response.error ? response.error.message : genericErrorText);
                }

                // If the upload is successful, resolve the upload promise with an object containing
                // at least the "default" URL, pointing to the image on the server.
                // This URL will be used to display the image in the content. Learn more in the
                // UploadAdapter#upload documentation.
                resolve(response);
            });

            // Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded
            // properties which are used e.g. to display the upload progress bar in the editor
            // user interface.
            if (xhr.upload) {
                xhr.upload.addEventListener('progress', evt => {
                    if (evt.lengthComputable) {
                        loader.uploadTotal = evt.total;
                        loader.uploaded = evt.loaded;
                    }
                });
            }
        }

        // Prepares the data and sends the request.
        _sendRequest(file) {
            // Prepare the form data.
            const data = new FormData();

            data.append('upload', file);
            data.append('folderName', this.folderName); // Append the folderName to the FormData

            // Send the request.
            this.xhr.send(data);
        }
    }

    function MyCustomUploadAdapterPlugin(editor) {
        editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
            // Configure the URL to the upload script in your back-end here!
            return new MyUploadAdapter(loader);
        };
    }
</script>

<script>
    // If you want inserted images in a CKEditor to be responsive
    // you can use the following code. It creates a htmlfilter for the
    // image tag that replaces inline "width" and "style" definitions with
    // their corresponding attributes and adds (in this example) the
    // Bootstrap "img-responsive" class.
    ClassicEditor
        .create(document.querySelector('#body'), {
            extraPlugins: [MyCustomUploadAdapterPlugin],
        })
        .then(editor => {
            editor.model.schema.extend('$block', {
                allowAttributes: 'style'
            });
            editor.model.schema.extend('img', {
                allowAttributes: 'style'
            });

            editor.conversion.for('upcast').attributeToAttribute({
                model: 'style',
                view: 'style'
            });

            editor.conversion.for('downcast').attributeToAttribute({
                model: 'style',
                view: 'style'
            });

            editor.data.processor.htmlFilter.addRules({
                elements: {
                    img: function(el) {
                        // Add Bootstrap "img-responsive" class to each inserted image
                        el.addClass('img-responsive');

                        // Process inline "width" and "style" definitions to support percentages
                        var style = el.attributes.style;

                        if (style) {
                            // Get the width from the style
                            var match = /(?:^|\s)width\s*:\s*(\d+(?:\.\d+)?)(%|px);?/i.exec(style),
                                width = match && match[1],
                                unit = match && match[2];

                            // Replace the width
                            if (width) {
                                el.attributes.style = el.attributes.style.replace(
                                    /(?:^|\s)width\s*:\s*(\d+(?:\.\d+)?)%?px;?/i, '');
                                el.attributes.width = unit === '%' ? width : null;
                                el.attributes.style += 'width: ' + width + (unit === '%' ? '%' : 'px') +
                                    ';';
                            }
                        }

                        // Remove the style attribute if it is empty
                        if (!el.attributes.style)
                            delete el.attributes.style;

                        return el;
                    }
                }
            });
        })
        .catch(error => {
            console.error(error);
        });
</script>

Here is the current route that is being used to direct the javascript to the appropriate controller:

Route::post('/image-upload/', [ImageUploadController::class, 'upload'])->name('image.upload');

Here is the ImageUploadController class where the image processing happens. Where you see {$folderName}, I want to replace that with the passed folderName:

class ImageUploadController extends Controller
{
    public function upload(Request $request)
    {
        // Validate the uploaded file
        $request->validate([
            'upload' => 'required|image|mimes:jpeg,png,jpg,gif|max:10000', // Example validation rules
            'folderName' => 'required|string' // Validate that folderName is provided and is a string
        ]);

        if ($request->hasFile('upload')) {
            $uploadedFile = $request->file('upload');

            // Generate a unique filename
            $filename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
            $extension = $uploadedFile->getClientOriginalExtension();
            $filenametostore = $filename . '_' . time() . '.' . $extension;

            // Store the uploaded file
            $uploadedFile->storeAs('public/uploads/{$folderName}/full', $filenametostore);

            // Create and store thumbnail
            $thumbnail = Image::make($uploadedFile)->resize(500, 150, function ($constraint) {
                $constraint->aspectRatio();
            });
            $thumbnailPath = 'public/uploads/{$folderName}/thumbnail/' . $filenametostore;
            Storage::put($thumbnailPath, (string)$thumbnail->encode());

            // Create and store Preview
            $preview = Image::make($uploadedFile)->resize(1000, null, function ($constraint) {
                $constraint->aspectRatio();
            });
            $previewPath = 'public/uploads/{$folderName}/preview/' . $filenametostore;
            Storage::put($previewPath, (string)$preview->encode());

            Log::info('storage/uploads/{$folderName}/' . $filenametostore);
            // Return response
            return response()->json([

                'default' => asset('storage/uploads/{$folderName}/' . $filenametostore),
                '500' => asset('storage/uploads/{$folderName}/thumbnail/' . $filenametostore),
                '1000' => asset('storage/uploads/{$folderName}/preview/' . $filenametostore),
            ]);
        }

        // Handle case where no file is uploaded
        return response()->json(['error' => 'No file uploaded'], 400);
    }
}

How can I accomplish this?

0 likes
2 replies
LaryAI's avatar
Level 58

To accomplish passing the folder name from your JavaScript component to your Laravel controller and using it to store images in the specified folder, you need to make sure that the folder name is being sent correctly in the request and that your controller is handling it properly. Here's how you can do it:

  1. Ensure that the folder name is being appended to the FormData object in your JavaScript component, which you are already doing with the line data.append('folderName', this.folderName);.

  2. In your Laravel controller, you need to replace the single quotes with double quotes when using the variable in the string to allow for variable interpolation. PHP will not parse variables inside single-quoted strings, so {$folderName} will not be replaced with the actual folder name.

Here's the updated upload method in your ImageUploadController:

class ImageUploadController extends Controller
{
    public function upload(Request $request)
    {
        // Validate the uploaded file
        $request->validate([
            'upload' => 'required|image|mimes:jpeg,png,jpg,gif|max:10000', // Example validation rules
            'folderName' => 'required|string' // Validate that folderName is provided and is a string
        ]);

        if ($request->hasFile('upload')) {
            $folderName = $request->input('folderName'); // Retrieve the folderName from the request
            $uploadedFile = $request->file('upload');

            // Generate a unique filename
            $filename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
            $extension = $uploadedFile->getClientOriginalExtension();
            $filenametostore = $filename . '_' . time() . '.' . $extension;

            // Store the uploaded file
            $uploadedFile->storeAs("public/uploads/{$folderName}/full", $filenametostore);

            // Create and store thumbnail
            $thumbnail = Image::make($uploadedFile)->resize(500, 150, function ($constraint) {
                $constraint->aspectRatio();
            });
            $thumbnailPath = "public/uploads/{$folderName}/thumbnail/" . $filenametostore;
            Storage::put($thumbnailPath, (string)$thumbnail->encode());

            // Create and store Preview
            $preview = Image::make($uploadedFile)->resize(1000, null, function ($constraint) {
                $constraint->aspectRatio();
            });
            $previewPath = "public/uploads/{$folderName}/preview/" . $filenametostore;
            Storage::put($previewPath, (string)$preview->encode());

            Log::info("storage/uploads/{$folderName}/" . $filenametostore);
            // Return response
            return response()->json([
                'default' => asset("storage/uploads/{$folderName}/" . $filenametostore),
                '500' => asset("storage/uploads/{$folderName}/thumbnail/" . $filenametostore),
                '1000' => asset("storage/uploads/{$folderName}/preview/" . $filenametostore),
            ]);
        }

        // Handle case where no file is uploaded
        return response()->json(['error' => 'No file uploaded'], 400);
    }
}

Make sure to import the necessary classes at the top of your controller file:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Intervention\Image\Facades\Image;

With these changes, your ImageUploadController should now correctly use the folder name passed from the JavaScript component to store the images in the specified folder.

gych's avatar

In your controller upload method change your path strings, use double quotes instead of single quotes

Example

"public/uploads/$folderName/full";

Please or to participate in this conversation.