Upload images in Ckeditor 5 with Laravel

David CarrDavid Carr
3 min read

CKeditor 5 out-of-the-box does not come with upload capabilities. Uploading is supported with its plugins, some are official paid plugins that require subscriptions. There are a few free options.

Base64 upload adapter

This plugin allows uploads that will convert an uploaded image into base64 data. Very easy to use but will require you to save the complete base64 code with the post, this can get long.

Docs

Simple image adapter

An image upload tool. It allows uploading images to an application running on your server using the XMLHttpRequest API with a minimal editor configuration.

Docs

Use the online builder to add the simple image adapter then download the generated bundle unzip and place the folder inside a publicly accessible place in Laravel. such as /public/js/ckeditor5

Then link ckeditor.js in the pages you want to use Ckeditor

<script src="/js/ckeditor5/build/ckeditor.js"></script>

Using Ckeditor in a textarea.

I've made a blade component called Ckeditor so I can use:

<x-form.ckeditor id="content" wire:model="content" name="content" />

To render the editor. I'm using Livewire and AlpineJS.

The component looks like this:

@props([
    'name' => '',
    'label' => '',
    'required' => false
])

@if ($label == '')
    @php
        //remove underscores from name
        $label = str_replace('_', ' ', $name);
        //detect subsequent letters starting with a capital
        $label = preg_split('/(?=[A-Z])/', $label);
        //display capital words with a space
        $label = implode(' ', $label);
        //uppercase first letter and lower the rest of a word
        $label = ucwords(strtolower($label));
    @endphp
@endif
<div wire:ignore class="mt-5">
    @if ($label !='none')
        <label for="{{ $name }}" class="block text-sm font-medium leading-5 text-gray-700 dark:text-gray-200">{{ $label }} @if ($required != '') <span class="text-red-600">*</span>@endif</label>
    @endif
    <textarea
        x-data
        x-init="
            ClassicEditor
                .create($refs.item, {
                simpleUpload: {
                    uploadUrl: '{{ url('admin/image-upload') }}'
                }
                })
                .then(editor => {
                    editor.model.document.on('change:data', () => {
                    @this.set('{{ $name }}', editor.getData());
                    })
               })
                .catch(error => {
                    console.error(error);
                });
        "
        x-ref="item"
        {{ $attributes }}
    >
        {{ $slot }}
    </textarea>
</div>
@error($name)
    <p class="error">{{ $message }}</p>
@enderror

the important part is:

simpleUpload: {
    uploadUrl: '{{ url('admin/image-upload') }}'
}

This tells Ckeditor where to upload files to.

My route is defined inside an auth group so you have to be authenticated in order to use the upload route.

Route::middleware(['web', 'auth'])->group(function () {
    //other routes
    Route::post('admin/image-upload', [UploadController::class, 'index']);
});

Next create an UploadController:

<?php namespace App\Http\Controllers;

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

class UploadController
{
    public function index(Request $request)
    {
        if ($request->hasFile('upload')) {

            $file = $request->file('upload');
            $name = $file->getClientOriginalName();
            $name = Str::slug($name);
            $img  = Image::make($file);
            $img->stream();
            $name = str_replace('png', '', $name).'.png';

            Storage::disk('images')->put('posts/'.$name, $img);

            return response()->json([
                'url' => "/images/posts/$name"
            ]);

        }
    }
}

First, check if there is a file request.

Next, collect to the file, and define its file name.

$file = $request->file('upload');
$name = $file->getClientOriginalName();

I'll use a slug to rename the file name.

$name = Str::slug($name);

Next, I use Intervention package to use its Image class to make the image and stream it.

$img = Image::make($file);
$img->stream();

Next remove any unwanted text from the file name

$name = str_replace('png', '', $name).'.png';

Then save the image

Storage::disk('images')->put('posts/'.$name, $img);

Finally, return the image path so Ckeditor can load the image into the textarea

return response()->json([ 'url' => "/images/posts/$name" ]);
1
Subscribe to my newsletter

Read articles from David Carr directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

David Carr
David Carr

Blogger at http://dcblog.dev.