Кеширование ID инфоблоков

в 18:47, , рубрики: 1c-битрикс, 1С-Битрикс, php, инфоблоки, метки: ,

Битрикс, как платформа, содержит в себе несколько неудобных для разработчика мест. Об одном таком месте, которое мне давно хотелось «почесать», и пойдёт повествование в данном топике. Кому интересно добро пожаловать под кат.

То самое место

Я думаю ни для кого не является секретом, что использовать в вызовах API ID инфоблоков или же ID их свойств, а так же ID значений свойств типа «список», является не очень хорошей практикой разработки под Битрикс. Если это для кого-то секрет, я поясню. Дело в том, что у данной техники есть несколько неприятных минусов:

  1. ID — это всего лишь числовой идентификатор, и в вызовах API больше напоминает магическое число. Иначе говоря, ни какой ясности относительно того какой инфоблок, свойство или значение свойства под данным идентификатором подразумевается он не вносит, что негативно сказывается на общей читаемости кода.
  2. При разработке готовых решений для «1С-Битрикс» использование в вызовах API идентификаторов инфоблоков и т.п. крайне не рекомендуется, поскольку при установке вашего решения на платформу пользователя именно его платформа решает какие будут ID у инфоблоков, их свойств и т.п. Поэтому они могут отличаться от тех, что вы изначально прописали в вызовах API.

Я при первом знакомстве с Битриксом не видел в этих ограничениях особой проблемы. Ведь у инфоблоков и свойств инфоблоков есть код (CODE), а у значений свойств типа «список» есть внешний код (XML_ID), которые задаются разработчиком. Но с опытом и всё большим погружением в API Битрикса, выяснилось что и тут не обошлось без подводных камней. Поэтому идея использовать символьный код вместо числового идентификатора в вызовах API оказалась не идеальной.

Первый и наиболее серьёзный камень проявляется в том, что не все методы API модуля инфоблоков поддерживают символьные коды. Многие методы (особенно для работы со свойствами инфоблоков) поддерживают только идентификаторы, среди этих методов:

Второй подводный камень кроется в событиях модуля инфоблоков, а если быть точнее в:

В обработчики данных событий передаётся массив $arFields, содержащий в себе поля и свойства создаваемого или изменяемого элемента инфоблока. Камень же лежит в массиве $arParams['PROPERTY_VALUES'], в котором хранятся значения свойств элементов, а в виде ключей используются ID свойств инфоблоков.

[PROPERTY_VALUES] => Array
    (
      [33] => Array
        (
          [0] => Array
            (
              [VALUE] => 24
            )
        )
    )

И последний камень лежит в методе CIBlockElement::GetList и проявляет себя главным образом при попытке отфильтровать элементы инфоблока по значениям свойств типа «список». В документации к API описывается только 2 пути построения фильтра по значениям свойств типа «список»:

  1. Передача в качестве значения фильтра ID значения свойства,
  2. Передача в качестве значение фильтра строковое значение свойства.

Второй вариант, по мне честно говоря, является чем-то несуразным, потому что я считаю, что строковое значение свойства создаётся в первую очередь для контент-менеджера, будущего заполнять сайт, а не для организации фильтра по данному свойству. Более того фильтрация по строковому значению будет работать значительно медленней, чем по числовому идентификатору.

Вот тут вот и возникает желание почесать затылок: получается, что использовать идентификаторы вроде как нельзя, а с другой стороны во многих задачах без их использования не обойтись.

Как «почесать»

Самое простое, что можно придумать в данной ситуации — это сделать перечисления идентификаторов инфоблоков, а их свойств и значений списков в виде статических классов со списком констант.

// Перечисление ID инфоблоков
class MyIBlocks{
    const News = 1;
    const Products = 2;
}

// Перечисление ID свойств
class MyProperties{
    const Weight = 10;
    const Photos = 11;
}

// Перечисление ID свойств типа "список"
class MyEnums{
    const Prop1Yes = 1;
    const Prop1No = 2;
}

$newsIBlockId = MyIBlocks::News;
$weightPropertyId = MyProperties::Weight;
$prop1YesValueId = MyEnums::Prop1Yes;

Это решение самое простое и я бы сказал идеальное, но вот только не в нашем грешном мире. Очень редко получается определить структуру инфоблоков с первого раза так, чтобы потом не изменять, не добавлять новые свойства. А после каждого изменения нам придётся в ручную править наши классики-перечисления, чтобы держать их в актуальном виде. Если же говорить о готовом решении для битрикса, то наши перечисления вообще не решают проблемы, потому что мы не можем заранее знать какие идентификаторы инфоблоков, свойств инфоблоков и значений списков будут у пользователя нашего решения.

Как «почесать» так, чтобы больше не чесалось

Сталкиваясь с этой проблемой и запинаясь о её подводные камни на различных проектах, я разработал класс CIBlockTools, самостоятельно сканирующий структуру инфоблоков и позволяющий получить значение ID инфоблоков и т.п. в зависимости от их символьного кода.

CIBlockTools позволяет:

  1. Получить ID инфоблока по его коду,
  2. Получить ID свойства инфоблока по коду инфоблока и коду свойства,
  3. Получить ID значение списка по коду инфоблока, коду свойства и внешнему идентификатору значения.

C помощью CIBlockTools более не надо записывать ID инфоблоков напрямую в вызовах API как магические числа, например:

$dbProducts = CIBlockElement::GetList(
    false,
    array(
        'IBLOCK_ID' => CIBlockTools::GetIBlockId('news'),
        'PROPERTY_TOPICAL' => CIBlockTools::GetPropertyEnumValueId('news', 'TOPICAL', 'yes')
    )
);

Принцип работы CIBlockTools прост. При первом использовании он сканирует структуру инфоблоков и сохраняет необходимые данные в кэше битрикса. После этого класс берёт данные из кэша, не обращаясь к БД. При любом изменении инфоблоков и их свойств кэш автоматически сбрасывается.

Туториал по использованию можно посмотреть на Github

Автор: xescoder

Источник

Поделиться

* - обязательные к заполнению поля