Пример плагина «Отзывы»
Рабочий плагин к руководству «Плагины». Что в нём показано:
| Что | Где |
|---|---|
| описание плагина, автозагрузка классов | plugin.json |
| подписка на события из класса | plugin.php, src/Reviews.php → register() |
| свой тип записей с меню в админке | registerTypes() → register_post_type('review', …) |
| свой блок для редактора | registerTypes() → register_block_type('otzyvy-list', …), шаблон views/blocks/list.blade.php |
| поля в редакторе записи (метабокс) и их сохранение | metaBoxes(), views/meta-box.blade.php, save() |
| стили на сайте | styles() → plugin_asset(), assets/otzyvy.css |
| действие при включении | register_activation_hook() → activate() |
| уборка при удалении | uninstall.php |
Как попробовать
- Скопируйте папку
otzyvyвplugins/(или запакуйте её в zip и загрузите в «Плагины → Добавить плагин»). - Включите плагин «Отзывы» — в меню появится раздел «Отзывы».
- Добавьте пару отзывов с оценкой, затем в любой странице добавьте блок «Отзывы».
Файлы примера
plugin.json
{
"name": "Отзывы",
"description": "Пример плагина из документации: свой тип записей «Отзывы» с оценкой и блок «Отзывы» для страниц.",
"version": "1.0.0",
"author": "CMS ARGELIT",
"uri": "https://cms.argelit.com",
"requires_cms": "0.13",
"requires_php": "8.3",
"autoload": {
"Examples\\Otzyvy\\": "src"
}
}
plugin.php
<?php
/*
* «Отзывы» — пример плагина из документации CMS ARGELIT (docs/developers/plugins.md).
*
* Этот файл подключается на каждом запросе, пока плагин включён. Вся работа — в классе src/Reviews.php
* (он подключается сам, см. «autoload» в plugin.json). Переменная $plugin — описание плагина из plugin.json.
*/
use Examples\Otzyvy\Reviews;
(new Reviews($plugin))->register();
assets/otzyvy.css
/* Блок «Отзывы». Цвета и скругления берутся из переменных темы, если они есть. */
.otzyvy {
margin: 2em 0;
}
.otzyvy__title {
margin-bottom: 0.75em;
}
.otzyvy__list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}
.otzyvy__item {
margin: 0;
padding: 20px;
border-radius: var(--radius, 12px);
background: var(--surface, #F2F4F7);
}
.otzyvy__stars {
margin: 0 0 8px;
color: #f5a623;
letter-spacing: 2px;
}
.otzyvy__text {
margin: 0;
padding: 0;
border: 0;
font-style: normal;
}
.otzyvy__text > :first-child {
margin-top: 0;
}
.otzyvy__author {
margin-top: 12px;
color: var(--muted, #5e6670);
font-size: 0.9em;
}
src/Reviews.php
<?php
namespace Examples\Otzyvy;
use Cms\Admin\MetaBoxes;
use Cms\Content\PostType;
use Cms\Models\Post;
use Cms\Plugins\Plugin;
use Illuminate\Http\Request;
/**
* Плагин «Отзывы»: тип записей «review», поля «Оценка», «Имя клиента», «Город» в редакторе
* и блок «Отзывы», который выводит последние отзывы на любой странице.
*/
class Reviews
{
/** Метаполя отзыва. Подчёркивание в начале — служебное поле: его не видно в блоке «Произвольные поля». */
public const META = ['rating' => '_otzyvy_rating', 'author' => '_otzyvy_author', 'city' => '_otzyvy_city'];
public function __construct(protected Plugin $plugin) {}
/**
* Подписаться на события CMS. Обработчики — методы этого класса.
*/
public function register(): void
{
register_activation_hook($this->plugin, [$this, 'activate']);
add_action('init', [$this, 'registerTypes']); // тип записей и блок — в «init»
add_action('add_meta_boxes', [$this, 'metaBoxes'], 10, 3); // поля в редакторе отзыва
add_action('save_post_review', [$this, 'save'], 10, 2); // сохранить их вместе с отзывом
add_action('site_head', [$this, 'styles']); // стили блока на сайте
}
/**
* При включении плагина: запомнить версию (пригодится, если в следующих версиях понадобится что-то перенести).
*/
public function activate(): void
{
update_option('otzyvy_version', $this->plugin->version);
}
public function registerTypes(): void
{
register_post_type('review', [
'labels' => [
'name' => 'Отзывы',
'singular_name' => 'Отзыв',
'all_items' => 'Все отзывы',
'add_new' => 'Добавить отзыв',
'add_new_item' => 'Новый отзыв',
'edit_item' => 'Редактировать отзыв',
'count' => 'отзыв|отзыва|отзывов',
],
'public' => false, // отдельных страниц у отзывов нет — их показывает блок
'supports' => ['title', 'editor'],
'menu_icon' => 'star',
'menu_position' => 27,
'admin_slug' => 'reviews', // адрес в админке: /admin/reviews
]);
register_block_type('otzyvy-list', [
'title' => 'Отзывы',
'icon' => 'star',
'category' => 'design',
'description' => 'Последние отзывы клиентов с оценкой.',
'keywords' => ['отзывы', 'рейтинг', 'клиенты'],
'fields' => [
'heading' => ['type' => 'text', 'label' => 'Заголовок', 'default' => 'Отзывы наших клиентов'],
'number' => ['type' => 'number', 'label' => 'Сколько показать', 'min' => 1, 'max' => 12, 'default' => 3],
'show_rating' => ['type' => 'checkbox', 'label' => 'Показывать оценку', 'default' => true],
],
'view' => $this->plugin->slug.'::blocks.list',
]);
}
/**
* Событие «add_meta_boxes» срабатывает для каждой записи — блок добавляем только отзывам.
*/
public function metaBoxes(PostType $postType, Post $post, MetaBoxes $boxes): void
{
if ($postType->name === 'review') {
add_meta_box('otzyvy-details', 'Оценка и автор', $this->plugin->slug.'::meta-box', 'review', 'side', 5);
}
}
/**
* Сохранить поля из блока «Оценка и автор». Права на редактирование отзыва CMS уже проверила.
*/
public function save(Post $post, Request $request): void
{
if (! $request->has('otzyvy')) {
return; // блок полей убрали — не трогаем
}
$data = $request->validate([
'otzyvy.rating' => ['required', 'integer', 'between:1,5'],
'otzyvy.author' => ['nullable', 'string', 'max:100'],
'otzyvy.city' => ['nullable', 'string', 'max:100'],
])['otzyvy'];
update_post_meta($post, self::META['rating'], (int) $data['rating']);
update_post_meta($post, self::META['author'], trim((string) ($data['author'] ?? '')));
update_post_meta($post, self::META['city'], trim((string) ($data['city'] ?? '')));
}
public function styles(): void
{
echo '<link rel="stylesheet" href="'.e(plugin_asset($this->plugin->slug, 'otzyvy.css')).'">'.PHP_EOL;
}
/**
* Звёзды оценки для шаблона: «★★★★☆».
*/
public static function stars(int $rating): string
{
$rating = max(1, min(5, $rating));
return str_repeat('★', $rating).str_repeat('☆', 5 - $rating);
}
}
uninstall.php
<?php
/*
* Выполняется, когда плагин удаляют в разделе «Плагины» (при отключении — нет).
* Убираем всё, что плагин создал: отзывы (вместе с их полями) и настройку.
*/
use Cms\Models\Post;
Post::withTrashed()->where('type', 'review')->get()->each(fn (Post $review) => $review->forceDelete());
delete_option('otzyvy_version');
views/blocks/list.blade.php
{{-- Блок «Отзывы». Переменные: $block (поля блока), $post (запись, в которой стоит блок). --}}
@php
$reviews = get_posts(['type' => 'review', 'number' => (int) $block['number']]);
$meta = \Examples\Otzyvy\Reviews::META;
@endphp
@if ($reviews->isNotEmpty())
<section class="block otzyvy">
@if ($block['heading'] !== '')
<h2 class="otzyvy__title">{{ $block['heading'] }}</h2>
@endif
<div class="otzyvy__list">
@foreach ($reviews as $review)
<figure class="otzyvy__item">
@if ($block['show_rating'])
<p class="otzyvy__stars" aria-label="Оценка {{ (int) $review->getMeta($meta['rating'], 5) }} из 5">{{ \Examples\Otzyvy\Reviews::stars((int) $review->getMeta($meta['rating'], 5)) }}</p>
@endif
<blockquote class="otzyvy__text">{!! $review->contentHtml() !!}</blockquote>
<figcaption class="otzyvy__author">
{{ $review->getMeta($meta['author']) ?: $review->displayTitle() }}@if ($review->getMeta($meta['city'])), {{ $review->getMeta($meta['city']) }}@endif
</figcaption>
</figure>
@endforeach
</div>
</section>
@endif
views/meta-box.blade.php
{{-- Блок «Оценка и автор» в редакторе отзыва. Переменные: $post, $postType. --}}
@php
$meta = \Examples\Otzyvy\Reviews::META;
$rating = (int) old('otzyvy.rating', $post->getMeta($meta['rating'], 5));
@endphp
<p>
<label for="otzyvy-rating">Оценка</label><br>
<select id="otzyvy-rating" name="otzyvy[rating]">
@foreach ([5, 4, 3, 2, 1] as $value)
<option value="{{ $value }}" @selected($rating === $value)>{{ \Examples\Otzyvy\Reviews::stars($value) }} — {{ $value }}</option>
@endforeach
</select>
</p>
<p>
<label for="otzyvy-author">Имя клиента</label>
<input type="text" id="otzyvy-author" name="otzyvy[author]" class="widefat" maxlength="100"
value="{{ old('otzyvy.author', $post->getMeta($meta['author'])) }}">
</p>
<p>
<label for="otzyvy-city">Город</label>
<input type="text" id="otzyvy-city" name="otzyvy[city]" class="widefat" maxlength="100"
value="{{ old('otzyvy.city', $post->getMeta($meta['city'])) }}">
</p>