akiraz2 / editor-js
PHP Editor JS with Parser
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:package
Requires
- ezyang/htmlpurifier: ^4.18
- spatie/laravel-package-tools: ^1.4
This package is auto-updated.
Last update: 2025-09-01 19:01:18 UTC
README
Визуальный Редактор контента https://editorjs.io (весь контент сохраняет в виде json структуры, чтобы любой мог по своим правилам перевести в html-код)
Использовал за основу https://github.com/editor-js/editorjs-php - сделал исправленное рабочее решение.
Добавил Parser - это компонент, который парсит сохраненную json-структуру контента и трансформирует в HTML-верстку.
Легкая интеграция в Laravel проект
Используются все стандартные компоненты editor-js (тексты, заголовки, списки, картинки, таблицы и пр)
Install
composer require akiraz2/editor-js
Используется конфиг-файл (config/editor.php) для валидации и очистки входящих данных (htmlpurifier).
Вы можете изменять конфиг, добавлять в него новые блоки.
Если устанавливаете в проектах Laravel, то можно конфиг скопировать в проект с помощью команды php artisan vendor:publish --tag=editor-js
или просто скопировать в свое место и использовать его в вызове
Если используете сборщики фронтенда, то добавьте npm-пакеты в package.json (или лучше командами npm install)
"@editorjs/editorjs": "2.30.0",
"@editorjs/embed": "^2.7.6",
"@editorjs/header": "^2.8.8",
"@editorjs/image": "^2.10.3",
"@editorjs/link": "^2.6.2",
"@editorjs/list": "^2.0.8",
"@editorjs/marker": "^1.4.0",
"@editorjs/quote": "^2.7.6",
"@editorjs/table": "^2.4.5",
В resources/js/editorjsEmbed.js
лежит рабочий вариант кода "встраивание Youtube", скопируйте себе в проект, если нужно.
Guide
Сначала действуем как на официальном сайте.
- Добавляем на страницу блок-контейнер для редактора
<div id="editorjs" class="edit-xxx__content"></div>
- На странице подключаем фронтенд-скрипт (в примере используется vite сборщик из Laravel). Для передачи информации с PHP-бекенда (Laravel) используется объект window.content
<script>
window.content = {!! json_encode($modelXxx->content) !!};
</script>
@vite('resources/js/frontend/editXxx.js')
- Фронтенд JS код - ниже код для примера - используется сборщик Vite из состава Laravel
import EditorJS from '@editorjs/editorjs'; import Header from '@editorjs/header'; import List from '@editorjs/list'; import Link from '@editorjs/link'; import Marker from '@editorjs/marker'; import ImageTool from '@editorjs/image'; import Quote from '@editorjs/quote'; import Table from '@editorjs/table'; import {createFetchPostData, noReturn, notify, swalErr} from "@/frontend/helpers/http.js"; import Embed from "./helpers/editorjsEmbed.js"; const url = window.location.pathname; //это для компонента загрузки изображений - может быть любой базовый URL window.csrf = $('[name=csrf-token]').attr('content');//это необязательно. у меня это глобальный объект, установленный совершенно в другом месте window.editor = new EditorJS({ /** * Id of Element that should contain the Editor */ holder: 'editorjs', placeholder: 'Let`s write an awesome Xxx!', /** * Available Tools list. * Pass Tool's class or Settings object for each Tool you want to use */ tools: { header: Header, list: List, marker: Marker, embed: Embed, //link: Link, image: { class: ImageTool, config: { additionalRequestData: {_token: window.csrf},//это не обязательно, но в проектах Laravel используется для безопасности endpoints: { byFile: `${url}/image/upload`, // Your backend file uploader endpoint byUrl: `${url}/image/fetchUrl`, // это не использовал } } }, quote: Quote, table: Table }, data: window.content })
Собираем фронтенд, в Laravel это команда npm run build
Проверяем.
- Кнопка сохранения контента
<button type="button" class="btn btn-wide btn-primary" id="edit-XXX-form-save">Save</button>
Также во фронтенд-скрипте добавляем функционал сохранения.
Для примера:
$('#edit-XXX-form-save').click(function (e) { e.preventDefault(); window.editor.save().then(async (outputData) => { $('#svg-loading').css('display', 'flex');//optional. анимация загрузки (экран блокирую чтобы не накликали лишнего) //формируем объект для передачи на бекенд, это для примера, возможно вам нужно добавить csrf-токен const form = { online: $('input[name=online]').is(':checked') ? 1 : 0, status: $('input[name=status]').is(':checked') ? 1 : 0, content: outputData, } await fetch(url, createFetchPostData(form)).then(noReturn) .then((data) => { $('#svg-loading').hide();//разблокируем экран }) .then(notify) //optional, функция уведомляет об успешности .catch(swalErr);//тут лучше обязательно сделать функцию, уведомляющую об ошибке (а она может случиться) }).catch((error) => { console.log('Saving failed: ', error) swalErr(error) }); })
Примеры ниже - всего лишь примеры для полноценного рабочего функционала, вы вправе писать свои лучшие проектные решения
import notifier from 'codex-notifier'; //это для уведомления об успешности (маленькое облачко в углу экрана) "codex-notifier": "^1.1.2" export function createFetchPostData(form) { const formData = {}; for (const key in form) { formData[key] = form[key]; } formData['_token'] = window.csrf; const options = createPostHeader(formData); return { method: 'POST', body: JSON.stringify(formData), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }; } export function createPostHeader(formData) { return { method: 'POST', body: JSON.stringify(formData), headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }; } export async function noReturn(response) { if (response.ok) { return await response.json(); } //это для ошибок валидации от Laravel if (response.status === 422) { const json = await response.json() throw new Error(json.message); } //надеюсь ваш бекенд передает ошибку throw new Error(response.status + ' ' + response.statusText); } export async function notify(json) { notifier.show({ message: 'Saved', style: 'success', time: 2500 }) } //использовал "sweetalert2-neutral": "^11.18.0-neutral" export function swalErr(error) { Swal.fire({icon: "error", text: error}); }
- Бекенд-часть
Для примера - Сохранение в Laravel:
public function save(Request $request, StaticPage $page) { $validated = $request->validate([ 'content' => ['required', 'array'], ]); //это для валидации и очистки входящих данных $blocks = []; try { // Initialize Editor backend and validate structure $editor = new EditorJS($validated['content'], config('editor.config')); // Get sanitized blocks (according to the rules from configuration) $blocks = $editor->getBlocks(); } catch (EditorJSException $e) { //здесь я делал код для вывода эксепшна пользователям } $validated['content']['blocks'] = $blocks; $page->updateOrFail($validated); return [];//возвращать что-либо не обязательно в случае успешности операции, но вы всегда можете добавить свои структуры и на фронте распарсить :) }
Для примера - Генерация HTML в Laravel:
public function view(StaticPage $page) { //optional - выкидываем исключение если вдруг все блоки пустые throw_if(!isset($page->content['blocks']) || !count($page->content['blocks']), new SimpleException('Page is not ready')); //а это наше главное - парсим json структуру и превращает в HTML-верстку $html = Parser::parse(json_encode($page->content, JSON_UNESCAPED_UNICODE))->toHtml(); //дальше передаем куда нужно или можете сохранить верстку в отдельный файл return view("pages.view", ['page' => $page, 'html' => $html]); }
Custom
Зачастую решение нужно дорабатывать своими нестандартными блоками.
Новые Фронтенд-компоненты вы можете писать в своих проектах самостоятельно - заглядывайте в официальную документацию https://editorjs.io/the-first-plugin/
Все кастомные блоки опишите в конфиге.
Парсер придется дорабатывать, унаследуйте от базового и допишите нужный функционал (добавьте новый блок в init() и новый соответствующий метод)