Начало работы
Введение
Исторически так сложилось, что манипуляция данными об элементах основных сущностей CRM осуществляется с помощью массивов.
У этого подхода есть ряд недостатков:
- Названия полей у разных сущностей отличаются, хотя имеют одинаковый смысл. Надо всегда помнить их названия.
- Нет autocomplete в IDE.
- Нет типизации и др.
С точки зрения публичного интерфейса доступа к данным об элементе сущности, особенных различий между элементами разных сущностей нет. Общего гораздо больше.
Чтобы унифицировать подход, закрыть перечисленные выше недостатки, в новом API был введён абстрактный класс Bitrix\Crm\Item
.
Этот класс содержит в себе Bitrix\Main\ORM\Objectify\EntityObject
и имитирует его поведение.
Каждый тип сущности имеет свою реализацию этого класса, где содержатся особенности этого типа.
Класс воспроизводит интерфейс \ArrayAccess
, при обращении к объекту как к массиву, будут вызваны метод get
и set
.
Коды "общих" полей этого класса основаны на соответствующих кодах полей таблицы элементов смарт-процесса.
Т.к. сам по себе класс не обращается к базе данных напрямую, он полностью полагается на состояние внутреннего EntityObject
.
Это значит, что если у этого объекта не заполнены какие-то данные (не были переданы в select
), то и класс не будет иметь к ним доступ.
Именованные методы
Класс имеет набор именованных методов (по аналогии с EntityObject
) для работы с состоянием и значениями полей.
Например, метод getStageId
вернет строковый идентификатор стадии элемента. Даже если поле называется не STAGE_ID, а STATUS_ID, метод всё равно будет работать.
Аналогично будет работать метод setStageId
- он запишет новое значение в поле для стадии, независимо от его кода в таблице элементов.
И так же будет работать метод isChangedStageId
. Остальные кейсы, доступные в EntityObject
, не реализованы.
Работа с именованными методами реализована через магический метод __call
. Некоторые именованные методы реализованы в явном виде.
Вот полный набор методов, доступных для работы с полем "Заголовок" (TITLE):
getTitle(): ?string
- вернет текущее значение;setTItle(string $title): Item
- запишет новое значение;isChangedTitle(): bool
- вернет true, если значение поля было изменено.
Аналогичным образом можно обращаться к методам других полей. Код поля должен быть преобразован в UpperCamelCase нотацию.
Примеры:
getCreatedTime(): ?DateTime
getUpdatedTime(): ?DateTime
getMovedTime(): ?DateTime
getCreatedBy(): ?int
getUpdatedBy(): ?int
getMovedBy(): ?int
getAssignedById(): ?int
getOpened(): bool
getBegindate(): ?DateTime
getClosedate(): ?DateTime
getCompanyId(): ?int
getContactId(): ?int
getStageId(): ?string
getCategoryId(): ?int
getOpportunity(): ?float
getIsManualOpportunity(): bool
getTaxValue(): float
getCurrencyId(): ?string
getOpportunityAccount(): ?float
getTaxValueAccount(): ?float
getAccountCurrencyId(): ?string
getMycompanyId(): ?int
getProductRows(): ?ProductRowCollection
getClosed(): bool
getSourceId(): ?string
getSourceDescription(): ?string
getWebformId(): ?int
Аналогичным образом можно работать с полями UTM-меток. Хотя значения этих полей не хранятся непосредственно в таблице элементов, к ним есть доступ аналогичным способом из этого класса.
getUtmSource(): ?string
getUtmMedium(): ?string
getUtmCampaign(): ?string
getUtmContent(): ?string
getUtmTerm(): ?string
Общие методы
Метод | Описание | С версии |
---|---|---|
public function __construct(int $entityTypeId, EntityObject $entityObject, array $fieldsMap = [], array $disabledFieldNames = [])
|
Использовать конструктор напрямую настоятельно не рекомендуется. Для получения объектов необходимо использовать соответствующие методы фабрики. | |
public function hasField(string $fieldName): bool |
Вернет true, если элемент имеет поле с названием $fieldName.
Здесь и далее $fieldName может быть как "общим", так и специфическим для сущности.
Например, hasField('STAGE_ID') и hasField('STATUS_ID') для элемента сущности "Лид" оба вернут true.
| |
public function getDefaultValue(string $fieldName) |
Вернет значение по умолчанию для поля $fieldName. | |
public function get(string $fieldName) |
Вернет текущее значение поля $fieldName. | |
public function set(string $fieldName, $value): self |
Запишет новое текущее значение $value у поля $fieldName. | |
public function reset(string $fieldName): self |
Сбросит значение поля $fieldName в его исходное состояние. | |
public function unset(string $fieldName): self |
Запишет пустое значение в поле $fieldName. | |
public function remindActual(string $fieldName) |
Вернет исходное значение поля $fieldName. | |
public function isChanged(string $fieldName): bool |
Вернет true, если поле $fieldName было изменено (текущее значение отличается от исходного). | |
public function getData(int $valuesType = Values::ALL): array |
Вернет массив значений полей элемента, где ключами являются "общие" коды полей, а значениями - значения этих полей, отобранные по маске $valuesType.
$valuesType может принимать значения, описанные в классе \Bitrix\Main\ORM\Objectify\Values :
\Bitrix\Main\ORM\Objectify\Values::ACTUAL - вернутся только исходные значения полей\Bitrix\Main\ORM\Objectify\Values::CURRENT - вернутся только текущие значения полей\Bitrix\Main\ORM\Objectify\Values::ALL - вернутся все значения полей. | |
public function getCompatibleData (int $valuesType = Values::ALL): array $valuesType принимает те же значения и с тем же смыслом, что и в методе getData
|
Вернет массив значений полей элемента в том же формате, в котором работа с ними производилась в "старом" API.
Здесь коды полей отдаются в кодах, специфических для конкретного типа. Значения полей преобразуются в строки / числа / массивы. | |
public function getCompatibleData (int $valuesType = Values::ALL): array $valuesType принимает те же значения и с тем же смыслом, что и в методе getData
|
Вернет массив значений полей элемента в том же формате, в котором работа с ними производилась в "старом" API.
Здесь коды полей отдаются в кодах, специфических для конкретного типа. Значения полей преобразуются в строки / числа / массивы. | |
public function setFromCompatibleData(array $data): self |
Метод запишет новые значения полей из массива $data. Данные в массиве должны быть в формате "старого" API.
Ключами массива должны являться коды полей, специфических для конкретного типа. Если элемент является новым, и значение какого-то поля не передано, то в элемент будет записано значение этого поля по умолчанию (см. метод getDefaultValue ). | |
public function isNew(): bool |
Вернет true, если элемент ещё не сохранен в базу данных. | |
public function getId(): int |
Вернет идентификатор элемента. | |
public function getEntityTypeId(): int |
Вернет идентификатор типа сущности CRM. | |
public function getTitlePlaceholder(): ?string |
Вернет заголовок по умолчанию, предназначенный для показа в форме создания. | |
public function save(bool $isCheckUserFields = true): Result |
Сохранит текущее состояние элемента в базу данных. Вернет объект Bitrix\Main\Result .
Флаг $isCheckUserFields говорит о том, надо ли проверять корректность заполнения пользовательских полей при сохранении.
Этот метод выполняет только сохранение в базу данных. Внутри него не выполняются никакие дополнительные действия (кроме обработчиков событий, подписанных на таблет).
Все дополнительные действия производятся через операции. | |
public function delete(): Result |
Удаляет элемент из базы данных, возвращает Bitrix\Main\Result .
Флаг $isCheckUserFields говорит о том, надо ли проверять корректность заполнения пользовательских полей при сохранении.
Метод выполняет только непосредственно удаление.
Для учета всех дополнительных действий надо воспользоваться операцией. | |
public function jsonSerialize(): array |
Вернет данные об элементе в подготовленном для фронта или REST виде (см. Service\Converter). | |
public function getEntityFieldNameIfExists (string $commonFieldName): ?string |
Вернет специфический для типа код поля по его "общему" названию. | |
public function isCategoriesSupported(): bool |
Вернет true, если тип элемента поддерживает работу с направлениями. | |
public function isStagesEnabled(): bool |
Вернет true, если для типа элемента должны отображаться стадии в интерфейсе. | |
public function getCategoryIdForPermissions(): int |
Вернет идентификатор направления для учета при проверке прав доступа. Если направления не поддерживаются, возвращает 0. |
Работа с коллекциями
Все методы, изменяющие состояние привязанных коллекций, не производят запись изменений в базу данных. Они только изменяют состояние объекта (аналогично поведению методов get
/ set
).
Чтобы приведенные ниже методы работали корректно, из БД должны быть выбраны соответствующие данные: поля Item::FIELD_NAME_CONTACTS или Item::FIELD_NAME_CONTACT_BINDINGS для работы с контактами, поле Item::FIELD_NAME_OBSERVERS и поле Item::FIELD_NAME_PRODUCTS для товарных позиций.
Сделать это можно, передав в select метода Factory::getItems
необходимые поля.
Если же необходимо получить один элемент, то можно воспользоваться методом Factory::getItem
. Он вернет объект, для которого из БД получены все связанные записи.
Чтобы изменения были сохранены, необходимо вызвать метод save
.
Метод | Описание | С версии |
---|---|---|
public function getPrimaryContact(): ?Contact |
Вернет orm-объект с "основным" контактом, если есть хотя бы один. | |
public function getContacts(): array |
Вернет массив orm-объектов привязанных контактов. | |
public function bindContacts(array $contactBindings): void |
Запишет связь с контактами $contactBindings элемента.
Данные $contactBindings должны быть в формате, который отдает метод \Bitrix\Crm\Binding\EntityBinding::prepareEntityBindings().
| |
public function unbindContacts(array $contactBindings): void |
Удалит связь с контактами $contactBindings элемента.
Данные $contactBindings должны быть в формате, который отдает метод \Bitrix\Crm\Binding\EntityBinding::prepareEntityBindings().
| |
public function getContactBindings(): array |
Вернет данные о текущих привязках контактов.
Результат будет в формате, который отдает метод \Bitrix\Crm\Binding\EntityBinding::prepareEntityBindings().
| |
public function getObservers(): array |
Вернет массив идентификаторов пользователей, которые являются наблюдателями у элемента. | |
public function setObservers($observerIds): Item |
Запишет идентификаторы пользователей $observerIds в данные о наблюдателях.
Все необходимые действия по сохранению атрибутов доступа и созданию чатов будут произведены при выполнении операции в классе Bitrix\Crm\Field\Observers . | |
public function addToProductRows(ProductRow $product): Result |
Метод добавит orm-объект товарной позиции $product к текущей коллекции товарных позиций. | |
public function removeFromProductRows(ProductRow $product): void |
Метод удалит orm-объект товарной позиции $product из текущей коллекции товарных позиций. | |
public function setProductRowsFromArrays(array $productArrays): Result |
Метод перезапишет данные о коллекции товаров из массива $productArrays.
Каждый элемент массива $productArrays - это массив данных о товарной позиции. | |
public function setProductRows($products): Result |
Метод перезапишет данные о коллекции товаров из параметра $products. $products может быть либо массивом orm-объектов ProductRow , либо коллекцией ProductRowCollection . |
Пользовательские поля. Файловые поля
Для класса нет особенной разницы, с какими полями он работает - полями самой таблицы или пользовательскими. Главное, чтобы эти поля были доступны в EntityObject
.
Есть некоторые особенности при обработке полей типа "Файл".
Особенности работы с файловыми полями
С точки зрения базы данных тип поля "Файл" - это число. Но в старом API файл перед сохранением можно было передать в виде массива с описанием загруженного файла (см. \CFile::MakeFileArray()
)
Чтобы этот способ работал, при передаче таких данных в метод setFromCompatibleData
файл сразу сохраняется в таблицу b_file, а его ID записывается в поле EntityObject
.
Загрузка производится через сервис загрузки файлов, который очистит файлы, которые не были сохранены.
Примеры
Создание элемента
use Bitrix\Crm\Service; use Bitrix\Crm\Item; $factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Quote); $newFile = [ 'name' => 'document.docx', 'size' => 145961, 'type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'description' => '', 'tmp_name' => '/tmp/d1gadg', ]; $fields = [ 'UF_CRM_FIELD' => 'some value', 'UF_CRM_FILE' => $newFile, ]; $item = $factory->createItem([ Item::FIELD_NAME_STAGE_ID => 'D128_3:CLIENT' , ]); $item->setFromCompatibleData($fields); // here we can get new file identifier. But if item not saved, this file will be deleted. $newFileId = $item->get('UF_CRM_FILE'); $result = $item->save();
Изменение элемента
use Bitrix\Crm\Service; use Bitrix\Crm\Item; $factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Quote); $item = $factory->getItem(1); if ($item) { $item->setStageId('SENT'); $result = $item->save(); }
Удаление элемента
use Bitrix\Crm\Service; use Bitrix\Crm\Item; $factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Quote); $item = $factory->getItem(1); if ($item) { $result = $item->delete(); }
Изменение контактов
Представим, что у нас есть коммерческое предложение с id = 1, привязанное к двум контактам с id = 2 и id = 3. Мы хотим, чтобы оно было привязано к контактам 3 и 4.
use Bitrix\Crm\Binding\EntityBinding; use Bitrix\Crm\Item; use Bitrix\Crm\Service; $factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Quote); $item = $factory->getItem(1); if ($item) { // add new contact with id = 4 $item->bindContacts(EntityBinding::prepareEntityBindings(\CCrmOwnerType::Contact, [4])); // remove contact with id = 2 $item->unbindContacts(EntityBinding::prepareEntityBindings(\CCrmOwnerType::Contact, [2])); }
Преимущества и недостатки
Работа с этим классом имеет ряд преимуществ:
- Легко определить, было ли изменено значение поля (через метод
isChanged
). - Прозрачный доступ к значениям полей, соответственно их смыслу, независимо от их кода в таблице.
- Типизация и autocomplete.
- Возможность работать с абстракцией.
При работе с этим классом есть ряд недостатков, связанных с особенностями поведения EntityObject
:
- Значения полей приводятся к соответствующему типу. Из-за этого числовые поля с незаполненными значениями отдают 0. И нет никакой возможности определить, там записан 0 или поле не заполнено.
- При работе непосредственно с объектом нет возможности определить, было ли значение поля передано с фронта, или нет. Метод
isChanged
говорит только о том, изменено ли значение.
Эти недостатки известны, возможно, через некоторое время будут добавлены новые методы, позволяющие их нивелировать.