Приветствую всех неравнодушных! В статье я расскажу, как мы смогли подружить сложные D7 свойства инфоблоков с нашей IDE.
Есть в одном проекте такая волшебная штука, как подборы. В них столько свойств, что обычный getList() по 30 записям съедает 6 Гб оперативной памяти, а для оптимизации этого монстра приходится использовать ядро D7. Что же может нам рассказать интернет о том, как правильно обращаться к свойствам инфоблоков, чтобы проект не «ушел отдыхать», обидевшись на всех?
1. Изучим концепцию
Нам, как во многих фреймворках, предлагают описать нашу сущность, создать модель и по ней уже баловаться, как пожелаем. Но в нашей сущности получается 570 полей. Одно описание этих полей займет о-о-ой как много времени — не наш вариант.
2. Стандартное решение
Идем дальше, берем решение с динамической генерацией свойств через DataManager. Но ядро изначально не умеет работать со свойствами, о чем мы можем узнать из этой статьи, где попытались «натянуть сову на глобус» и сделать самописное решение.
3. Решение от битрикс-разработчиков
Битрикс взяли все в свои руки и такие: «А мы вам нарисуем, как все делается, потому что в документацию и аннотацию кода — это долго и кропотливо».
Каждый инфоблок является самостоятельным типом данных со своим собственным набором свойств. В ORM он представляется отдельной сущностью
Если вам этого мало, то мы еще магическими методами __call позволим вам выбирать свойства через генерируемые на лету таблицы. Решение интересное, если не учитывать то, что далеко не все разработчики, работающие с битриксом, понимают, как это работает, ну и IDE, конечно, не будет дружить с магией, что усложняет использование, когда все надо держать в голове.
Ура, проблема решена!
Скрытый текст
Осталось, рассказать IDE — как это все работает…
4. Итак, что мы имеем?
Мы можем создать псевдокласс, который должен содержать сущность инфоблока, и давать нам генерируемую на лету коллекцию классов элементов.
Loader::includeModule('iblock'); $this->iblock = Iblock::wakeUp($this->iblockEnum->value); $this->iblock->fill('API_CODE'); return $this->iblock->getEntityDataClass()::getList($parameters)->fetchCollection();
Скрытый текст
Даже не пытайтесь вывести на var_dump содержимое коллекции 😉
Или такую запись для одного элемента:
Loader::includeModule('iblock'); $this->iblock = Iblock::wakeUp($this->iblockEnum->value); $this->iblock->fill('API_CODE'); return $this->iblock->getEntityDataClass()::getByPrimary(primary: $primary, parameters: $parameters)->fetchObject();
И, конечно же, IDE понятия не имеет, что возвращают такие методы и что в них содержится:
На что разработчики Битрикса дают совет: “что вы, как дети? Просто используйте аннотации — и будет вам счастье!”.
Мы, как добропорядочные граждане, добавляем эти аннотации, и IDE такая: «Ребята, вы что там курите? Я, как бы, не умею компилировать код, и смотреть созданный динамически класс”.
5. Уточнения
Дальше умные дяди копают ядро, пишут попытку в документацию и выясняют, что одних только свойств получить мало — они могут быть аж четырех типов и коллекцией:
-
ITEM — значение свойства типа «список»;
-
ELEMENT — привязанный элемент;
-
SECTION — привязанный раздел;
-
FILE — файл.
Если свойство множественное, то getSomePropertyCode() вернет коллекцию.
Причем, у каждого из перечисленных типов есть свои методы. Например:
-
FILE — getFile()->getSource() и т. д., о чем можно узнать только на просторах интернета.
-
ITEM — getItem()->getXmlId()
-
ELEMENT — getElement()->getEntity() и т. д.по иерархии…
Однако IDE понятия не имеет, какие у них методы.
6. Решение
Для сущностей я создал псевдокласс, который возвращает мне и сам генерируемый класс, и класс-подмен, откуда будет браться иерархия методов.
В принципе, достаточно в phpDoc-блоке @var указывать псевдокласс, а его — наследовать от ORM-сущности. Приведу пример: сущность (элемент)
namespace App\Domains\<Selections>\Entities; use App\Shared\Contracts\Entities\Properties\BaseInterface; use App\Shared\Contracts\Entities\Properties\ItemInterface; use Bitrix\Iblock\EO_Section_Collection; use Bitrix\Iblock\EO_Section_Entity; use Bitrix\Main\ORM\Objectify\EntityObject; /** * * @method BaseInterface getProcessingVersion() * @method BaseInterface getIsProcessed() * @method BaseInterface getIsSentTo<RemoteService>() * @method BaseInterface getId<RemoteService>() * @method BaseInterface getClient() * @method BaseInterface getIsSentTo<RemoteService>() * @method BaseInterface getDateSentTo<RemoteService>() * @method ItemInterface getSource() * @method ItemInterface getCallStatus() * @method EO_Section_Entity getIblockSection() * @method EO_Section_Collection getSections() */ class ElementEntity extends EntityObject { }
Здесь, согласно статьям выше, мы описываем методы для необходимых нам сущностей, которые получаются магическими методами getPropertyCode() и указываем им один из 4х типов интерфейсов, для которых также подготавливаем дочерние интерфейсы:
5.1 Базовый интерфейс
namespace App\Shared\Contracts\Entities\Properties; /** * * Свойства элементов * Свойства можно получить методом getXyz, где Xyz - CamelCased-код свойства. * У свойств есть VALUE и DESCRIPTION, которые можно получить методами getValue и getDescription соответственно. * Для некоторых типов добавляются дополнительные поля для доступа к дополнительной информации * (ITEM - значение свойства типа список, ELEMENT - привязанный элемент, SECTION - привязанный раздел, FILE - файл). *@see https://mrcappuccino.ru/blog/post/iblock-elements-bitrix-d7 */ interface BaseInterface { public function getValue(): mixed; public function setValue(mixed $value): void; public function getDescription(): string|null; public function setDescription(string $description): void; public function hasDescription(): bool; }
5.2 Интерфейс элемента
namespace App\Shared\Contracts\Entities\Properties; use Bitrix\Iblock\EO_Element; /** * PROPERTY.ELEMENT */ interface ElementInterface extends BaseInterface { public function getElement(): EO_Element; }
5.3 Файлы
namespace App\Shared\Contracts\Entities\Properties; use Bitrix\Main\EO_File; /** * PROPERTY.FILE */ interface FileInterface extends BaseInterface { public function getFile(): EO_File; }
5.4 Итем
namespace App\Shared\Contracts\Entities\Properties; /** * PROPERTY.ITEM */ interface ItemInterface extends BaseInterface { public function getItem(): ItemActionsInterface; }
5.4.1 Который, в свою очередь, имеет свой набор методов внутри getItem()
namespace App\Shared\Contracts\Entities\Properties; use Bitrix\Iblock\EO_PropertyEnumeration_Collection; interface ItemActionsInterface { public function getId(): int; public function getXmlId(): string; public function getValue(): string; /** * Метод доступен, если getItem вернет коллекцию. * @see EO_PropertyEnumeration_Collection * @return array */ public function getAll(): array; }
Далее, если нам возвращается коллекция, то мы, по такому же методу, как и у элемента, делаем псевдокласс. В этом классе описываем, какие именно псевдоэлементы нам приходят:
namespace App\Domains\<Selections>\Entities; use Bitrix\Main\ORM\Objectify\Collection; /** * @method ElementEntity[] getAll() */ class CollectionEntity extends Collection { }
И IDE теперь точно знает из документации, что нам при помощи getAll() возвращается массив из псевдоэлементов инфоблока подборов, который, в свою очередь, имеет иерархию своих методов из интерфейсов.
6 Результат
6.1 Возвращает класс, в котором методы коллекций;
6.2 Возвращает коллекцию элементов конкретно этого инфоблока;
6.3 Возвращает свойство инфоблока и методы работы со свойством;
6.4 Возвращает элемент или коллекцию, в случае, когда поле множественное;
6.5 Возвращает методы для работы с итемом.
IDE, наконец-то, понимает, что от него хотят, и будущие поколения разработчиков вознесут хвалу тому, кто опишет, что находится в этих генерируемых на лету классах.
Бонусом вы сможете видеть, какие свойства есть у инфоблока:
Приятного аппетита, кушайте на здоровье!
ссылка на оригинал статьи https://habr.com/ru/articles/865982/
Добавить комментарий