Интеграция TinyMCE 6 в панель управления Laravel Orchid

0f6608395d0f4913d53d4558ab9509e0a781c5b0
В статье рассмотрен процесс интеграции визуального редактора TinyMCE в панель управления Laravel Orchid. Данный инструмент намного функциональнее предлагаемого по умолчанию редактора Quill. Он сможет улучшить и облегчить вашу работу с текстовым контентом в админке.

Оглавление


TinyMCE – это мощный и гибкий редактор для создания и редактирования контента на веб-страницах. Он предлагает широкий набор инструментов и настроек, которые позволяют разработчикам адаптировать его под свои нужды. Здесь мы рассмотрим добавление TinyMCE в панель управления Laravel Orchid.

Laravel Orchid – это современный, быстрый и гибкий фреймворк для разработки панелей управления и административных интерфейсов. Он предоставляет разработчикам множество полезных инструментов и возможностей, среди которых поддержка различных типов полей, система разграничения прав доступа и другое. Интеграция TinyMCE позволит вам расширить функционал панели управления, предоставив пользователям удобный инструмент для работы с контентом.

Подготовка

В первую очередь нам понадобятся скрипты самого редактора. Их можно скачать с официального сайта или через пакетный менеджер. Скачав скрипты, нужно скопировать их в публичную директорию вашего сайта. Я предлагаю разместить их в public/static/tinymce.

После копирования подключите основной скрипт tinymce.min.js в админку Orchid. Для этого достаточно добавить ссылку на файл в конфиг config/platform.php:

return [
    // ...

    'resource' => [
        'stylesheets' => [ ],
        'scripts'     => [
            '/static/tinymce/tinymce.min.js',
        ],
    ],

    // ...
];

Также нужно настроить сборку пользовательских скриптов админки. 

Последние версии Laravel поставляются с настроенным конфигом для сборки фронта с помощью Vite, а Orchid предлагает возможность добавлять пользовательские сборки без необходимости изменять свои шаблоны.

Я предлагаю создать отдельную директорию для файлов админки по пути resources/admin/js. Здесь можно позже располагать все необходимые для админки скрипты, а сейчас создайте основной файл, который будет служить точкой входа: resources/admin/js/dashboard.js. Пока его содержимое будет таким:

console.log('My dashboard');

Добавьте этот файл в конфигурацию сборки Vite (файл vite.config.js).

import laravel from 'laravel-vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    laravel({
      input  : [
        'resources/sass/app.scss',
        'resources/js/app.js',

        // Добавим свой файл для админки
        'resources/admin/js/dashboard.js',
      ],
      refresh: true,
    }),
  ],
});

А также в конфиг Orchid (config/platform.php)

return [
    'vite' => [
        'resources/admin/js/dashboard.js',
    ],
];

Выполните в терминале команду

npm run build

и после обновления любой страницы админки, вы увидите в консоли браузера сообщение "My dashboard". Значит всё собралось и подключилось правильно.

Пользовательское поле

Теперь нужно создать пользовательское поле для использования в панели управления. Для этого нам потребуется написать новый php класс, расширяющий класс Orchid\Screen\Field, blade-шаблон элемента управления и javascript контроллер, инициализирующий TinyMCE.

PHP класс

Чтобы не писать вручную много кода, можно взять уже готовый класс Orchid\Screen\Fields\Quill и немного изменить его. Создайте новый файл app/Orchid/Fields/TinyMCE.php и вставьте в него следующее содержимое:

<?php

namespace App\Orchid\Fields;

use Orchid\Screen\Field;

/**
 * Class TinyMCE
 * @method TinyMCE name(string $value = null)
 * @method TinyMCE value($value = true)
 * @method TinyMCE help(string $value = null)
 * @method TinyMCE title(string $value = null)
 */
class TinyMCE extends Field
{
    protected $view = 'orchid.fields.tinymce';

    /**
     * All attributes that are available to the field.
     */
    protected $attributes = [
        'value'               => null,
        'class'               => 'textarea-tinymce',
        'data-tinymce-target' => 'textarea',
    ];

    /**
     * Attributes available for a particular tag.
     */
    protected $inlineAttributes = [
        'accesskey',
        'autofocus',
        'cols',
        'disabled',
        'form',
        'maxlength',
        'name',
        'placeholder',
        'readonly',
        'required',
        'rows',
        'tabindex',
        'wrap',
    ];
}

Здесь мы изменили шаблон в свойстве $view для вывода в админке и набор атрибутов – добавили css-класс и data-атрибут, необходимый для контроллера.

Blade-шаблон

Шаблон для вывода данного элемента, как указано выше, будет располагаться в директории resources/views/orchid/fields и называться tinymce.blade.php. Его содержимое также будет на основе шаблона для встроенного редактора Quill:

@component($typeForm, get_defined_vars())
    <div
        id="tinymce-wrapper-{{ $id }}"
        data-controller="tinymce"
        data-turbo-temporary
    >
        <textarea {{ $attributes }}>{{ $value ?? '' }}</textarea>
    </div>
@endcomponent

Javascript-контроллер

Фронтэнд панели управления Orchid построен на связке фреймворков Hotwire Turbo и Hotwire Stimulus. Первый предоставляет некое подобие SPA, а второй – компонентный подход и управление элементами на странице. Соответственно, каждый элемент управления в админке управляется контроллером Stimulus. Выше, в коде шаблона, мы указали атрибут data-controller="tinymce", указывающий, что наш редактор будет управляться контроллером с именем tinymce.

Создайте файл resources/admin/js/controllers/tinymce_controller.js.

export default class extends Controller {

    static targets = [
        'textarea',
    ];

    connect() {
        tinymce.init({
            target: this.textareaTarget,
        });
    }

    disconnect() {
        tinymce.remove();
    }
}

Теперь подключите этот констроллер в файле resources/admin/js/dashboard.js.

import TinyMCEController from './controllers/tinymce_controller.js';

window.application.register('tinymce', TinyMCEController);

Осталось выполнить сборку npm run build и новый редактор можно использовать в админке как и любое другое поле.

Пример использования:

Layout::rows([
    Input::make('article.title')->title('Заголовок')->required(),
    TinyMCE::make('article.content')->title('Содержание'),
]),

Загрузка изображений

По умолчанию TinyMCE сохраняет вставляемые изображения прямо в содержимое в виде base64. Чаще всего такой вариант не годится. Нужно сделать так, чтобы картинки сохранялись в хранилище в виде файлов, используя при этом штатную модель Orchid\Attachment\Models\Attachment.

Создайте контроллер app/Orchid/Controllers/UploadController.php, который будет обрабатывать загрузку изображений. Метод загрузки должен возвращать json-объект с ключом location, содержащий url загруженного файла.

namespace App\Orchid\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Routing\Controller;
use Orchid\Attachment\File;

class UploadController extends Controller
{
    public function upload(Request $request): JsonResponse
    {
        /** @var \Orchid\Attachment\Models\Attachment $attachment */
        $attachment = collect($request->allFiles())
            ->flatten()
            ->map(fn(UploadedFile $file) => resolve(File::class, [
                'file'  => $file,
                'disk'  => 'public',
                'group' => 'content',
            ])->load())
            ->first();

        return response()->json([
            'location' => $attachment->url(),
        ]);
    }
}

Теперь нужно определить маршрут, по которому будет осуществляться загрузка. Добавьте в файл routes/platform.php его определение:

use App\Http\Middleware\VerifyCsrfToken;
use App\Orchid\Controllers\UploadController;

Route::post('upload', [UploadController::class, 'upload'])
    ->name('image.upload')
    ->withoutMiddleware(VerifyCsrfToken::class);

Для этого маршрута мы отключили проверку csrf-токена, так как не собираемся писать собственный загрузчик, а просто укажем url загрузки в конфигурации TinyMCE. Обновите конфигурацию:

tinymce.init({
    target: this.textareaTarget,

    plugins: [
        'image',
    ],

    toolbar: 'undo redo | styles | bold italic | link image',

    relative_urls     : false,
    remove_script_host: true,

    images_upload_url: '/admin/upload',
    image_dimensions : false,
    images_file_types: 'jpeg,jpg,png,gif,svg,webp',
});

Здесь мы добавили плагин для вставки изображений и соответствующую кнопку на панель редактора, а также указали адрес для сохранения файлов.

Опция relative_urls: false предписывает генерировать абсолютные url к изображениям, а remove_script_host: true – удалять из url имя хоста.

Не забудьте пересобрать код для админки.

Решение проблем

В процессе использования выяснилось, что инициализация редактора не происходит, если повторно зайти на страницу, на которой мы уже были. В этом случае вместо редактора отображается простое поле textarea. Для решения этой проблемы нужно добавить атрибут data-turbo-temporary в шаблон редактора (это уже сделано в коде, представленном выше). А также отключить кэширование в Turbo. Для этого нужно установить параметр platform.turbo.cache в значение false.

'turbo' => [
    'cache' => false,
],