Аннотация классов в ORM bitrix

Объекты ORM, утилита генерации

С 18 версии в ORM Битрикс появились объекты.
О работе с объектами подробно можно узнать из курса
А также из видео партнерских конференций:
https://youtu.be/QmAf1xxkX0Q
https://youtu.be/mnX8oeE_lO0

Класс объекта создается в процессе выполнения кода, а практически все методы основываются на «магических» методах (__call, __get).
В результате чего IDE (например, phpStorm) будет считать обращение к таким методам ошибкой, а также не будет выводить подсказки. Для решения проблемы был выпущен bitrix cli с поддержкой команды orm:annotate. Инструмент предназначен для работы через командную строку, требует наличия библиотеки symfony/console (устанавливается через composer).

Процесс установки описан в курсе:
https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=4637
В этом же курсе приведены примеры использования:
https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=11733

В результате вызова команды orm:annotate мы получаем файл /bitrix/modules/orm_annotations.php который содержит описание всех генерируемых классов ORM в формате phpdoc, IDE видит этот файл (если он находится в рамках проекта) и начинает выводить все необходимые подсказки.

Правила генерации

Аннотация генерируется только для классов, которые заканчиваются на Table и являются наследниками класса \Bitrix\Main\ORM\Data\DataManager (либо его алиаса для старых версий Bitrix\Main\Entity\DataManager).
Поиск классов, соответствующих указанным правилам, осуществляется в нескольких местах:

  1. В get_declared_classes()
  2. Рекурсивно в директориях lib для указанных модулей
    php bitrix.php orm:annotate -m tasks forum
    • Формируется список директорий (папки lib) в которых будет производится поиск
      /bitrix/modules/tasks/lib/
      /bitrix/modules/tasks/dev/lib/
      /local/modules/forum/lib/
      /local/modules/forum/dev/lib/
      Если директория не существует, то она исключается из списка.  Далее для каждой директории:
    • Вызывается get_declared_classes(), чтобы определить, какие классы существовали до обхода директории. Результат сохраняется в переменную
    • Подключаются все php файлы внутри директории и поддиректорий.
    • Далее снова вызывается get_declared_classes(), значение сравнивается с полученным ранее и для новых классов выполняется проверка.
  3. После вызова события onVirtualClassBuildList модуля main.
    Событие работает только для registerEventHandler (addEventHandler в данном случае использовать не получится), т.к. установлен фильтр для передаваемых модулей
    Например, для строки
     php bitrix.php orm:annotate -m tasks
    Будет вызвано только событие, которое зарегистрировал модуль tasks.
    Логика следующая:
    • Вызывается get_declared_classes(), чтобы определить, какие классы существовали до вызова события
    • Вызывается событие
    • Далее снова вызывается get_declared_classes(), значение сравнивается с полученным ранее и для новых классов выполняется проверка.

Ограничение

В соответствии с описанным выше, аннотация классов не будет сгенерирована в случае, если классы системы не принадлежат модулям и подключаются, например, по psr-4 через composer или через \Bitrix\Main\ Loader::registerAutoLoadClasses(null, …)

Событие onVirtualClassBuildList

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

namespace Pixelplus\OrmAnnotations;

use Bitrix\Main\EventManager;

class Handlers
{
    public static function onVirtualClassBuildList()
    {
        //Основной код
    }

    public static function registerHandler()
    {
        EventManager::getInstance()->registerEventHandler(
            'main',
            'onVirtualClassBuildList',
            'main',
            self::class,
            'onVirtualClassBuildList'
        );
    }
}

Разово вызываем метод

\Pixelplus\OrmAnnotations\Handlers::registerHandler(). 
Для отдельного модуля это можно сделать при установке, в качестве toModuleId указывая свой модуль.

Внутри метода \Pixelplus\OrmAnnotations\Handlers::onVirtualClassBuildList

Для классов без модуля, зарегистрированных через Bitrix\Main\Loader::registerAutoLoadClasses, вызываем class_exists, таким образом они попадают в get_declared_classes(). Альтернативный вариант – где-то сохранить список классов, добавляемых в загрузчик.

$rp = new \ReflectionProperty('Bitrix\Main\Loader', 'arAutoLoadClasses');
$rp->setAccessible(true);
$classes = $rp->getValue();
$rp->setAccessible(false);

foreach ($classes as $class => $classData) {
	if (!$classData['module']) {
		class_exists($class);
	}
}

Для классов, загруженных через composer по psr-4 

psr.png

(например такого)

  • Подключаем composer через composer =) composer require composer/composer
  • Используем из библиотеки класс \Composer\Autoload\ClassMapGenerator для получения карты классов
  • Для каждой директории psr-4, psr-0, описанной в composer.json генерируем карту классов
  • Для каждого класса вызываем class_exists
if (class_exists('\Composer\Autoload\ClassMapGenerator')) {
	$composerJsonFile = $_SERVER['DOCUMENT_ROOT'].'/local/composer.json';

	$map = [];
	if (file_exists($composerJsonFile)) {
		$jsonContent = json_decode(file_get_contents($composerJsonFile), true);

		if (array_key_exists('psr-4', $jsonContent['autoload'])) {
			foreach (['psr-4', 'psr-0'] as $psrType) {
				foreach ($jsonContent['autoload'][$psrType] as $path) {
					if (substr($path, 0, 1) != '/') {
						$path = dirname($composerJsonFile).'/'.$path;
					}
					$map = array_merge($map, array_keys(\Composer\Autoload\ClassMapGenerator::createMap(
						$path
					)));
				}
			}
		}
	}
	foreach ($map as $class) {
		class_exists($class);
	}
}
где $composerJsonFile - полный путь к файлу composer.json

Таким образом все наши дополнительные классы попадут в файл аннотаций



Назад в раздел


С чего начинается продвижение сайта, старт работ по поисковому продвижению и раскрутке сайта в компании Пиксель Плюс
Начало работ по поисковому продвижению сайта в компании «Пиксель Плюс». Базовые понятия.
Необходимость ежемесячной оплаты работ по поисковому продвижению сайта. Основные работы по сайту для его эффективной раскрутки и себестоимость работ
Я бы хотел заплатить за продвижение своего сайта 1 раз и быть высоко в выдаче по конкурентным запросам всегда, возможно ли такое?
Продвижение по трафику: вопросы клиентов и ответы на них
Ряд вопросов по продвижению сайта по трафику. Нюансы тарификации, расчёта стоимости работ, абонентской оплаты.
Часто задаваемые вопросы по веб-аналитике (FAQ)
Вопросы, которые часто задаются заказчиками услуги по веб-аналитике и оказанию самой услуги. Что такое веб-аналитика? Зачем проекту нужна веб-аналитика? Зачем нужно определять KPI и какие они бывают? И так далее.
Какие работы НЕ входят в SEO в случае продвижения в «Пиксель Плюс»?
Поисковое продвижение включает в себя большой перечень работ, необходимый для получения максимальных результатов... Но какие же работы не входят в платеж на SEO?
Время продвижения и внесения изменений в результаты продвижения сайта, скорость реагирования Яндекса (Yandex) на внесение изменений на сайте
Я оплатил услуги продвижения сайта на месяц. Прошло уже 10 дней и позиции в Яндексе не улучшились, вы там работаете или нет?