# Архитектура

[English version](ARCHITECTURE.md)

## Сайт для гостей

- [`/modules/guest`](/modules/guest)

## Сайт для пользователей

Используйте виджеты и хэлперы для предотвращения дублирования, копирования кода. Виджеты и хэлперы помогают переиспользовать существующий код, быстро и безопасно наращивать новый функционал.

### Виджеты

#### Кнопки

- AddButton - кнопка добавления нового элемента
- CancelButton - кнопка закрытия модального окна или возврата
- DeleteButton - кнопка удаления элемента (включено окно confirm)
- TrashButton - кнопка удаления элемента (включено окно confirm). В отличие от Delete обозначена иконкой мусорного ведра
- EditButton - кнопка редактирования
- SaveButton - кнопка сохранения
- SendButton - кнопка отправления

Namespace кнопок-виджетов: `app\widgets\buttons`. При вызове виджета можно не передавать параметры.

Для отображения кнопок рекомендуется использовать короткую конструкцию `<?= ?>`, так как при отображении более одной кнопки такая конструкция добавляет пространство между кнопками, что не даёт им слипнуться.

##### Создание кнопок

При создании нового виджета-кнопки необходимо наследовать базовый класс [`LinkButton`](/widgets/base/LinkButton.php) или [`PjaxButton`](/widgets/base/PjaxButton.php).

Оба класса наследуются от класса [`Linkable`](/widgets/base/Linkable.php), который возвращает сгенерированную гиперссылку `Html::a`.

При вызове виджетов, унаследованных от `Linkable`, во view можно передать следующие параметры:

- url (array|string|null) - url-адрес для перехода
- options (array) - третий параметр для `a`
- text (string) - текст или иконка (app\components\helpers\Html::icon())

А также можно переопределить или дополнить дефолтные параметры внутри класса в массиве `defaultOptions`, который являются третьим параметром метода `Html::a`.

Класс `PjaxButton` в отличие от первого в дополнение задаёт параметры для работы с Pjax (defaultOptions) и в основных параметрах содержит:

- confirm (bool) - включает/отключает нативное окно confirm
- confirmMessage в параметрах (string) - задаёт сообщение при выводе confirm-окна

При создании нового класса, убедитесь, что Ваш класс наследует `Linkable` или любой другой дочерний класс. При переопределении `init()` не забудьте вызвать родительский метод (parent::int()), и если необходимо переопределить `run()` без изменения возврата метода, то также необходимо в конце вызвать родительский `run()` (parent::run()).

#### Модальные окна

В проекте используется два вида модальных окон:

- [обычное модальное окно](https://www.yiiframework.com/extension/yiisoft/yii2-bootstrap/doc/api/2.0/yii-bootstrap-modal) ([`widgets\Modal`](/widgets/Modal.php))
- [модальное окно с подгрузкой контента через Ajax](https://github.com/loveorigami/yii2-modal-ajax/blob/master/README.md) ([`widgets\ModalAjax`](/widgets/ModalAjax.php))

При использовании модальных окон необходимо придерживаться следующий правил:

1. Модальное окно должно иметь кнопку закрытия (крестик). Для этого достаточно не устанавливать параметр `'closeButton'` в значение `false`.
2. При создании модального окна `ModalAjax` в view не создавайте отдельную html-структуру как для обычной страницы. Виджет подгрузит контент внутрь готового контейнера.

## Телеграм бот

### Структура файлов

- [`/modules/bot`](/modules/bot)
  - [`/components`](/modules/bot/components) - Основные компоненты бота, обеспечивающие его функциональность.
    - [`/actions`](/modules/bot/components/actions) - Действия общие для некоторых контроллеров.
      - [`/privates`](/modules/bot/components/actions/privates) - Действия общие для личных чатов.
    - [`/api`](/modules/bot/components/api) - Классы для взаимодействия с Telegram API.
      - [`/Types`](/modules/bot/components/api/Types) - Классы для взаимодействия с объектами, предоставляемыми Telegram API.
        - [`Update`](/modules/bot/components/api/Types/Update) - Класс для взаимодействия с объектами `Update`, предоставляемыми Telegram API.
      - [`BotApi`](/modules/bot/components/api/BotApi.php) - Класс для взаимодействия с Telegram API.
    - [`/crud`](/modules/bot/components/crud) - Компоненты, связанные с CRUD (Create, Read, Update, Delete) операциями для `CrudController`.
      - [`/rules`](/modules/bot/components/crud/rules) - Правила, определяющие доступ к данным.
      - [`/services`](/modules/bot/components/crud/services) - Сервисы для обработки данных.
    - [`CrudController`](/modules/bot/components/crud/CrudController.php) - Базовый класс контроллера, для контроллеров предоставляющие P2P сервисы.
    - [`/helpers`](/modules/bot/components/helpers) - Классы для вспомогательных функций.
    - [`/request`](/modules/bot/components/request) - Классы для обработки запросов и входящих данных.
    - [`/response`](/modules/bot/components/response) - Классы для отправки ответов и исходящих данных.
      - [`/commands`](/modules/bot/components/response/commands) - Классы для подготовки ответов разных типов.
    - `ResponseBuilder` - Класс для подготовки ответов подходящего типа.
    - [`ChannelRouteResolver`](/modules/bot/components/ChannelRouteResolver.php) - Компонент для маршрутизации запросов в каналах.
    - [`Controller`](/modules/bot/components/Controller.php) - Базовый класс контроллера, содержащий общую базовую логику, для обычных контроллеров.
    - [`GroupRouteResolver`](/modules/bot/components/GroupRouteResolver.php) - Компонент для маршрутизации запросов в группах.
    - [`PrivateRouteResolver`](/modules/bot/components/PrivateRouteResolver.php) - Компонент для маршрутизации запросов в личных чатах.
  - [`/config`](/modules/bot/config) - Конфигурационные файлы для настройки модуля.
    - [`actions`](/modules/bot/config/actions.php) - Массив с индексами вместо названий действий.
    - [`channels`](/modules/bot/config/channels.php) - Настройки маршрутизации для каналов.
    - [`controllers`](/modules/bot/config/controllers.php) - Массив с индексами вместо названий контроллеров.
    - [`groups`](/modules/bot/config/groups.php) - Настройки маршрутизации для групп.
    - [`privates`](/modules/bot/config/privates.php) - Настройки маршрутизации для личных чатов.
  - [`/controllers`](/modules/bot/controllers) - Контроллеры для обработки запросов и управления функциональностью бота.
    - [`/channels`](/modules/bot/controllers/channels) - Контроллеры для работы с каналами.
    - [`/groups`](/modules/bot/controllers/groups) - Контроллеры для работы с группами.
    - [`/privates`](/modules/bot/controllers/privates) - Контроллеры для работы с личными чатами.
  - [`/filters`](/modules/bot/filters) - Фильтры, используемые для обработки запросов перед передачей их контроллерам.
  - [`/models`](/modules/bot/models) - Модели данных, включая запросы к базе данных.
    - [`/queries`](/modules/bot/models/queries) - Запросы, используемые для извлечения и обработки данных.
  - [`/validators`](/modules/bot/validators) - Валидаторы для проверки входящих данных.
  - [`/views`](/modules/bot/views) - Представления для отображения информации в чатах.
    - [`/channels`](/modules/bot/views/channels) - Представления для каналов.
    - [`/groups`](/modules/bot/views/groups) - Представления для групп.
    - [`/privates`](/modules/bot/views/privates) - Представления для личных чатов.
  - [`Module`](/modules/bot/Module.php) - Главный компонент модуля, который настраивает и управляет всеми остальными компонентами.
  - [`WebHookAction`](/modules/bot/WebHookAction.php) - Класс, предназначенный для обработки входящих запросов (веб-хуков) от Telegram API.

### Процесс обработки запросов

#### 1. Получение запроса - Update

Бот ожидает 2 типа обьектов `Update` от Telegram API с использованием Webhook:

- [`CallbackQuery`](/modules/bot/components/api/Types/CallbackQuery.php) - Объект, представляющий запрос в ответ на нажатие inline-кнопки.
- [`Message`](/modules/bot/components/api/Types/Message.php) - Объект, представляющий сообщение от пользователя.

#### 2. Маршрутизация запроса

Запрос проходит через один из компонентов для маршрутизации, в зависимости от типа чата, который определяет, какие действия следует выполнить и создает маршрут к соответствующему контроллеру и действию. Компонент маршрутизации использует настройки маршрутизации для определения соответствия между командами и маршрутами. В настройках маршрутизации определяются правила роутинга, которые позволяют определить, какие команды отправляются на какие контроллеры и действия.

Компонент для маршрутизации позволяет боту обрабатывать команды, отправляемые пользователями, и выполнять соответствующие действия на основе правил маршрутизации. Если команда не обнаружена, то запрос попадает в дефолтный роут.

###### Структура команды из сообщения

Команды это сообщения которые начинаются с команды в виде `/controller_name__action_name?key1=value1&ke2=value2 text`.

- `/` - Слеш указывает на начало команды.
- `controller_name` - Название контроллера, который будет обрабатывать команду.
- `action_name` (опционально) - Название действия, которое будет выполнено в контроллере. Если не указано, используется действие по умолчанию (например, `index`).
- `?key1=value1&key2=value2` (опционально) - Параметры команды, передаваемые как часть команды. Эти параметры могут использоваться для указания дополнительной информации или настроек. Эти параметры передаются в обьект `Request`.
- `text` (опиционально) - Переменная, которая передается в контроллер и содержит весь текст, следующий после команды. Это позволяет контроллерам обрабатывать текст, введенный пользователем.

Массивы с индексами, используемые вместо названий контроллеров и действий, используются для сопоставления числовых значений в правилах маршрутизации фактическим именам контроллеров и действий. Например, если команда содержит числовой код вместо имени контроллера или действия, то числовой код будет преобразован в соответствующее название контроллера или действия.

#### 3. Выполнение запроса

`Module` вызывает соответствующий контроллер и действие на основе полученного маршрута. Все действия контроллеров возвращают массив объектов, наследуемых от класса `Command`.

После выполнения действия, Module отправляет каждую команду, которые представляют собой запросы к Telegram API.

Класс `Command` - это класс, который знает, как отправить определенный запрос к Telegram API.

#### 4. Отправка запроса к Telegram API

После создания ответа, он отправляется в Telegram API через компонент `BotApi`.

#### 5. Очистка предыдущих сообщений (для личных чатов)

В личных чатах, бот автоматически удаляет свои предыдущие сообщения, не связанные с текущим запросом, и также удаляет все сообщения пользователя. При этом для вывода сообщения бота используется команда `editMessageTextOrSendMessage`. Когда необходимо отправить несколько сообщений (например, сперва отобразить текстовое сообщение «Ваше местонахождение», затем отправить сообщение с геолокацией, а затем сообщение с клавиатурой), то следует сперва вызвать команду `editMessageTextOrSendMessage`, а затем команды `sendLocation` и `sendMessage`.

У самого модуля существует четыре поля, которые идентифицируют пользователя и могут вам пригодиться: `update`, `user`, `telegramUser` и `telegramChat`. Для простоты доступа, в базовом классе контроллеров определены методы для получение данных полей.

### Callback data

Значения для callback кнопок должны быть в пределах 1-64 байт. [Подробнее](https://core.telegram.org/bots/api#inlinekeyboardbutton).

Это ограничение обходится с использованием индексов массивов вместо имен контроллеров и действий, в файлах конфигураций.

### CrudController

Используется для быстрой инициализации режимов добавления и редактирования обьектов в p2p сервисах бота. Данный контроллер берет на себя основную часть рутинной работы, следуя правилам из метода `rules`.

Для подключения:

* Наследуйте ваши контроллеры от данного контроллера.
* Создавайте в ваших контроллерах методы `rules` и `afterSave`.

Примеры подключения можно увидеть в сервисах:

- [Обьявления](/modules/bot/controllers/privates/AdController.php)
- [Обмен наличных](/modules/bot/controllers/privates/CaController.php)
- [Обмен валют](/modules/bot/controllers/privates/CeController.php)
- [Знакомства](/modules/bot/controllers/privates/DaController.php)
- [Работа](/modules/bot/controllers/privates/JoController.php)

## Core

- `FLOAT`
  - Работая с `float` в PHP всегда используйте [Функции BC Math](https://www.php.net/manual/ru/ref.bc.php).
    - Для удобного сравнения двух числе используйте [`helpers\Number`](/helpers/Number.php). [Почему это важно?](https://stackoverflow.com/questions/3148937/compare-floats-in-php)
    - Если в БД таблице есть DECIMAL поле - добавьте трейт [`models\traits\FloatAttributeTrait`](/models/traits/FloatAttributeTrait.php) в `ActiveRecord`.
    - Учитывайте эту проблему в любом языке программирования ([MySql](https://stackoverflow.com/questions/2188139/check-for-equality-on-a-mysql-float-field), [JS](https://stackoverflow.com/questions/3343623/javascript-comparing-two-float-values/3343658), etc.)
