Особенности работы с коллекциями
Общие моменты
В основной статье приводятся методы Item
для работы с коллекциями (контактами, товарами, наблюдателями). Если вы ищете описание API, то обратитесь к ней. Тут разговор пойдет о том, как реализуется работа с данными сущностями в Item
, особенностях некоторых методов и о других нюансах.
Работа с коллекциями основывается на использовании ORM. EntityObject
, который используется внутри Item
, имеет доступ к связанным записям через стандартный механизм отношений в ORM. В случае контактов — это записи о связях, для товарных позиций и наблюдателей — записи соответствующих таблиц (подробнее про данные в каждом соответствующем разделе). Таблет элемента имеет в описании поле OneToMany с необходимым именем (например, Item::FIELD_NAME_CONTACT_BINDINGS для контактов и Item::FIELD_NAME_PRODUCTS для товарных позиций), ссылающееся на какой-то таблет. Через него Item
управляет информацией о том, какие контакты привязаны к элементу.
Так же для контактов, товарных позиций и наблюдателей внутри Item
реализованно множество методов вида remindActualContacts
, unsetContactBindings
, isChangedObservers
. Они необходимы для корректной работы общих методов Item::remindActual, Item::unset
и др.
Работа с контактами
В CRM множественные контакты хранятся особым образом. Для обеспечения связи "многие ко многим" используются отдельные таблицы, в которых хранятся записи о связи между контактами и другими элементами CRM. Эти таблеты находятся в неймспейсе \Bitrix\Crm\Binding
.
Записи о связи имеют особый формат, который во многом диктуется методами класса \Bitrix\Crm\Binding\EntityBinding
.
Сами ORM-объекты контактов, получаемые через методы getContacts
и др., загружаются внутри Item
на основе записей о связи с помощью брокера контактов. Это позволяет уменьшить количество запросов в базу при частых использовании методов получения и привязки/отвязки контактов.
Алгоритм работы bindContacts
и unbindContacts
Метод bindContacts
используется для того, чтобы привязать к элементу контакты. В качестве аргумента ему передаются массивы связи в формате, отдаваемом методами класса \Bitrix\Crm\Binding\EntityBinding
.
Алгоритм его работы можно описать следующим образом:
- Нормализуем полученные через аргумент массивы связи.
- Получаем записи связи об привязанных на данный момент контактах.
- Сравниваем полученные массивы связи и текущие записи. Если необходимо, то обновляем текущие записи или создаем новые.
- Проверяем, что среди привязанных контактов ровно один из них помечен как основой (primary). Если основного контакта нет или их несколько, делаем ровно один из них основным.
Метод unbindContacts
используется для того, чтобы отвязать уже привязанные контакты от элемента. В качестве аргумента он так же принимает массивы связи в формате \Bitrix\Crm\Binding\EntityBinding
.
Алгоритм его работы практически идентичен таковому у bindContacts
. Единственное отличие в том, что при сравнении полученных массивов связи текущие записи удаляются, а не обновляются/создаются.
Абстрактные методы, использующиеся при работе с контактами
При работе с контактами используется несколько абстрактных методов. Так как разные типы сущности могут использовать разные таблицы для хранения записей о связях, в этим методах инкапсулируются детали работы Item
с упоминаемым выше полем связи OneToMany(Item::FIELD_NAME_CONTACT_BINDINGS)
.
abstract protected function getContactBindingTableClassName(): string
- метод должен вернуть FQN класса таблета, в котором хранятся записи о связи элемента и контактов (например, \Bitrix\Crm\Binding\EntityContactTable
).
abstract protected function compilePrimaryForBinding(array $contactBinding): array
- метод должен сформировать ассоциативный массив со значениями первичных ключей на основе предоставленного массива связи. Формат возвращаемого массива с первичными ключами напрямую зависит от используемого таблета связей, который возвращается в методе getContactBindingTableClassName
.
Работа с товарными позициями
Элемент CRM может иметь товарные позиции, привязанные к нему. Они хранятся в таблице b_crm_product_row (таблет \Bitrix\Crm\ProductRowTable
).
Особенности работы setProductRows
, addToProductRows
Так как записи в БД о товарных позициях имеют сложную структуру со множеством полей, то при их сохранении особое внимание уделяется нормализации данных. Именно поэтому методы добавления/изменения товарных позиций возвращают объекты класса \Bitrix\Main\Result
- в них будут собираться ошибки, которые возникли в процессе нормализации.
Таким образом, если в процессе добавления/изменения товарной позиции при нормализации возникла ошибка, то такая товарная позиция добавлена не будет.
Абстрактные методы, использующиеся при работе с товарными позициями
protected function getItemReferenceFieldNameInProduct(): ?string
- метод должен вернуть название поля, под которым в таблете \Bitrix\Crm\ProductRowTable
записано поле типа Reference, ссылающееся на таблет элементов. Допустим, что наши элементы хранятся в таблете CoolEntityTypeTable
. А в ProductRowTable
есть поле new Reference('COOL_ENTITY_TYPE', CoolEntityTypeTable::class, ...). В таком случае соответствующий наследник Item
для этого типа сущности должен вернуть 'COOL_ENTITY_TYPE'.
Работа с наблюдателями
С точки зрения данных, наблюдатели — это массив ID пользователей, которые наблюдают за каким-то элементом CRM. Между элементами и пользователями реализуется связь "многие ко многим". Для хранения записей о том, к какому элементу привязаны какие наблюдатели, используется отдельная таблица b_crm_observer (\Bitrix\Crm\Observer\Entity\ObserverTable
). Помимо ссылок на элемент CRM и пользователя, в ней также хранятся поля сортировки и даты добавления и последнего изменения наблюдателя.
Алгоритм работы setObservers
- Нормализуем входящие данные.
- Получаем данные о том, какие пользователи сейчас являются наблюдателями.
- Сравниваем полученные данные с текущими. Если наблюдатель не был передан, отвязываем его. Если был передан новый, то создаем новую запись. Если передан уже существующий, обновляем время последнего изменения наблюдателя.
При вызове setObservers
сортировка наблюдателей соответствует сортировке в переданном массиве. Т.е. тот ID, который в переданном массиве был первым, будет сохранен с минимальной сортировкой, и т.д.
Пользовательские комментарии
Мы будем рады, если разработчики добавят свои комментарии по практическому использованию методов системы.Для этого нужно всего лишь авторизоваться на сайте
Но помните, что Пользовательские комментарии, несмотря на модерацию, не являются официальной документацией. Ответственность за их использование несет сам пользователь.
Также Пользовательские комментарии не являются местом для обсуждения функционала. По подобным вопросам обращайтесь на форумы.