Начало проекта

Это первая заметка из небольшого цикла по реальному применению Vesp. В качестве примера мы напишем и запустим Telegram бота, который будет делать что-то несложное.
Понятное дело, что за работу с Телеграм будет отвечать готовая библиотека, но в остальном мы всё напишем сами.
Я буду предполагать, что у вас уже есть своё рабочее окружение, и вы можете запускать на своей машине PHP с NodeJS.
Лично я использую последнюю MacOS с Homebrew, Laravel Valet, MySQL 8, PHP 7.4 и Node 17.5. Если нужна отдельная заметка по их установке и настройке - напишите в комментариях.

Подготовка

Если вы тоже используете Valet, то установите мой драйвер для работы с Vesp.
Итак, создаём новый проект в директории ExampleBot:
composer create-project vesp/vesp ExampleBot
Эта команда скачает и установит все нужные зависимости для PHP и Node.
Создаём локальный домен example-bot.test для работы:
cd ExampleBot
valet link example-bot
Переименовываем .env в .env.local, чтобы избежать возможных конфликтов при будущем деплое на рабочий сервер
mv .env .env.local
И редактируем файл, примерно так:
APP_NAME="Example Bot"

SITE_URL=http://example-bot.test/
API_URL=http://example-bot.test/api/

CORS=1 # Разрешаем кросс-доменные запросы к API

DB_DRIVER=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_PREFIX=app_
DB_DATABASE=ExampleBot
DB_USERNAME=root
DB_PASSWORD=root
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_general_ci
DB_FOREIGN_KEYS=1

JWT_SECRET=secret
JWT_EXPIRE=2592000
JWT_MAX=3

UPLOAD_DIR=/Users/developer/Projects/ExampleBot/upload/
CACHE_DIR=/Users/developer/Projects/ExampleBot/tmp/
Теперь можно собрать фронтенд и проверить работу проекта:
composer node:generate
Открываем ваш локальный http://example-bot.test, и видим стандартную заглушку.
Фронтенд нам пока не нужен, но пусть будет. Интересует нас работа API, которую тоже легко проверить.
Открываем http://example-bot.test/api/user/profile, и в ответ мы должны получить "Authentication required" и код 401.
Если вы это видите, значит всё в порядке, API работает и можно продолжать работу.

Создаём бота

Телеграм управляет ботами через главного папу-бота, @BotFather. Вам нужно его добавить к себе, и создать нового бота, там всё очень просто.
Я сделал VespExampleBot, и получил токен для HTTP запросов к нему.
Cоздаём новые переменные в .env.local, чтобы затем использовать их в своём коде
BOT_USERNAME=ВашеИмяБота
BOT_API_KEY=ВашСекретныйТокен
Если в будущем нужно будет заменить токен, то поменять его придётся только в файле настроек.
Всё, телеграм-бот уже работает, только ничего не делает, потому что мы не написали программу, которая будет с ним дружить.

Логика работы API Vesp

Для получения и обработки запросов Vesp использует Slim 4.
  • Все запросы прилетают в www/api.php
  • Там Slim инициализируется через PHP-DI, что сразу даёт нам поддержку контейнера зависимостей в контроллерах
  • Затем добавлятся маршруты с контроллерами из core/routes.php
  • Slim сверяет запрос с указанными маршрутами и передаёт его подходящему контроллеру
  • Контроллер обрабатывает запрос и возвращает ответ через $this->success() или $this->failure() в формате JSON.
Согласно спецификации HTTP, запросы могут быть отправлены разными методами: GET, POST, PUT, PATCH, DELETE и OPTIONS.
Ответы должны соответствовать PSR-7, за что также отвечает Slim 4.
Нам остаётся только добавить в котроллер публичную функцию для нужного запроса с его именем, например для GET:
public function get(): Psr\Http\Message\ResponseInterface
{
    return $this->success('Hello World!');
}
Как следуюет из сигнатуры функции, ответ обязательно должен быть экземпляром Psr\Http\Message\ResponseInterface.
Для POST и PATCH это будут соответственно:
public function post(): Psr\Http\Message\ResponseInterface
{
    // ...
}
public function patch(): Psr\Http\Message\ResponseInterface
{
    // ...
}
Если кто-то запросит метод, которого в котроллере нет, то получит ошибку 405 Method Not Allowed.
Итого, для поддержки любых запросов в API, нам нужно:
  1. Создать контроллер в core/src/Controllers, который будет расширять Vesp\Controllers\Controller или его наследника.
  2. Прописать в нём нужные публичные методы.
  3. Указать этот контроллер для маршрута в core/routes.php
  4. Ну и присылать запросы на этот адрес.
Вот и всё!

Проверяем связь с ботом

Устанавливаем библиотеку для работы с API Телеграм:
composer require longman/telegram-bot
Создаём сервис для работы с API, чтобы его можно было легко использовать в контроллерах Vesp.
Создаём файл core/src/Services/Telegram.php:
<?php

namespace App\Services;

class Telegram extends \Longman\TelegramBot\Telegram
{
    public function __construct()
    {
        parent::__construct(getenv('BOT_API_KEY'), getenv('BOT_USERNAME'));
    }
}
Как видно, наш класс расширяет основной класс библиотеки, всего с одним изменением - он сразу указывает наши переменные для бота. То есть, когда мы будем вызывать наш сервис, он всегда будет инициализировать именно нашего бота, без лишних вопросов.
Теперь пишем простенький контроллер для теста core/src/Controllers/Web/Test.php. Он не будет требовать никакой авторизации, поэтому кладём его в раздел Web:
<?php

namespace App\Controllers\Web;

use App\Services\Telegram;
use Longman\TelegramBot\Request;
use Psr\Http\Message\ResponseInterface;
use Vesp\Controllers\Controller;
use Vesp\Services\Eloquent;

class Test extends Controller
{
    protected Telegram $telegram;

    public function __construct(Eloquent $eloquent, Telegram $telegram)
    {
        parent::__construct($eloquent);
        $this->telegram = $telegram;
    }

    public function get(): ResponseInterface
    {
        $response = Request::getMe();
        if ($response->isOk()) {
            return $this->success($response->getResult());
        }

        return $this->failure($response->getDescription(), $response->getErrorCode());
    }
}
Тут нужно обратить внимание, что мы расширили конструктор контроллера и помимо стандартной загрузки Eloquent для работы с базой данных, добавили еще и наш новый сервис Telegram.
Так как Vesp использует контейнер зависимостей, при обращении к такому контроллеру в него автоматически передаются требуемые классы.
Таким образом можно загружать любые свои сервисы в контроллеры и там их использовать.
Осталось только указать новый маршрут с нашим контроллером в core/routes.php внутри группы api, сразу после подгруппы admin:
        $group->group(
            '/web',
            static function (RouteCollectorProxy $group) {
                $group->get('/test', App\Controllers\Web\Test::class);
            }
        );
Теперь можно делать запрос по адресу http://example-bot.test/api/web/test и, если вы всё сделали верно, то получите ответ с данными бота.
У меня он вот такой:
Это значит, что наша система не только работает, но и связана с ботом, и может им управлять.
Продолжение следует!
Полный код проекта в текущем виде находится на GitHub.

3 комментария

Сергей Лелеко
О прикольно! я как раз начинал делать заготовку и очень интересно как должно быть реализовано правильно
Павел Гвоздь
Так как Vesp использует контейнер зависимостей, при обращении к такому контроллеру в него автоматически передаются требуемые классы
Wow! Это PHP-DI может такое вытворять? Просто круто!
P.S. Кажется цитаты у тебя никак не стилизуются в комментариях...
Василий Наумкин
P.S. Кажется цитаты у тебя никак не стилизуются в комментариях...
Спасибо, поправил!
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
23.12.2024, 05:33:00
В MODX сначала создали проблему, автоматически генерируя адреса, а потом "решили" заморозкой. Так ч...
Дмитрий
14.12.2024, 09:10:38
Василий, прошу прощения, тупанул, не разобрался сразу. Фреймворк отличный! "Чистый лист" на vue, рис...
Василий Наумкин
05.12.2024, 20:01:14
В итоге основная ошибка была в неправильном общем root в Nginx, из-за чего запросы не улетали на фай...
Василий Наумкин
22.11.2024, 03:33:54
Спасибо!
inna
06.11.2024, 15:47:13
Да. Все работает. Спасибо.
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
Уровни подписки
Спасибо!
500 ₽ в месяц
Эта подписка ничего не даёт, просто возможность сказать спасибо за мои заметки. Подписчики отмечаются зелёненьким цветом в комментариях.
Большое спасибо!
1 000 ₽ в месяц
И эта подписка не даёт ничего, кроме оранжевого цвета в комментариях и возможности сказать спасибо, но уже большое!