Расширение кода
JournalPHP расширяется стандартными средствами Laravel и новым кодом по соглашениям проекта.
Обзор
Новая функциональность добавляется сервис-провайдерами, событиями, middleware и классами по тем же соглашениям, что и существующий код: Actions, репозитории с интерфейсами, сервисы со сменными драйверами. Соглашения описаны в разделе Архитектура проекта.
Сервис-провайдеры
Провайдеры приложения зарегистрированы в config/app.php → providers:
| Провайдер | Назначение |
|---|---|
AppServiceProvider | Общая инициализация, включая FORCE_HTTPS |
AuthServiceProvider | Политики авторизации |
EventServiceProvider | События, слушатели, провайдеры Socialite |
RouteServiceProvider | Загрузка маршрутов и лимиты запросов |
BroadcastServiceProvider | Каналы WebSocket-вещания |
RepositoryServiceProvider | Автоматическая регистрация репозиториев |
MetaBuilderServiceProvider | SEO-билдеры страниц |
MailServiceProvider | SMTP из настроек админ-панели |
MediaServiceProvider | Привязка хранилища файлов через UploaderManager |
AiServiceProvider | Привязка AI-сервиса через AiManager |
ImageModerationServiceProvider | Привязка модерации изображений через ImageModerationManager |
ScoutConfigServiceProvider | Поисковый движок из настроек админ-панели |
HorizonServiceProvider, LogViewerServiceProvider | Доступ к Horizon и Log Viewer |
SlowQueryServiceProvider | Логирование SQL-запросов дольше 700 мс |
Свои привязки добавляйте в AppServiceProvider::register() или в отдельный провайдер, зарегистрированный в config/app.php:
$this->app->bind(MyServiceInterface::class, MyService::class);Actions
Бизнес-операции оформляются как Action-классы с методом execute():
namespace App\Actions\MyFeature;
class DoSomethingAction
{
public function __construct(
protected DoSomethingRepositoryInterface $repository,
) {}
public function execute(User $user, array $data): Result
{
// бизнес-логика
}
}Зависимости внедряются через конструктор, Action — через параметр метода контроллера:
public function store(MyRequest $request, DoSomethingAction $action): RedirectResponse
{
$action->execute($request->user(), $request->validated());
return back();
}Репозитории
Создайте интерфейс в подкаталоге Interfaces/ и реализацию рядом с ним:
app/Repositories/MyFeature/
├── Interfaces/
│ └── GetSomethingRepositoryInterface.php
└── GetSomethingRepository.phpRepositoryServiceProvider зарегистрирует привязку автоматически, если имя реализации совпадает с именем интерфейса без суффикса Interface.
Новая настройка в админ-панели
Репозиторий: в
app/Repositories/Config/MyFeature/создайте репозиторий чтения настройки из таблицыconfig. Значение по умолчанию верните из репозитория или добавьте вConfigSeeder.Контроллер:
app/Http/Controllers/Admin/MyFeatureSettingsController.phpс методамиeditиupdate. Сохраняйте значение черезConfig::updateOrCreate('myFeature', $value)— модель сама сбросит кэш.Маршруты: в
routes/admin.php:php$route->get('/settings/my-feature', [MyFeatureSettingsController::class, 'edit'])->name('settings.my-feature.edit'); $route->put('/settings/my-feature', [MyFeatureSettingsController::class, 'update'])->name('settings.my-feature.update');Раздел: в
config/admin_sections.phpдобавьте ключ, совпадающий с путём после/admin-panel/:php'settings/my-feature' => 'Моя настройка',Страница: Inertia-страница в
resources/js/Admin/Pages/Settings/MyFeature/Edit.vue.Меню: пункт в
resources/js/Admin/Layout/AppSidebar.vueс тем же путём.
WARNING
Путь, для которого нет ключа в config/admin_sections.php, доступен любому администратору. Добавляйте ключ для каждого нового раздела.
Вход через новый OAuth-провайдер
- Установите пакет провайдера из SocialiteProviders.
- Добавьте его обработчик в
SocialiteWasCalledвEventServiceProvider::$listen. - Добавьте
redirectпровайдера вconfig/services.php→oauth. - Добавьте поля Client ID и Client Secret в
AuthSettingsController, репозиторий настройки вapp/Repositories/Config/Auth/и форму вresources/js/Admin/Pages/Settings/Auth/Edit.vue. - Добавьте провайдер в список публичных провайдеров в
GetSharedInertiaConfigRepository, чтобы кнопка появилась в окне входа.
Новое хранилище файлов
- Добавьте значение и название в
App\Enums\UploaderDriver. - Создайте класс в
app/Services/Uploader/Drivers/, унаследовавAbstractUploader. - Добавьте метод
create{Name}Driver()вUploaderManager. - Добавьте поля учётных данных в
MediaSettingsControllerиresources/js/Admin/Pages/Settings/Media/Edit.vue.
Новый поисковый движок
- Добавьте значение в
App\Enums\SearchDriverи перевод названия вresources/lang/*/enums.php. - Реализуйте драйвер в
app/Services/Search/Drivers/с интерфейсамиSearchDriverInterfaceиSearchIndexManagementInterface, а также провайдеры поиска постов и пользователей вapp/Services/Search/Providers/. - Зарегистрируйте драйвер методом
create{Name}Driver()вSearchManager. - Передайте ключи в конфигурацию Scout в
ScoutConfigServiceProvider. - Добавьте поля в
UpdateSearchSettingsRequest,UpdateSearchSettingsActionиresources/js/Admin/Pages/Settings/Search/Edit.vue.
Новый сервис GIF
- Добавьте значение и название в
App\Enums\GifDriver. - Создайте класс в
app/Services/Gif/Drivers/, унаследовавAbstractGifService, и реализуйте методыendpoint(),params(),results()иnext(). - Добавьте метод
create{Name}Driver()вGifManager. - Добавьте ветку с ключами сервиса в
matchвAdmin/GifSettingsControllerи секцию с полями вresources/js/Admin/Pages/Settings/Gifs/Edit.vue.
Драйвер возвращает результаты в формате {results, next}.
Новый AI-провайдер
- Добавьте значение в
App\Enums\AiProviderвместе с названием, моделью и адресом API по умолчанию. - Добавьте метод
create{Name}Driver()вAiManager. Если провайдер совместим с OpenAI Chat Completions API, метод возвращаетOpenAiCompatibleService. Иначе создайте драйвер вapp/Services/Ai/Drivers/, унаследовавAbstractAiServiceи реализовавrequest().
Провайдер появится в списке в Админ-панель → AI → Настройки автоматически.
Новый драйвер модерации изображений
- Добавьте значение и название в
App\Enums\ImageModerationDriver. - Создайте класс в
app/Services/Moderation/Image/Drivers/, унаследовавAbstractImageModerationServiceи реализовавmoderateBatch(). - Добавьте метод
create{Name}Driver()вImageModerationManager. - Добавьте поля настроек в
ImageModerationSettingsControllerиresources/js/Admin/Pages/AiIntegrations/ImageModerationSettings/Edit.vue.
Новый платёжный провайдер
- Добавьте значение в
App\Enums\PaymentProviderDriverи перевод названия вresources/lang/*/enums.php. - Реализуйте
App\Services\Payments\Contracts\PaymentProviderInterfaceи клиент API вapp/Services/Payments/Providers/{Name}/. - Добавьте метод
create{Name}Driver()вPaymentManager. - Добавьте код провайдера в правило
in:yookassa,tbankвSubscriptionPaymentProviderControllerиSubscriptionPaymentController, поля ключей — вSubscriptionPaymentProviderControllerи форму настроек. - Если провайдер присылает уведомления о платежах, добавьте обработчик в
App\Http\Controllers\Payments\PaymentWebhookController.
Новый язык
Переводы находятся в resources/lang/:
| Файлы | Что содержат |
|---|---|
resources/lang/ru.json, resources/lang/en.json | Строки интерфейса сайта |
resources/lang/ru/*.php, resources/lang/en/*.php | Серверные тексты: авторизация, валидация, письма, ошибки (errors.php), сообщения (messages.php), подписи значений перечислений (enums.php), мета-теги |
JSON-файлы подключаются через laravel-vue-i18n. Добавляя строку, вносите её во все языки с одинаковым ключом.
Чтобы добавить язык:
- Создайте
resources/lang/{code}.jsonсо строками интерфейса. - Создайте каталог
resources/lang/{code}/и переведите файлы изresources/lang/en/. - Подключите локаль Moment.js в
resources/js/app.jsпо примеруmoment/dist/locale/ru. - Добавьте код языка в правило валидации
in:en,ruвapp/Http/Controllers/Admin/GeneralSettingsController.php, в список языков на странице общих настроек (resources/js/Admin/Pages/Settings/General/Edit.vue) и в список допустимых локалей вapp/Http/Middleware/SetLocaleFromConfig.php.
Команды, события, middleware
php artisan make:command MyCommand
php artisan make:event SomethingHappened
php artisan make:listener HandleSomething --event=SomethingHappened
php artisan make:middleware MyMiddleware- Команды из
app/Console/Commands/загружаются автоматически; расписание — вapp/Console/Kernel.php. - Слушатели регистрируются в
EventServiceProvider::$listen. - Middleware регистрируются в
app/Http/Kernel.php— глобально, в группе или как псевдоним. - Наблюдатели моделей подключаются атрибутом
#[ObservedBy]на модели.
Локальные пакеты
Код можно вынести в пакет и подключить через path-репозиторий Composer:
{
"repositories": [
{ "type": "path", "url": "./packages/my-package" }
]
}Сервис-провайдер пакета регистрируется автоматически через extra.laravel.providers в его composer.json или вручную в config/app.php.
Форк
Обновления из основного репозитория забираются в форк командами:
git remote add upstream https://github.com/ADamCarraway/journalphp.git
git fetch upstream
git merge upstream/master