Программный продукт 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;
}
}
Заключение
И, как бы, всё. Дальше метод будет выполнен найденным для этого бота хендлером.
Добавить больше нечего, поэтому всех благ!