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

Чтение данных из сущностей разных типов

Довольно типовой случай, когда надо прочитать данные о сущностях разных типов. Задача распространённая, но в каждый раз есть какие-то свои особенности. На стороне CRM нет такого "волшебного" способа, который учтет все варианты. Для каждого случая надо писать свою обвязку.

Получить заголовки разделов сущностей (Сделки, Лиды и т.д.)

$descriptions = \CCrmOwnerType::GetAllDescriptions();

// вернет "Контакт"
$contactDescription = $descriptions[\CCrmOwnerType::Contact];
// то же самое
//$contactDescription = \CCrmOwnerType::GetDescription(\CCrmOwnerType::Contact);

$categoryDescriptions = \CCrmOwnerType::GetAllCategoryCaptions();
// вернет "Контакты"
$contactCategoryDescription = $categoryDescriptions[\CCrmOwnerType::Contact];
// то же самое
//$contactCategoryDescription = \CCrmOwnerType::GetCategoryCaption(\CCrmOwnerType::Contact);

Получить ссылку на список / элемент

Необходимо воспользоваться Service\Router.

Проверить права доступа

Необходимо воспользоваться Service\UserPermissions.

Получить заголовок элемента произвольного типа

Метод сформирует заголовок элемента из нескольких полей, в зависимости от типа сущности.

Не используйте этот метод внутри цикла.

$dealTitle = \CCrmOwnerType::GetCaption(\CCrmOwnerType::Deal, 11);

// title is not escaped!

Получить базовые данные об элементе произвольного типа

$data = [];

$isCheckAccess = true;

$isLoaded = \CCrmOwnerType::TryGetEntityInfo(\CCrmOwnerType::Order, 22, $data, $isCheckAccess);
if ($isLoaded)
{
    print_r($data);
}

Получить базовые данные о списке элементов одного типа

// ключ - идентификатор элемента
$data = [
    11 => [],
    13 => [],
    222 => [],
];

$isCheckAccess = true;

\CCrmOwnerType::PrepareEntityInfoBatch(\CCrmOwnerType::Lead, $data, $isCheckAccess);
print_r($data);

Узнать ответственного за элемент

$isCheckAccess = true;

// загружает актуального ответственного
$responsibleId = \CCrmOwnerType::loadResponsibleId(\CCrmOwnerType::Lead, 11, $isCheckAccess);

// GetResponsibleID - то же самое, но данные кешируются

Чтение произвольных данных

  1. Необходимо понимать, что разные типы сущностей имеют разный набор полей. По умолчанию метод Service\Factory::getItems() загружает все поля, кроме данных о связанных коллекциях контактов и товаров.
  2. Если нужно ограничить/расширить выборку, то select для запроса необходимо формировать индивидуально, исходя из типа сущности.
  3. Новое API с версии crm 21.400.0 поддерживает полноценную работу только со смарт-процессами и предложениями.
  4. Поддержка лидов, сделок, контактов и компаний появится не раньше crm 21.800.0.

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

use Bitrix\Main\Loader;
use Bitrix\Crm\Service;
use Bitrix\Crm\Item;

Loader::includeModule('crm');

$elements = [
	[
		'ENTITY_TYPE_ID' => 1,
		'ENTITY_ID' => 1,
	],
	[
		'ENTITY_TYPE_ID' => 2,
		'ENTITY_ID' => 3,
	],
	[
		'ENTITY_TYPE_ID' => 2,
		'ENTITY_ID' => 5,
	],
	[
		'ENTITY_TYPE_ID' => 3,
		'ENTITY_ID' => 10,
	],
	[
		'ENTITY_TYPE_ID' => 4,
		'ENTITY_ID' => 33,
	],
	[
		'ENTITY_TYPE_ID' => 130,
		'ENTITY_ID' => 10,
	]
];

// исходные даннные, сгруппированные по типу сущности
$typed = [];
foreach ($elements as $element)
{
	$typed[$element['ENTITY_TYPE_ID']][$element['ENTITY_ID']] = $element['ENTITY_ID'];
}
// ид всех контактов
$contactIds = $typed[\CCrmOwnerType::Contact] ?? [];
// ид всех компаний
$companyIds = $typed[\CCrmOwnerType::Company] ?? [];
// данные о всех контактах
$contacts = [];
// данные о всех компаниях
$companies = [];
// связи
$relations = [];
// все сущности с мульти-полями
$multiFields = [];

$result = [];
foreach ($typed as $entityTypeId => $entityIds)
{
	// first load all types but contacts and companies
	if (
		$entityTypeId === \CCrmOwnerType::Contact
		|| $entityTypeId === \CCrmOwnerType::Company
	)
	{
		continue;
	}
	$factory = Service\Container::getInstance()->getFactory($entityTypeId);
	if (!$factory)
	{
		continue;
	}
	// если нужно с проверкой прав доступа
	// $items = $factory->getItemsFilteredByPermissions([
	$items = $factory->getItems([
		'select' => ['*', Item::FIELD_NAME_CONTACT_BINDINGS],
		'filter' => [
			'@ID' => $entityIds,
		],
	]);
	
	foreach ($items as $item)
	{
		$identifier = \Bitrix\Crm\ItemIdentifier::createByItem($item);
		$compatibleData = $item->getCompatibleData();
		$result[$item->getEntityTypeId()][$item->getId()] = $compatibleData;
		foreach ($compatibleData['CONTACT_BINDINGS'] as $contactBinding)
		{
		    // дозапишем идентификаторы связанных контактов 
			$contactIds[] = $contactBinding['CONTACT_ID'];
			$relations[\CCrmOwnerType::Contact][$contactBinding['CONTACT_ID']][] = $identifier;
		}
		if ($item->hasField(Item::FIELD_NAME_COMPANY_ID))
		{
		    // дозапишем ид связанной компании
			$companyId = (int)$item->getCompanyId();
			if ($companyId > 0)
			{
				$companyIds[] = $companyId;
				$relations[\CCrmOwnerType::Company][$companyId][] = $identifier;
			}
		}
		if ($item->getEntityTypeId() === \CCrmOwnerType::Lead)
		{
			$multiFields[] = [
				'NAME' => \CCrmOwnerType::LeadName,
				'ID' => $item->getId(),
			];
		}
	}
}

\Bitrix\Main\Type\Collection::normalizeArrayValuesByInt($contactIds);
\Bitrix\Main\Type\Collection::normalizeArrayValuesByInt($companyIds);

// загрузим все контакты
if (!empty($contactIds))
{
	$factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Contact);
	if (!$factory)
	{
		// no factory sorry
		throw new Exception('factory for ' . \CCrmOwnerType::Contact . ' not found');
	}
	// если нужно с проверкой прав доступа
	// $items = $factory->getItemsFilteredByPermissions([
	$items = $factory->getItems([
		'filter' => [
			'@ID' => $contactIds,
		],
	]);
	foreach ($items as $item)
	{
		$compatibleData = $item->getCompatibleData();
		$contacts[$item->getId()] = $compatibleData;
		if (isset($typed[$item->getEntityTypeId()][$item->getId()]))
		{
			$result[$item->getEntityTypeId()][$item->getId()] = $compatibleData;
		}
		$multiFields[] = [
			'NAME' => \CCrmOwnerType::ContactName,
			'ID' => $item->getId(),
		];
	}
}

// загрузим все компании
if (!empty($companyIds))
{
	$factory = Service\Container::getInstance()->getFactory(\CCrmOwnerType::Company);
	if (!$factory)
	{
		// no factory sorry
		throw new Exception('factory for ' . \CCrmOwnerType::Company . ' not found');
	}
	// если нужно с проверкой прав доступа
	// $items = $factory->getItemsFilteredByPermissions([
	$items = $factory->getItems([
		'filter' => [
			'@ID' => $companyIds,
		],
	]);
	foreach ($items as $item)
	{
		$compatibleData = $item->getCompatibleData();
		$companies[$item->getId()] = $compatibleData;
		if (isset($typed[$item->getEntityTypeId()][$item->getId()]))
		{
			$result[$item->getEntityTypeId()][$item->getId()] = $compatibleData;
		}
		$multiFields[] = [
			'NAME' => \CCrmOwnerType::CompanyName,
			'ID' => $item->getId(),
		];
	}
}

// загрузим все контактные данные и примешаем их к результату с учетом связей
$filter = \Bitrix\Crm\FieldMultiTable::prepareFilter($multiFields);
$multiFields = \Bitrix\Crm\FieldMultiTable::getList([
	'filter' => $filter,
]);
while ($fieldItem = $multiFields->fetch())
{
	$entityTypeId = \CCrmOwnerType::ResolveID($fieldItem['ENTITY_ID']);
	$itemId = (int)$fieldItem['ELEMENT_ID'];
	if (isset($result[$entityTypeId][$itemId]))
	{
		$result[$entityTypeId][$itemId]['FM'][] = $fieldItem;
	}
	if (isset($relations[$entityTypeId][$itemId]))
	{
		foreach ($relations[$entityTypeId][$itemId] as $identifier)
		{
			$result[$identifier->getEntityTypeId()][$identifier->getEntityId()]['FM'][] = $fieldItem;
		}
	}
}

print_r($result);

Код в этом примере можно улучшить, оставлен в таком виде для наглядости.

Желательно обернуть в какой-то класс на своей стороне.

Пользовательские комментарии

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

Для этого нужно всего лишь авторизоваться на сайте

Но помните, что Пользовательские комментарии, несмотря на модерацию, не являются официальной документацией. Ответственность за их использование несет сам пользователь.

Также Пользовательские комментарии не являются местом для обсуждения функционала. По подобным вопросам обращайтесь на форумы.
© «Битрикс», 2001-2021, «1С-Битрикс», 2021
Наверх