Документация для разработчиков
Темная тема

Начало работы

Введение


Исторически так сложилось, что манипуляция данными об элементах основных сущностей 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 = [])

  • $entityTypeId - идентификатор типа сущности CRM;
  • $entityObject - orm-объект с данными;
  • $fieldsMap - карта сопоставления полей, где ключ - "общий" код поля (см. публичные константы класса FIELD_NAME_), а значение - код поля из таблицы элементов;
  • $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 говорит только о том, изменено ли значение.

Эти недостатки известны, возможно, через некоторое время будут добавлены новые методы, позволяющие их нивелировать.

© «Битрикс», 2001-2023, «1С-Битрикс», 2023
Наверх