Полный цикл в digital

Свой модуль для Битрикс24 с REST API

Готовый код можно скачать в моем репозитории на GitFlic.

Cоздадим модуль, который в момент инсталяции подключится к системному событию OnRestServiceBuildDescription и через него объявит новый scope, логическую группу со списком собственных методов, более подробно почитать про добавление своих методов:

hmarketing.rest/install/index.php<?php
// пространство имен для подключений ланговых файлов
use Bitrix\Main\Localization\Loc;
// пространство имен для управления (регистрации/удалении) модуля в системе/базе
use Bitrix\Main\ModuleManager;
// пространство имен для работы с параметрами модулей хранимых в базе данных
use Bitrix\Main\Config\Option;
// пространство имен с абстрактным классом для любых приложений, любой конкретный класс приложения является наследником этого абстрактного класса
use Bitrix\Main\Application;
// пространство имен для работы c ORM
use \Bitrix\Main\Entity\Base;
// пространство имен для автозагрузки модулей
use \Bitrix\Main\Loader;
// пространство имен для событий
use \Bitrix\Main\EventManager;

// подключение ланговых файлов
Loc::loadMessages(__FILE__);

/**
 * Class Hmarketing_Rest
 */

class Hmarketing_Rest extends CModule
{
    // переменные модуля
    public  $MODULE_ID;
    public  $MODULE_VERSION;
    public  $MODULE_VERSION_DATE;
    public  $MODULE_NAME;
    public  $MODULE_DESCRIPTION;
    public  $PARTNER_NAME;
    public  $PARTNER_URI;
    public  $SHOW_SUPER_ADMIN_GROUP_RIGHTS;
    public  $MODULE_GROUP_RIGHTS;
    public  $errors;

    // конструктор класса, вызывается автоматически при обращение к классу
    function __construct()
    {
        // создаем пустой массив для файла version.php
        $arModuleVersion = array();
        // подключаем файл version.php
        include_once(__DIR__ . '/version.php');
        // версия модуля
        $this->MODULE_VERSION = $arModuleVersion['VERSION'];
        // дата релиза версии модуля
        $this->MODULE_VERSION_DATE = $arModuleVersion['VERSION_DATE'];
        // id модуля
        $this->MODULE_ID = "hmarketing.rest";
        // название модуля
        $this->MODULE_NAME = Loc::getMessage('MODULE_NAME');
        // описание модуля
        $this->MODULE_DESCRIPTION = Loc::getMessage('MODULE_DESCRIPTION');
        // имя партнера выпустившего модуль
        $this->PARTNER_NAME = Loc::getMessage('PARTNER_NAME');
        // ссылка на рисурс партнера выпустившего модуль
        $this->PARTNER_URI = Loc::getMessage('PARTNER_URI');
        // если указано, то на странице прав доступа будут показаны администраторы и группы
        $this->SHOW_SUPER_ADMIN_GROUP_RIGHTS = 'Y';
        // если указано, то на странице редактирования групп будет отображаться этот модуль
        $this->MODULE_GROUP_RIGHTS = 'Y';
    }

    // метод отрабатывает при установке модуля
    function DoInstall()
    {
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // регистрируем модуль в системе
        ModuleManager::RegisterModule($this->MODULE_ID);
        // создаем таблицы баз данных, необходимые для работы модуля
        $this->InstallDB();
        // регистрируем обработчики событий
        $this->InstallEvents();
        // подключаем скрипт с административным прологом и эпилогом
        $APPLICATION->includeAdminFile(
            Loc::getMessage('INSTALL_TITLE'),
            __DIR__ . '/instalInfo.php'
        );

        return true;
    }

    // метод отрабатывает при удалении модуля
    function DoUninstall()
    {
        // глобальная переменная с обстрактным классом
        global $APPLICATION;
        // удаляем таблицы баз данных, необходимые для работы модуля
        $this->UnInstallDB();
        // удаляем обработчики событий
        $this->UnInstallEvents();
        // удаляем файлы, необходимые для работы модуля
        $this->UnInstallFiles();
        // удаляем регистрацию модуля в системе
        ModuleManager::UnRegisterModule($this->MODULE_ID);
        // подключаем скрипт с административным прологом и эпилогом
        $APPLICATION->includeAdminFile(
            Loc::getMessage('DEINSTALL_TITLE'),
            __DIR__ . '/deInstalInfo.php'
        );

        return true;
    }

    // метод для создания таблицы баз данных
    public function InstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // через класс Application получаем соединение по переданному параметру, параметр берем из ORM-сущности (он указывается, если необходим другой тип подключения, отличный от default), если тип подключения по умолчанию, то параметр можно не передавать. Далее по подключению вызываем метод isTableExists, в который передаем название таблицы полученное с помощью метода getDBTableName() класса Base
        if (!Application::getConnection(\Hmarketing\Rest\Models\DataTable::getConnectionName())->isTableExists(Base::getInstance("\Hmarketing\Rest\Models\DataTable")->getDBTableName())) {
            // eсли таблицы не существует, то создаем её по ORM сущности
            Base::getInstance("\Hmarketing\Rest\Models\DataTable")->createDbTable();
        }

        return true;
    }

    // метод для удаления таблицы баз данных
    public function UninstallDB()
    {
        // подключаем модуль для того что бы был видем класс ORM
        Loader::includeModule($this->MODULE_ID);
        // делаем запрос к бд на удаление таблицы, если она существует, по подключению к бд класса Application с параметром подключения ORM сущности
        Application::getConnection(\Hmarketing\Rest\Models\DataTable::getConnectionName())->queryExecute('DROP TABLE IF EXISTS ' . Base::getInstance("\Hmarketing\Rest\Models\DataTable")->getDBTableName());
        // удаляем параметры модуля из базы данных битрикс
        Option::delete($this->MODULE_ID);

        return true;
    }

    // метод для создания обработчика событий
    function InstallEvents()
    {
        EventManager::getInstance()->registerEventHandler(
            'rest',
            'OnRestServiceBuildDescription',
            $this->MODULE_ID,
            '\\Hmarketing\\Rest\\Handlers\\RestHandler',
            'restMethodsRegistration'
        );

        return true;
    }

    // метод для удаления обработчика событий
    function UnInstallEvents()
    {
        EventManager::getInstance()->unRegisterEventHandler(
            'rest',
            'OnRestServiceBuildDescription',
            $this->MODULE_ID,
            '\\Hmarketing\\Rest\\Handlers\\RestHandler',
            'restMethodsRegistration'
        );

        return true;
    }
}

Для каждого метода укажем в методе restMethodsRegistration() человекочитаемое название и обработчик в виде метода обертки:

hmarketing.rest/lib/Handlers/RestHandler.php<?php

namespace Hmarketing\Rest\Handlers;

use Bitrix\Main\Loader;
use Bitrix\Main\ObjectNotFoundException;
use Bitrix\Rest\RestException;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\DI\ServiceLocator;
use Psr\Container\NotFoundExceptionInterface;

Loc::loadMessages(__FILE__);

Loader::includeModule('rest');

class RestHandler
{
    const SCOPE = 'hmarketing.rest';
    const SERVICE = 'hmarketing.rest.service.locator';

    /**
     * Хендлер метод обработчика события регистрации REST методов,
     * добавляет в систему перечень кастомных методов REST
     * @return array[]
     */
    public static function restMethodsRegistration()
    {
        Loc::getMessage('REST_SCOPE');

        return [
            self::SCOPE => [
                self::SCOPE . '.add' => [__CLASS__, 'add'],
                self::SCOPE . '.list' => [__CLASS__, 'list'],
                self::SCOPE . '.update' => [__CLASS__, 'update'],
                self::SCOPE . '.delete' => [__CLASS__, 'delete'],
            ]
        ];
    }

    /**
     * @param $arParams - поля и значения для добавляемой сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return int
     * @throws RestException
     */
    public static function add($arParams, $navStart, \CRestServer $server)
    {
        $service = self::getService(self::SERVICE);

        return $service->add($arParams, $navStart, $server);
    }

    /**
     * @param $arParams - параметры для выборки по полям сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return array
     * @throws ArgumentException
     * @throws ObjectPropertyException
     * @throws SystemException
     */
    public static function list($arParams, $navStart, \CRestServer $server)
    {
        $service = self::getService(self::SERVICE);

        return $service->list($arParams, $navStart, $server);
    }

    /**
     * @param $arParams - поля сущности с значениями которые будут обновлены
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return int
     * @throws RestException
     */
    public static function update($arParams, $navStart, \CRestServer $server)
    {
        $service = self::getService(self::SERVICE);

        return $service->update($arParams, $navStart, $server);
    }

    /**
     * @param $arParams - обязательный ключ ID записи сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * * @param \CRestServer $server - объект с данными о сервере
     * @return bool
     * @throws RestException
     */
    public static function delete($arParams, $navStart, \CRestServer $server)
    {
        $service = self::getService(self::SERVICE);

        return $service->delete($arParams, $navStart, $server);
    }

    /**
     * Метод возвращает сервис если такой зарегистрирован в сервис-локаторе
     * или выбрасывает исключение если его там нет
     * @param string $code - код сервиса
     * @return mixed
     * @throws RestException
     * @throws ObjectNotFoundException
     * @throws NotFoundExceptionInterface
     */
    private static function getService(string $code)
    {
        if (!ServiceLocator::getInstance()->has($code)) {
            throw new RestException(
                json_encode(Loc::getMessage('EXCEPTION_SERVICE_LOCATOR'), JSON_UNESCAPED_UNICODE),
                RestException::ERROR_METHOD_NOT_FOUND, \CRestServer::STATUS_NOT_FOUND
            );
        }

        $meaning = \Bitrix\Main\Config\Option::get(
            "hmarketing.rest",
            "on-off",
            "",
            false
        );

        if ($meaning != "Y") {
            throw new RestException(json_encode(Loc::getMessage('EXCEPTION_MODULE_DESCRIPTION'), JSON_UNESCAPED_UNICODE), Loc::getMessage('EXCEPTION_MODULE_ERROR'));
        }

        return ServiceLocator::getInstance()->get($code);
    }
}

Для удобства подключим Service Locator, более подробно можно почитать тут:

hmarketing.rest/.settings.php<?php
/**
 * Регистрация класса как сервиса в ServiceLocator
 */
return [
    'services' => [
        'value' => [
            'hmarketing.rest.service.locator' => [
                'className' => '\\Hmarketing\\Rest\\Services\\StorageService',
            ]
        ],
        'readonly' => true,
    ]
];

Класс с методами который подключает Service Locator:

hmarketing.rest/lib/Services/StorageService.php<?php

namespace Hmarketing\Rest\Services;

use Bitrix\Main\ArgumentException;
use Bitrix\Main\ObjectPropertyException;
use Bitrix\Main\SystemException;
use Bitrix\Rest\RestException;
use Hmarketing\Rest\Models\DataTable;
use Hmarketing\Rest\Interface\EntityStorage;

class StorageService implements EntityStorage
{
    /**
     * @param $arParams - поля и значения для добавляемой сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return int
     * @throws RestException
     */
    public function add($arParams, $navStart, \CRestServer $server): int
    {
        $originDataStoreResult = DataTable::add($arParams);

        if ($originDataStoreResult->isSuccess()) {
            return $originDataStoreResult->getId();
        } else {
            throw new RestException(
                json_encode($originDataStoreResult->getErrorMessages(), JSON_UNESCAPED_UNICODE),
                RestException::ERROR_ARGUMENT, \CRestServer::STATUS_OK
            );
        }
    }

    /**
     * @param $arParams - параметры для выборки по полям сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return array
     * @throws ArgumentException
     * @throws ObjectPropertyException
     * @throws SystemException
     */
    public function list($arParams, $navStart, \CRestServer $server): array
    {
        return DataTable::getList([
            'filter' => $arParams['filter'] ?: [],
            'select' => $arParams['select'] ?: ['*'],
            'order' => $arParams['order'] ? [$arParams['order']['by'] => $arParams['order']['direction']] : ['ID' =>
                'ASC'],
            'group' => $arParams['group'] ?: [],
            'limit' => $arParams['limit'] ?: 0,
            'offset' => $navStart ?: 0,
        ])->fetchAll();
    }

    /**
     * @param $arParams - поля сущности с значениями которые будут обновлены
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * @param \CRestServer $server - объект с данными о сервере
     * @return int
     * @throws RestException
     */
    public function update($arParams, $navStart, \CRestServer $server): int
    {
        $entityId = intval($arParams['ID']);

        unset($arParams['ID']);

        $originDataStoreResult = DataTable::update($entityId, $arParams);

        if ($originDataStoreResult->isSuccess()) {
            return $originDataStoreResult->getId();
        } else {
            throw new RestException(
                json_encode($originDataStoreResult->getErrorMessages(), JSON_UNESCAPED_UNICODE),
                RestException::ERROR_ARGUMENT, \CRestServer::STATUS_OK
            );
        }
    }

    /**
     * @param $arParams - обязательный ключ ID записи сущности
     * @param $navStart - если в ключе start перезать число то будет использован offset
     * * @param \CRestServer $server - объект с данными о сервере
     * @return bool
     * @throws RestException
     */
    public function delete($arParams, $navStart, \CRestServer $server): bool
    {
        $entityId = intval($arParams['ID']);
        $originDataStoreResult = DataTable::delete($entityId);

        if ($originDataStoreResult->isSuccess()) {
            return true;
        } else {
            throw new RestException(
                json_encode($originDataStoreResult->getErrorMessages(), JSON_UNESCAPED_UNICODE),
                RestException::ERROR_ARGUMENT, \CRestServer::STATUS_OK
            );
        }
    }
}

Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!