https://github.com/visavi/rotor-modules
https://github.com/visavi/rotor-modules
modules php rotor visavi wap
Last synced: 26 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/visavi/rotor-modules
- Owner: visavi
- Created: 2018-12-12T10:57:34.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2026-06-21T14:23:49.000Z (about 1 month ago)
- Last Synced: 2026-06-21T16:17:08.809Z (about 1 month ago)
- Topics: modules, php, rotor, visavi, wap
- Language: PHP
- Size: 9.89 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- Changelog: News/Http/Controllers/Admin/NewsController.php
Awesome Lists containing this project
README
# Модули для Rotor
Официальный репозиторий модулей для движка [Rotor](https://github.com/visavi/rotor).
## Установка
### Через каталог модулей (рекомендуется)
В админ-панели перейдите в **Модули → Каталог**. Все доступные модули из подключённых реестров отображаются там. Нажмите «Установить» — модуль будет скачан и распакован автоматически.
### Через ZIP-архив
В **Модули → Загрузить** можно установить модуль из ZIP-файла или по прямой ссылке.
### Вручную
Распакуйте модуль в директорию `/modules/ИмяМодуля`, после чего в админ-панели нажмите «Установить».
---
## Управление модулями
- **Установка** — выполняются миграции, создаются симлинки на статические файлы, подключаются настройки, хуки и маршруты
- **Отключение** — модуль становится недоступен, данные в БД сохраняются
- **Включение** — модуль возобновляет работу с теми же данными
- **Обновление** — если версия в `module.php` выше установленной, в админке появляется кнопка «Обновить»
- **Удаление** — откатываются миграции, удаляются симлинки и данные модуля
---
## Реестры модулей
Реестры — источники, из которых каталог берёт список доступных модулей. В **Модули → Реестры** можно добавить свой или сторонний реестр в формате JSON.
Официальный реестр этого репозитория:
```
https://github.com/visavi/rotor-modules/releases/download/registry/registry.json
```
Файл `registry.json` обновляется автоматически через GitHub Actions при каждом пуше в репозиторий.
---
## Структура модуля
```
MyModule/
├── module.php # обязательный — метаданные и возможности модуля
├── changelog.md # история изменений по версиям (для каталога и страницы модуля)
├── routes.php # маршруты веб и API
├── hooks.php # вставки в шаблоны через Hook::add и регистрации Registry/Restatement
├── helpers.php # глобальные вспомогательные функции
├── middleware.php # регистрация middleware (алиасы и/или группа web)
├── config.php # конфигурация модуля (config('MyModule.key'))
├── Http/
│ ├── Controllers/ # контроллеры (Modules\MyModule\Http\Controllers)
│ ├── Requests/ # FormRequest классы
│ └── Resources/ # API-ресурсы
├── Models/ # модели Eloquent (Modules\MyModule\Models)
├── Observers/ # наблюдатели моделей
├── Middleware/ # классы middleware
├── Services/ # сервисные классы
├── Console/ # консольные команды — автоматически регистрируются
├── database/
│ └── migrations/ # миграции БД — выполняются при установке/обновлении,
│ # откатываются при удалении
├── resources/
│ ├── views/ # Blade-шаблоны: view('MyModule::dir/file')
│ ├── lang/ # переводы по языкам: __('MyModule::file.key')
│ └── assets/ # статические файлы (css, js, img);
│ # симлинк создаётся на /assets/modules/my-module/
└── screenshots/ # скриншоты модуля для карточки в админке
```
Все поддиректории необязательные — создавай только нужные.
---
## Файл module.php
Обязательный файл. Возвращает массив с метаданными и возможностями модуля:
```php
use Modules\MyModule\Models\MyModel;
use Modules\MyModule\Observers\MyObserver;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Facades\DB;
return [
'name' => 'Название модуля',
'description' => 'Краткое описание',
'info' => '
Длинное описание с HTML, показывается на странице модуля в админке
',
'version' => '1.0.0',
'requires' => '14.0.0',
'author' => 'Автор',
'email' => 'author@example.com',
'homepage' => 'https://example.com',
// Возможности моделей модуля
'models' => [
MyModel::class => [
'label' => 'Метка раздела',
'search' => ['view' => 'MyModule::search/_results', 'with' => ['user']],
'feed' => ['with' => ['user', 'files'], 'view' => 'MyModule::feeds/_feed'],
'upload' => 'media',
'rating' => true,
'spam' => true,
],
],
// Наблюдатели моделей
'observers' => [
MyModel::class => MyObserver::class,
],
// Планировщик задач
'schedule' => function (Schedule $schedule) {
$schedule->command('my-module:cleanup')->daily();
},
// Пересчёты — вызываются из админки кнопкой «Пересчёт»
'restatement' => [
'mymodel' => function () {
DB::update('update ...');
},
],
// Ссылки-действия на странице модуля в админке
'actions' => [
'/admin/my-module' => 'Мой модуль',
],
// Публикация файлов: копирование из модуля в директории движка
'publish' => [
'stubs/views' => 'resources/views/vendor/my-module',
'stubs/widget.blade.php' => 'resources/views/themes/default/widgets/my-module.blade.php',
],
];
```
### Описание полей
**`name`, `description`, `author`, `email`, `homepage`** — отображаются в карточке модуля.
**`info`** — длинное описание с HTML, видно на странице модуля в админке. Сюда удобно класть инструкции по подключению.
**`version`** — текущая версия модуля. Если в `module.php` версия выше установленной, в админке появляется кнопка «Обновить».
**`requires`** — минимальная версия движка Rotor. При несовместимости модуль помечается в каталоге как «Несовместим».
**`models`** — массив `Class::class => [возможности]`. Каждая модель автоматически регистрируется в `morphMap` Laravel. Доступные возможности:
| Ключ | Описание |
|---|---|
| `label` | Метка раздела модели — название в результатах поиска, на странице «Спам» и в рейтингах. |
| `search` | Подключает модель к глобальному поиску. `view` — шаблон одного результата, `with` (опц.) — отношения для eager-load. |
| `feed` | Подключает к общей ленте активности. `with` — отношения для eager-load, `view` — шаблон записи, `scope` (опц.) — замыкание, ограничивающее выборку, `poll` (опц.) — замыкание `fn ($model): ?array`, возвращающее `[морф-имя, id]` связанной записи, если голосование привязано не к самой записи (тема → последний пост). |
| `upload` | Разрешает прикреплять файлы. `media` — изображения и видео, `file` — любые файлы. |
| `rating` | `true` — включает лайки/дизлайки. |
| `spam` | `true` — записи можно помечать как спам (раздел на странице «Спам» в админке; название раздела берётся из `label`). |
Если модель нужна только для `morphMap` (например, для полиморфных связей), но не имеет возможностей — оставь пустой массив:
```php
'models' => [
Vote::class => [],
],
```
**`observers`** — массив `Class::class => Observer::class`. Регистрирует Eloquent-наблюдателей.
**`schedule`** — замыкание, получающее `Schedule` Laravel. Регистрирует периодические задачи.
**`restatement`** — массив `'ключ' => callable`. Пересчёты счётчиков, запускаются из админки или вручную через `Restatement::run('ключ')`.
**`actions`** — массив `URL => 'Название'`. Ссылки-действия, отображаются на странице модуля в админ-панели.
**`publish`** — массив `источник => назначение`. Копирует файлы из модуля в произвольные директории движка при установке и удаляет их при отключении/удалении модуля. Подробнее в разделе «Публикация файлов».
---
## История изменений (changelog.md)
Необязательный файл `changelog.md` в корне модуля. Секции по версиям: заголовок `## X.Y.Z` обязателен, тело — произвольный текст (markdown знать не нужно).
```markdown
## 1.1.0
- Добавлена выгрузка в CSV
- Исправлена пагинация
## 1.0.0
- Первый релиз
```
Rotor показывает changelog на странице модуля в админке:
- **История изменений** — весь файл, всегда.
- **Что нового в версии X** — секция новой версии, при доступном обновлении (из реестра, ещё до установки).
При сборке реестра (`module:registry`) и в CI секция текущей версии попадает в `registry.json`. Файла нет — ничего не ломается, поле просто отсутствует.
---
## Маршруты (routes.php)
```php
use Illuminate\Support\Facades\Route;
use Modules\MyModule\Http\Controllers\MyController;
Route::middleware('web')
->controller(MyController::class)
->prefix('my-module')
->name('my-module.')
->group(function () {
Route::get('/', 'index')->name('index');
Route::get('/{id}', 'view')->name('view');
});
// Админка
Route::middleware(['web', 'check.admin', 'admin.logger'])
->prefix('admin')
->group(function () {
Route::controller(AdminMyController::class)
->prefix('my-module')
->name('admin.my-module.')
->group(function () {
Route::get('/', 'index')->name('index');
Route::delete('/{id}', 'delete')->name('delete');
});
});
```
---
## Контроллеры
Пространство имён: `Modules\MyModule\Http\Controllers`
```php
namespace Modules\MyModule\Http\Controllers;
class MyController extends \App\Http\Controllers\Controller
{
public function index() { ... }
}
```
Административные контроллеры размещаются в `Http/Controllers/Admin/` и наследуются от `\App\Http\Controllers\Admin\AdminController`.
---
## Модели
Пространство имён: `Modules\MyModule\Models`
```php
namespace Modules\MyModule\Models;
class MyModel extends \Illuminate\Database\Eloquent\Model
{
public static string $morphName = 'mymodels';
}
```
`$morphName` обязательно у моделей, заявленных в `models` (используется ядром для регистрации возможностей).
Ограничения морф-имени: максимум **20 символов** (ширина колонки `relate_type` в БД) и неизменность после релиза модуля — имя сохраняется в записях БД.
---
## Хуки (hooks.php)
Файл `hooks.php` содержит:
- вставки в шаблоны через `Hook::add` (UI-расширения);
- регистрации в `Registry` для возможностей, которые не привязаны к одной модели (sitemap, complaint, onDeleteUser, onAdminDeleteUser);
- регистрации `Restatement::register` (если не объявлено в `module.php`).
### Hook::add
Хук может быть строкой или callable. Callable получает аргументы из `@hook(...)` и возвращает свой HTML-фрагмент (или `null`/`''` если ничего не добавлять). Все фрагменты склеиваются в порядке убывания `priority`.
```php
use App\Classes\Hook;
// Статичная строка
Hook::add('sidebarMenuEnd', '
// Динамический фрагмент
Hook::add('head', static function () {
return '';
});
// С аргументом из @hook('userProfileLinks', $user)
Hook::add('userProfileLinks', static function ($user) {
return ' / Мои записи';
});
// Третий аргумент — приоритет (выше → раньше). По умолчанию 0
Hook::add('sidebarMenuEnd', static fn () => '
```
Вызов в шаблоне:
```blade
@hook('head')
@hook('userProfileLinks', $user)
```
### Registry
```php
use App\Classes\Registry;
use Modules\MyModule\Models\MyModel;
// Жалобы — обработчик клика на «пожаловаться»
Registry::complaint(MyModel::$morphName, function (int $id) {
$model = MyModel::query()->find($id);
return ['model' => $model, 'path' => $model?->getViewUrl(false)];
});
// Sitemap
Registry::sitemap('mymodels', function () {
return [['loc' => route('my-module.index'), 'lastmod' => gmdate('c')]];
});
// Удаление пользователя — что подчистить
Registry::onDeleteUser(function (\App\Models\User $user) {
MyModel::query()->where('user_id', $user->id)->delete();
});
// Удаление пользователя администратором (в Request — чекбоксы формы удаления,
// добавить свой можно через Hook::add('adminUserDeleteFields', ...))
Registry::onAdminDeleteUser(function (\App\Models\User $user, \Illuminate\Http\Request $request) {
if ($request->boolean('delmymodels')) {
MyModel::query()->where('user_id', $user->id)->get()->each->delete();
}
});
```
Полный список методов `Registry`:
| Метод | Назначение |
|---|---|
| `fileType($morphName)` | тип принимает файлы (вызывается из `module.php` через `'upload' => 'file'`) |
| `mediaType($morphName)` | тип принимает фото/видео (`'upload' => 'media'`) |
| `ratingType($morphName)` | тип поддерживает рейтинг (`'rating' => true`) |
| `spamType($morphName)` | тип — источник жалоб на спам (`'spam' => true`, метка берётся из `label`) |
| `label($morphName, $label)` | отображаемое название типа (`'label' => '...'`) |
| `feed($class, $config)` | запись в ленте активности (`'feed' => [...]`) |
| `search($class, $view, $with)` | полнотекстовый поиск (`'search' => [...]`) |
| `complaint($morphName, $handler)` | обработчик жалобы |
| `sitemap($key, $handler)` | страница в sitemap |
| `onDeleteUser($handler)` | очистка при удалении пользователя |
| `onAdminDeleteUser($handler)` | удаление пользователя администратором |
Первые семь обычно регистрируются декларативно через `module.php`, вручную их вызывать не нужно.
---
## Шаблоны
Файлы в `resources/views/` вызываются с указанием неймспейса модуля:
```php
view('MyModule::directory/file')
// → resources/views/directory/file.blade.php
```
---
## Переводы
Файлы в `resources/lang/ru/`, `resources/lang/en/` и т.д.:
```php
__('MyModule::file.key')
// → resources/lang/ru/file.php → ['key' => '...']
```
---
## Конфигурация (config.php)
```php
// config.php
return [
'api_key' => env('MY_MODULE_API_KEY'),
'limit' => 10,
];
// Использование
config('MyModule.api_key');
```
Значения из админки записываются в поле `settings` модуля и сливаются поверх `config.php` при загрузке.
---
## Helpers (helpers.php)
Глобальные функции, доступные везде:
```php
if (! function_exists('statsMyModule')) {
function statsMyModule(): string
{
return (string) MyModel::query()->count();
}
}
```
---
## Middleware (middleware.php)
```php
use Modules\MyModule\Middleware\MyMiddleware;
return [
// Алиасы для применения в routes.php через ->middleware('alias')
'aliases' => [
'my-alias' => MyMiddleware::class,
],
// Middleware, добавляемые в группу web автоматически
'web' => [
MyMiddleware::class,
],
];
```
---
## Консольные команды
Файлы в `Console/` подхватываются автоматически. Имя класса = имя файла:
```php
// Console/Cleanup.php
namespace Modules\MyModule\Console;
use Illuminate\Console\Command;
class Cleanup extends Command
{
protected $signature = 'my-module:cleanup';
public function handle(): void { /* ... */ }
}
```
---
## Статические файлы
Файлы из `resources/assets/` доступны по адресу:
```
/assets/modules/my-module/
```
Симлинк создаётся автоматически при установке модуля.
---
## Публикация файлов (publish)
Ключ `publish` в `module.php` копирует файлы из модуля в любые директории движка. В отличие от симлинка `resources/assets/` (доступ к статике по URL), это нужно когда файл должен физически лежать в ядре — переопределение Blade-шаблонов темы, вставка виджета в тему, файлы в корне проекта (`favicon.ico`, `robots.txt`).
```php
'publish' => [
'stubs/views' => 'resources/views/vendor/my-module',
'stubs/widget.blade.php' => 'resources/views/themes/default/widgets/my-module.blade.php',
],
```
- **Источник** — путь относительно папки модуля (`modules/MyModule/`).
- **Назначение** — путь относительно корня проекта (`base_path()`).
- Если источник — директория, копируется/удаляется директория; если файл — файл.
- Копирование выполняется при **установке и включении**, удаление — при **отключении и удалении** модуля.
- При **обновлении ядра** файлы перепубликуются автоматически (деплой и обновление через админку вызывают `module:sync`), так что затёртые апдейтом оверрайды восстанавливаются.
- Пути с `..` игнорируются.
> Удаление снимает с диска именно опубликованный путь назначения. Не указывайте в назначении общий каталог движка (например `config` или `public`) — при отключении он будет удалён целиком. Используйте выделенные пути (`resources/views/vendor/my-module`, `public/assets/my-module`).
---
## Примеры минимальных модулей
**Только маршруты и контроллер:**
```
MyModule/module.php, routes.php, Http/Controllers/MyController.php
```
**Только миграции (изменение БД):**
```
MyModule/module.php, database/migrations/
```
**Только внешний вид (хуки):**
```
MyModule/module.php, hooks.php
```
См. модуль `Template` — минимальный шаблон для старта.
---
## License
The Rotor is open-sourced software licensed under the [GPL-3.0 license](http://opensource.org/licenses/GPL-3.0)