CKEditor 5

CKEditor 5 là một trình soạn thảo văn bản phong phú JavaScript với nhiều tính năng và khả năng tùy chỉnh.

CKEditor 5 có kiến trúc MVC hiện đại, mô hình dữ liệu tùy chỉnh và DOM ảo, mang lại hiệu suất và khả năng mở rộng vượt trội.

Tích hợp gốc với Angular, React và Vue.js có sẵn để thuận tiện cho bạn cũng như tương thích với Electron và các thiết bị di động (Android, iOS).

Simple Upload Adapter

Simple Upload Adapter là một plugin của CKEditor 5, nó cho phép bạn có thể upload hình ảnh lên server thông qua XMLHttpRequest.

Đây là một giải pháp đơn giản, dễ dàng triển khai để thực hiện việc upload hình ảnh lên server.

Thực hiện Laravel CKEditor Image Upload

Trước khi, bắt đầu thực hiện tính năng nay, bạn cần phải tích hợp CKEditor vào Laravel bằng Laravel Vite.

Nếu bạn chưa thực hiện thì có thể tham khảo lại bài viết Integrating CKEditor 5 In Laravel 10 Using Vite.

Laravel API Upload Images

Trước khi, chúng ta tích hợp tính năng upload hình ảnh vào CKEditor 5, chúng ta cần phải chuẩn bị API upload hình ảnh bằng Laravel.

Đầu tiên, chúng ta sẽ tạo Form Request để kiểm tra và xác thực tính hợp lệ của dữ liệu đầu vào bằng lệnh sau:

php artisan make:request UploadImageRequest

Sau khi chạy lệnh trên xong, chúng ta hãy chỉnh sửa tập tin UploadImageRequest.php nằm trong thư mục /app/Http/Requests  với nội dung như sau:

<?php

namespace App\Http\Requests;

use Illuminate\Http\JsonResponse;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator as ValidatorContract;

class UploadImageRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'upload' => 'bail|required|image|mimes:jpeg,png|mimetypes:image/jpeg,image/png|extensions:jpg,png|max:2048',
        ];
    }

    /**
     * Handle a failed validation attempt.
     *
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     * @return void
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function failedValidation(ValidatorContract $validator)
    {
        $errors = (new ValidationException($validator))->errors();

        foreach ($errors as $key => $value) {
            $errors[$key] = reset($value);
        }

        $requests = [
            "error"  => [
                'message' => $errors['upload'] ?? ''
            ],
        ];
        throw new HttpResponseException(response()->json($requests, JsonResponse::HTTP_BAD_REQUEST));
    }
}

Trong bài viết này, tôi sử dụng storage của Laravel, do đó cần phải tạo symbolic link từ thư mục public/storage đến storage/app/public bằng lệnh sau:

php artisan storage:link

Để xử lý upload hình ảnh, chúng ta cần tạo controller để thực hiện nhiệm vụ này bằng lệnh sau:

php artisan make:controller UploadController

Sau khi chạy lệnh trên xong, chúng ta hãy chỉnh sửa tập tin UploadController.php nằm trong thư mục /app/Http/Controllers  với nội dung như sau:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Storage;
use App\Http\Requests\UploadImageRequest;
use Illuminate\Http\Request;

class UploadController extends Controller
{
    public function uploadImage(UploadImageRequest $request)
    {
        try {
            $name = $this->_getNameFile() . '.' .$request->upload->getClientOriginalExtension();
            return response()->json([
                'url' => Storage::disk('public')->url($request->upload->storeAs('images', $name, 'public'))
            ]);
        } catch (\Throwable $th) {
            report($th);
            return response()->json([
                'error' => [
                    'message' => 'HTTP/1.1 500 Server Error.'
                ]
            ], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Generates a unique name for a file.
     *
     * @return string
     */
    private function _getNameFile()
    {
        # Generate a unique name using MD5 and timestamp
        $md5 = strtoupper(md5(uniqid() . microtime()));
        return substr($md5, 0, 8) . '-' . substr($md5, 8, 4) . '-' . substr($md5, 12, 4) . '-' . substr($md5, 16, 4) . '-' . substr($md5, 20);
    }
}

Cuối cùng, chúng ta hãy thiết lập route cho hàm uploadImage trong UploadController.php ở phía trên trong tập tin /routes/web.php như sau:

<?php

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

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::post('images/upload', [UploadController::class, 'uploadImage']);

CKEditor Upload Images

Đầu tiên, chúng ta cần phải cài đặt package @ckeditor/ckeditor5-image bằng lệnh sau:

npm install --save @ckeditor/ckeditor5-image

Tiếp theo, chúng ta tiếp tục cài đặt package @ckeditor/ckeditor5-upload bằng lệnh sau:

npm install --save @ckeditor/ckeditor5-upload

Sau đó, chúng ta sẽ thêm plugin SimpleUploadAdapter vào danh sách plugin và cấu hình tính năng như sau:

import { ClassicEditor as ClassicEditorBase } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { SimpleUploadAdapter } from '@ckeditor/ckeditor5-upload';
import { Image, ImageUpload } from '@ckeditor/ckeditor5-image';

export default class ClassicEditor extends ClassicEditorBase {}

ClassicEditor.builtinPlugins = [
    Essentials,
    Autoformat,
    Bold,
    Italic,
    BlockQuote,
    Heading,
    Link,
    List,
    Paragraph,
    Image,
    ImageUpload,
    SimpleUploadAdapter,
];

ClassicEditor.defaultConfig = {
    toolbar: {
        items: [
            'heading',
            '|',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'blockQuote',
            'undo',
            'redo',
            'uploadImage',
        ]
    },
    language: 'en'
};


ClassicEditor
    // Note that you do not have to specify the plugin and toolbar configuration — using defaults from the build.
    .create( document.querySelector( '#editor' ), {
        simpleUpload: {
            // The URL that the images are uploaded to.
            uploadUrl: '/images/upload',

            // Enable the XMLHttpRequest.withCredentials property.
            withCredentials: true,

            // Headers sent along with the XMLHttpRequest to the upload server.
            headers: {
                'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
            }
        }
    })
    .then( editor => {
        console.log( 'Editor was initialized', editor );
    } )
    .catch( error => {
        console.error( error.stack );
    } );

Cuối cùng, chúng ta hãy mở tập tin welcome.blade.php trong thư mục resources/views và thêm thẻ meta bên dưới vào thẻ head như sau:

<!DOCTYPE html>
<html lang="en">
<head>
    ...
    <meta name="csrf-token" content="{{ csrf_token() }}">
    ...
</head>
<body>
    ...
</body>
</html>

Kết quả của công việc bạn đã làm đang chờ bạn khám phá!

Sau khi đã hoàn thành các bước trên, giờ là lúc để chúng ta cùng nhau khám phá thành quả công sức của mình.

Hãy thực thi lệnh sau để tiến hành build CKEditor sử dụng Laravel Vite:

npm run build

Cuối cùng, chúng ta hãy mở trình duyệt lên và truy cập vào địa chỉ  http://127.0.0.1 để chiêm ngưỡng kết quả do chính bản thân chúng ta tạo ra 🤤🤤🤤🏆🍨🍨🍨.

CÓ THỂ BẠN QUAN TÂM

Integrating CKFinder with Amazon S3 in Laravel

Integrating CKFinder with Amazon S3 in Laravel

CKFinder 3 CKFinder 3 là trình quản lý tập tin được tích hợp với CKEditor 4 và CKEditor 5. Nó giúp bạn dễ dàng đưa các tập tin và hình ảnh vào nội dung của Editor một cách an toàn. Đây là một tín...

Laravel One to Many Eloquent Relationship

Laravel One to Many Eloquent Relationship

One to Many Relationship được sử dụng trong trường hợp một dữ liệu của một bảng được liên kết với một hoặc nhiều dữ liệu ở bảng khác. Ví dụ, một bài post có thể có nhiều comment. Vì vậy, trong hướn...

Cloudflare's Turnstile CAPTCHA in Laravel

Cloudflare's Turnstile CAPTCHA in Laravel

Ngày 28/09/2022, Cloudflare đã thông báo về phiên bản beta mở của Turnstile, một giải pháp thay thế vô hình cho CAPTCHA. Bất kỳ ai, ở bất kỳ đâu trên Internet muốn thay thế CAPTCHA trên trang web c...

Integrating elFinder into TinyMCE 6 in Laravel

Integrating elFinder into TinyMCE 6 in Laravel

TinyMCE TinyMCE là một trình soạn thảo WYSIWYG được xây dựng trên nền tảng Javascript, được phát triển dưới dạng mã nguồn mở theo giấy phép MIT bởi Tiny Technologies Inc. TinyMCE cho phép người d...

Encrypted HTTP Live Streaming with Laravel FFMpeg

Encrypted HTTP Live Streaming with Laravel FFMpeg

HTTP Live Streaming (HLS)  HTTP Live Streaming (HLS) là một trong những giao thức phát trực tuyến video được sử dụng rộng rãi nhất . Mặc dù nó được gọi là HTTP "live" streaming, nhưng nó được sử dụn...

How To Optimize Your Site With GZIP Compression

How To Optimize Your Site With GZIP Compression

GZIP là công nghệ nén thường được sử dụng để truyền dữ liệu một cách nhanh chóng qua Insternet. Hiện nay, GZIP là một tiêu chuẩn để nén các file trên trang web, đa số các website hiện nay hơn 99% sử d...

Laravel Many to Many Polymorphic Relationship

Laravel Many to Many Polymorphic Relationship

Many to many Polymorphic Relationship cũng hơi phức tạp để hiểu. Ví dụ: nếu bạn có bài post, video và tag, bạn cần kết nối với nhau theo yêu cầu là mọi bài đăng đều có nhiều tag và video cũng như vậy....

Laravel Socialite Login With Facebook

Laravel Socialite Login With Facebook

Ngoài xác thực dựa trên biểu mẫu điển hình, Laravel cũng cung cấp một cách đơn giản và thuận tiện để sử dụng Laravel Socialite để xác thực với các nhà cung cấp OAuth. Socialite hiện hỗ trợ xác thực qu...

How to Install Laravel on CentOS 6/7

How to Install Laravel on CentOS 6/7

Laravel là một PHP Framework mã nguồn mở miễn phí, được phát triển bởi Taylor Otwell với phiên bản đầu tiên được ra mắt vào 6/2011. Laravel ra đời nhằm mục đích phát triển ứng dụng web dựa trên mô hìn...

ManhDanBlogs