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

Приветствую всех неравнодушных! В статье я расскажу, как мы смогли подружить сложные D7 свойства инфоблоков с нашей IDE. 

Есть в одном проекте такая волшебная штука, как подборы. В них столько свойств, что обычный getList () по 30 записям съедает 6 Гб оперативной памяти, а для оптимизации этого монстра приходится использовать ядро D7. Что же может нам рассказать интернет о том, как правильно обращаться к свойствам инфоблоков, чтобы проект не «ушел отдыхать», обидевшись на всех?

1. Изучим концепцию

Нам, как во многих фреймворках, предлагают описать нашу сущность, создать модель и по ней уже баловаться, как пожелаем. Но в нашей сущности получается 570 полей. Одно описание этих полей займет о-о-ой как много времени — не наш вариант.

2. Стандартное решение

Идем дальше, берем решение с динамической генерацией свойств через DataManager. Но ядро изначально не умеет работать со свойствами, о чем мы можем узнать из этой статьи, где попытались «натянуть сову на глобус» и сделать самописное решение.

1С-Битрикс Разработчикам — Свой вариант «CIBlockElement: GetProperty» на D7

dev. 1c-bitrix.ru

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 понятия не имеет, что возвращают такие методы и что в них содержится:

98f1326572f411b8f0549e8891833f0a.jpg

На что разработчики Битрикса дают совет: «что вы, как дети? Просто используйте аннотации — и будет вам счастье!».

5a6b44a08028e0fe7375343be0e38c98.png

Мы, как добропорядочные граждане, добавляем эти аннотации, и IDE такая: «Ребята, вы что там курите? Я, как бы, не умею компилировать код, и смотреть созданный динамически класс».

f2d5a468a0b2d80eda191f4cf0933e44.png

5. Уточнения

Дальше умные дяди копают ядро, пишут попытку в документацию и выясняют, что одних только свойств получить мало — они могут быть аж четырех типов и коллекцией:

  • ITEM — значение свойства типа «список»;

  • ELEMENT — привязанный элемент;

  • SECTION — привязанный раздел;

  • FILE — файл.

Если свойство множественное, то getSomePropertyCode () вернет коллекцию.

Причем, у каждого из перечисленных типов есть свои методы. Например:

  • FILE — getFile ()→getSource () и т. д., о чем можно узнать только на просторах интернета.

  • ITEM — getItem ()→getXmlId ()

  • ELEMENT — getElement ()→getEntity () и т. д.по иерархии… 

Работа с элементами инфоблоков в Битрикс D7

mrcappuccino.ru

Однако IDE понятия не имеет, какие у них методы.

6. Решение

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

14f9dc715b1b585d892487a0c1ed0840.png

В принципе, достаточно в phpDoc-блоке @var указывать псевдокласс, а его — наследовать от ORM-сущности. Приведу пример: сущность (элемент)

namespace App\Domains\\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()

* @method BaseInterface getId()

* @method BaseInterface getClient()

* @method BaseInterface getIsSentTo()

* @method BaseInterface getDateSentTo()

* @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\\Entities;

use Bitrix\Main\ORM\Objectify\Collection;

/**

* @method ElementEntity[] getAll()

*/

class CollectionEntity extends Collection

{

}

И IDE теперь точно знает из документации, что нам при помощи getAll () возвращается массив из псевдоэлементов инфоблока подборов, который, в свою очередь, имеет иерархию своих методов из интерфейсов.

6 Результат

01c18f62b37727c2dc036575005f0485.png

6.1 Возвращает класс, в котором методы коллекций;

6.2 Возвращает коллекцию элементов конкретно этого инфоблока;

6.3 Возвращает свойство инфоблока и методы работы со свойством;

6.4 Возвращает элемент или коллекцию, в случае, когда поле множественное;

6.5 Возвращает методы для работы с итемом.

IDE, наконец-то, понимает, что от него хотят, и будущие поколения разработчиков вознесут хвалу тому, кто опишет, что находится в этих генерируемых на лету классах.

Бонусом вы сможете видеть, какие свойства есть у инфоблока:

2210f6c21f96653994658df9f10bbaec.png

Приятного аппетита, кушайте на здоровье!

© Habrahabr.ru