Программный продукт 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;
    }
}

Заключение

И, как бы, всё. Дальше метод будет выполнен найденным для этого бота хендлером.

Добавить больше нечего, поэтому всех благ!

Комментарии (0)