Записки разработчика: как подружить D7 свойства и IDE

от автора

Приветствую всех неравнодушных! В статье я расскажу, как мы смогли подружить сложные 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *