Laravel makes it straightforward to add a visual editor to a form using CKEditor, a popular JavaScript library. CKEditor is a free, open-source web editing tool with a wide range of features.
Installing CKEditor
Create your CKEditor build using this link: https://ckeditor.com/ckeditor-5/online-builder/. Download it and copy it into /public/build/ckeditor5.
You can now use CKEditor in views or forms. For example, create a form in resources/views/editor.blade.php:
<form method="POST" action="/your-route">
@csrf
<script src="{{asset('build/ckeditor5/build/ckeditor.js')}}"></script>
<script>
ClassicEditor
.create(document.querySelector('#editor'), {
simpleUpload: {
uploadUrl: '{{route('image.upload').'?_token='.csrf_token()}}',
},
mediaEmbed: {
previewsInData: true
}
})
.catch(error => {
console.error(error);
});
</script>
<textarea name="content" id="editor">{!! old('content') !!}</textarea>
<button type="submit">Submit</button>
</form>
The example uses {!! old('content') !!} to populate the editor with previously submitted content when available.
Remember to define a route and controller method to process the form submission.
Visitors filling out this form will now see CKEditor's visual editor, which lets them format their content.
Image upload controller
Create an image upload controller:
php artisan make:controller ImageUploadController
app/Http/Controllers/ImageUploadController.php
<?php
namespace App\Services;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\File;
use Intervention\Image\ImageManagerStatic;
use Mockery\Exception;
class ImageUploadService
{
public static function uploadImage(UploadedFile $file): array
{
try {
// Получаем оригинальное имя файла
$originName = $file->getClientOriginalName();
// Извлекаем имя файла без расширения
$fileName = pathinfo($originName, PATHINFO_FILENAME);
// Получаем расширение файла
$extension = $file->getClientOriginalExtension();
// Создаем уникальное имя файла на основе текущей даты и времени
$uniqueFileName = $fileName . date('YmdHisu');
// Генерируем хэш из уникального имени файла и берем первые 8 символов
$hash = md5($uniqueFileName);
// Формируем окончательное имя файла с добавлением хэша и расширения
$fileName = substr($hash, 0, 8) . '.' . $extension;
// Создаем путь для сохранения файла, основываясь на текущей дате
$currentPath = date('Ymd');
$directoryPath = 'media/' . $currentPath . '/';
// Открываем изображение с помощью Intervention Image
$img = ImageManagerStatic::make($file->getRealPath());
// Получаем ширину и высоту изображения
$width = $img->getWidth();
$height = $img->getHeight();
// Проверяем, нужно ли уменьшать изображение по ширине
if ($width > 1080) {
$img->resize(1080, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
}
// Проверяем, нужно ли уменьшать изображение по высоте
if ($height > 840) {
$img->resize(null, 840, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
}
if (!File::exists($directoryPath)) {
File::makeDirectory($directoryPath);
}
// Сохраняем измененное изображение
$img->save(public_path($directoryPath) . $fileName);
} catch (Exception $e) {
return [
'uploaded' => false,
];
}
// Формируем URL для доступа к загруженному файлу
$url = '/' . $directoryPath . $fileName;
// Возвращаем информацию о загруженном файле
return [
'fileName' => $fileName,
'uploaded' => true,
'url' => $url
];
}
public static function uploadImageFromUrl(string $imageUrl): array
{
// Загружаем изображение по URL
$file = file_get_contents($imageUrl);
// Создаем временный файл для сохранения изображения
$tempFilePath = tempnam(sys_get_temp_dir(), 'image');
file_put_contents($tempFilePath, $file);
// Создаем экземпляр UploadedFile с временным файлом
$uploadedFile = new UploadedFile(
$tempFilePath,
basename($imageUrl)
);
// Вызываем функцию uploadImage для загрузки изображения
return self::uploadImage($uploadedFile);
}
public static function downloadAndReplaceImages(string $htmlContent): string|null
{
return preg_replace_callback("/<(img|image)[\s]+[^>]*src=['\"]?([^'\"\s>]+)['\"]?[^>]*>/is", array('self', 'replaceCallbackImages'), $htmlContent);
}
public static function replaceCallbackImages($matches): string
{
$src = $matches[2];
$img = $matches[0];
// Проверяем, является ли URL внешним (не ведет на наш сайт)
if (!self::isLocalUrlImage($src)) {
$uploadedImage = ImageUploadService::uploadImageFromUrl($src);
if ($uploadedImage['uploaded']) {
$img = '<img src="' . $uploadedImage['url'] . '" >';
}
}
return $img;
}
private static function isLocalUrlImage($url): bool
{
return (parse_url($url, PHP_URL_HOST) == null);
}
}
Add these lines to web.php:
Route::post('admin/image/upload', [ImageUploadController::class, 'storeImage'])->middleware(['auth', 'verified'])->name('image.upload');
Install the library:
composer require intervention/image
Uploading images in CKEditor
Add the image upload URL to the CKEditor configuration:
ClassicEditor
.create(document.querySelector('#editor'), {
simpleUpload: {
uploadUrl: '{{route('image.upload').'?_token='.csrf_token()}}',
}
})
.catch(error => {
console.error(error);
});
The visual editor can now upload images.