Программный продукт Telegraph от DefStudio работает шустро и работать с ним одно удовольствие. Но не обошлось и без ложки дёгтя в этом меду - несмотря на возможность работы с несколькими ботами, обработчик для них всех будет лишь один. Благо есть лёгкий способ это исправить.
Вводная
В прошлом году разработчику задавали вопрос по поводу расширения функционала поддержки разных обработчиков, и он дал понять, что добавлять подобное не планирует. Во всяком случае таково его мнение было на тот момент.
Итак, по-умолчанию в параметре webhook.handler файла config/telegraph.php мы можем указать лишь один единственный хендлер для обработки наших запросов.
Но, как говорится, разработчики - люди простые. Нужен калькулятор? Напишем! 😀
Для удобства я располагаю вебхуки в неймспейс App\Http\Webhooks. Лично мне так проще понять что где.
Создадим новый класс, который также будет наследоваться от базового WebhookHandler, и назовём его, например, Handler. В нём переопределим метод handle, являющийся основным. Делаем мы это лишь потому, что стандартный контроллер от Telegraph сам находит бота и передаёт его в параметр метода, в связи с чем нет смысла второй раз руками ходить за ним в базу.
<?php namespace App\Http\Webhooks; use DefStudio\Telegraph\Handlers\WebhookHandler; use DefStudio\Telegraph\Models\TelegraphBot; use Illuminate\Http\Request; class Handler extends WebhookHandler { public function handle(Request $request, TelegraphBot $bot): void {} }
Далее укажем ссылку на этот класс в настройках. Для этого открываем файл config/telegraph.php и в параметре webhook.handler указываем App\Http\Webhooks\Handler::class.
Ещё момент, что, несмотря на явную возможность не указывать имя бота (поле name в его таблице), рекомендую всё же его использовать, так как это потребуется для его идентификации.
Всё! Начало положено, давайте приступать!
Реализация
Представим, что у нас два бота и мы хотим разделить их логику. Создадим их классы. Пусть будут банальные названия Bot1Handler и Bot2Handler. ��очему бы и да ¯\_(ツ)_/¯
Так как наша цель показать возможность их разделения, классы будут пустыми:
<?php namespace App\Http\Webhooks; use DefStudio\Telegraph\Handlers\WebhookHandler; class Bot1Handler extends WebhookHandler {}
<?php namespace App\Http\Webhooks; use DefStudio\Telegraph\Handlers\WebhookHandler; class Bot2Handler extends WebhookHandler {}
Теперь возвращаемся к нашему основному хендлеру и заполняем. Дабы не тянуть резину, сразу прилагаю конечный его код, который дальше по ходу дела разберём:
<?php namespace App\Http\Webhooks; use DefStudio\Telegraph\Handlers\EmptyWebhookHandler; use DefStudio\Telegraph\Handlers\WebhookHandler; use DefStudio\Telegraph\Models\TelegraphBot; use Illuminate\Http\Request; class Handler extends WebhookHandler { protected array $handlers = [ 'bot_1' => Bot1Handler::class, 'bot_2' => Bot2Handler::class, // ...здесь возможны другие связи... ]; public function handle(Request $request, TelegraphBot $bot): void { $handler = $this->getHandler($bot->name); (new $handler)->handle($request, $bot); } protected function getHandler(string $botName): string { return $this->handlers[$botName] ?? EmptyWebhookHandler::class; } }
Первое свойство $handler является ассоциативным массивом, где в имени ключа как раз будем указывать имя бота из колонки name его таблицы. Для этого я и рекомендовал его использовать. В качестве значения - ссылка на класс хендлера. Здесь всё просто.
В переопределённом методе handle первым делом вызываем метод getHandle, передавая в его параметр имя бота. Метод находит в свойстве $handle ссылку на хендлер либо возвращает null, который мы перехватываем нуль-колизной функцией для возврата дефолтного хендлера либо любой другой нужной Вам механики.
Получив класс хендлера, создаём класс и вызываем его метод handle, передав в параметры объекты реквеста и бота.
Альтернативный вариант
В случаях, когда по логике нужно добавлять или убирать любых ботов без жёсткой привязки, а в случае отсутствия специализированного для бота хендлера выполнять дефолтную механику, можно использовать динамическую подгрузку путём проверки существования класса:
<?php namespace App\Http\Webhooks; use DefStudio\Telegraph\Handlers\EmptyWebhookHandler; use DefStudio\Telegraph\Handlers\WebhookHandler; use DefStudio\Telegraph\Models\TelegraphBot; use Illuminate\Http\Request; use Illuminate\Support\Str; class Handler extends WebhookHandler { protected $prefix = '\\App\\Http\\Webhooks\\'; public function handle(Request $request, TelegraphBot $bot): void { $handler = $this->getHandler($bot->name); (new $handler)->handle($request, $bot); } protected function getHandler(string $name): string { $name = Str::studly($name); if (class_exists($class = $this->prefix . $name)) { return $class; } return EmptyWebhookHandler::class; } }
Заключение
И, как бы, всё. Дальше метод будет выполнен найденным для этого бота хендлером.
Добавить больше нечего, поэтому всех благ!
