Бизнес-процессы
PHP-код в бизнес-процессах
Рабочая справка по PHP-коду в блоках бизнес-процессов коробочного Bitrix24: переменные, поля документа, отчёт, try-catch, функции и условия.
Главная особенность: код в действии бизнес-процесса пишется без открывающего и закрывающего
PHP-тега. Внутри блока уже есть контекст бизнес-процесса, поэтому переменные, константы,
поля документа и отчёт обычно получают через $this.
Оформление
В PHP-блоке лучше писать короткий и безопасный код: без прямых подстановок, с обработкой ошибок и записью важных шагов в отчёт.
Шаблон с try-catch
Если в PHP-блоке возникает ошибка, бизнес-процесс может прекратить выполнение. При
ручном запуске ошибку иногда видно сразу на экране, а при запуске от события её часто
приходится искать в F12 → Network.
Поэтому рабочий вариант — оборачивать код в try-catch и писать ошибку в
отчёт бизнес-процесса.
/**
* Записывает обычное сообщение в отчёт бизнес-процесса.
*/
function writeBpReportMessage($context, string $message): void
{
$context->WriteToTrackingService(
$message,
0,
\CBPTrackingType::Report
);
}
/**
* Записывает ошибку в отчёт бизнес-процесса.
*/
function writeBpErrorMessage($context, \Throwable $exception): void
{
$message = sprintf(
'Ошибка: %s. Файл: %s. Строка: %s',
$exception->getMessage(),
$exception->getFile(),
$exception->getLine()
);
$context->WriteToTrackingService(
$message,
0,
\CBPTrackingType::FaultActivity
);
}
try {
writeBpReportMessage($this, 'PHP-блок начал выполнение');
// Здесь основной код блока
writeBpReportMessage($this, 'PHP-блок завершил выполнение');
} catch (\Throwable $exception) {
writeBpErrorMessage($this, $exception);
// Если процесс должен остановиться, можно пробросить ошибку дальше.
// throw $exception;
}
Важно: try-catch не обязан останавливать бизнес-процесс. Он позволяет
записать ошибку в отчёт. После записи ошибки нужно решить, что делать дальше:
прервать выполнение через throw, выйти из блока через return
или дать процессу продолжиться.
Если код большой или повторяется в нескольких местах, функции лучше вынести в
/local/php_interface/init.php или в свой модуль. В PHP-блоке оставить только
вызов.
Почему не вставлять значения напрямую
Не стоит вставлять значения стандартным способом прямо в PHP-код:
{=Document:TITLE}, {{Название}} и похожими подстановками.
Битрикс подставляет значение до выполнения PHP-кода, и можно получить синтаксическую
ошибку.
Например, если поле пустое, код может превратиться в такой вариант:
$title = ; Если значение содержит кавычки, ошибка тоже возможна:
$title = 'Компания 'Рога и копыта''; Безопаснее получать значения через методы бизнес-процесса уже внутри PHP-кода.
Данные процесса
Основные данные бизнес-процесса читаются через $this или через корневую
активность $this->GetRootActivity().
Переменные и константы процесса
Переменные процесса читаются через GetVariable(), записываются через
SetVariable(). Константы процесса можно получить через
GetConstant().
/**
* Получает имя из переменной бизнес-процесса.
*/
function fetchProcessName($context): string
{
return (string) $context->GetVariable('name');
}
/**
* Сохраняет имя в переменную бизнес-процесса.
*/
function saveProcessName($context, string $name): void
{
$context->SetVariable('name', $name);
}
/**
* Получает значение константы бизнес-процесса.
*/
function fetchProcessConstant($context, string $constant_id)
{
return $context->GetConstant($constant_id);
}
try {
$name = fetchProcessName($this);
$name_prefix = (string) fetchProcessConstant($this, 'name_prefix');
saveProcessName($this, $name_prefix . $name);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
}
Вариант через корневую активность тоже рабочий. Его удобно использовать, если код
копируется из примеров, где принято сначала получать $root_activity.
$root_activity = $this->GetRootActivity();
$name = (string) $root_activity->GetVariable('name');
$root_activity->SetVariable('name', $name); Поля документа
Поля документа лучше читать через сервис документа. Так не нужно вставлять
{=Document:...} прямо в PHP-код.
/**
* Получает поля документа бизнес-процесса.
*/
function fetchDocumentFields($context): array
{
$document_service = $context->workflow->GetService('DocumentService');
$document_fields = $document_service->getDocument($context->getDocumentId());
return is_array($document_fields) ? $document_fields : [];
}
/**
* Получает значение поля документа.
*/
function fetchDocumentFieldValue(array $document_fields, string $field_id)
{
return $document_fields[$field_id] ?? null;
}
try {
$document_fields = fetchDocumentFields($this);
$title = (string) fetchDocumentFieldValue($document_fields, 'TITLE');
$this->SetVariable('document_title', $title);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Название поля зависит от документа, для которого запущен бизнес-процесс. Для CRM это могут быть поля сделки, лида, компании, контакта или смарт-процесса.
Изменение документа
Через $this удобно читать данные процесса и документа. Сам документ лучше
изменять через API конкретной сущности: CCrmDeal, CCrmLead,
ORM смарт-процесса или отдельные действия бизнес-процесса.
Пример ниже — упрощённая заготовка для CRM-документов, где ID сущности находится в
конце document id. Для других типов документов сначала выведите
$this->getDocumentId() в отчёт и проверьте фактический формат.
\Bitrix\Main\Loader::includeModule('crm');
/**
* Получает ID CRM-документа из бизнес-процесса.
*/
function fetchCrmDocumentEntityId($context): int
{
$document_ids = $context->getDocumentId();
$document_code = is_array($document_ids) ? (string) end($document_ids) : (string) $document_ids;
preg_match('/(\d+)$/', $document_code, $matches);
return isset($matches[1]) ? (int) $matches[1] : 0;
}
/**
* Изменяет название сделки.
*/
function updateDealTitle(int $deal_id, string $title): bool
{
if ($deal_id <= 0) {
return false;
}
$deal = new \CCrmDeal();
return (bool) $deal->Update(
$deal_id,
[
'TITLE' => $title,
],
true,
true,
[
'DISABLE_USER_FIELD_CHECK' => true,
]
);
}
try {
$deal_id = fetchCrmDocumentEntityId($this);
$title = (string) $this->GetVariable('new_title');
$is_updated = updateDealTitle($deal_id, $title);
$this->WriteToTrackingService(
$is_updated ? 'Сделка обновлена' : 'Сделка не обновлена',
0,
\CBPTrackingType::Report
);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Для лида, контакта, компании или смарт-процесса нужен другой API. Этот пример только показывает подход: получить ID документа и изменить сущность через её PHP-методы.
Глобальные значения
Глобальные переменные и константы бизнес-процессов читаются не через
GetVariable(), а через классы модуля bizproc.
Глобальные константы
Для глобальных констант используется
\Bitrix\Bizproc\Workflow\Type\GlobalConst. Через getById()
можно получить описание свойства, через getValue() — значение.
\Bitrix\Main\Loader::includeModule('bizproc');
/**
* Получает свойство глобальной константы.
*/
function fetchGlobalConstantProperty(string $constant_id): array
{
$constant_property = \Bitrix\Bizproc\Workflow\Type\GlobalConst::getById($constant_id);
return is_array($constant_property) ? $constant_property : [];
}
/**
* Получает значение глобальной константы.
*/
function fetchGlobalConstantValue(string $constant_id)
{
return \Bitrix\Bizproc\Workflow\Type\GlobalConst::getValue($constant_id);
}
try {
$constant_id = 'Const123456789';
$constant_property = fetchGlobalConstantProperty($constant_id);
$constant_value = fetchGlobalConstantValue($constant_id);
$this->WriteToTrackingService(
print_r($constant_value, true),
0,
\CBPTrackingType::Report
);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Глобальные переменные
Для глобальных переменных используется
\Bitrix\Bizproc\Workflow\Type\GlobalVar. Подход похож на глобальные
константы.
\Bitrix\Main\Loader::includeModule('bizproc');
/**
* Получает свойство глобальной переменной.
*/
function fetchGlobalVariableProperty(string $variable_id): array
{
$variable_property = \Bitrix\Bizproc\Workflow\Type\GlobalVar::getById($variable_id);
return is_array($variable_property) ? $variable_property : [];
}
/**
* Получает значение глобальной переменной.
*/
function fetchGlobalVariableValue(string $variable_id)
{
return \Bitrix\Bizproc\Workflow\Type\GlobalVar::getValue($variable_id);
}
try {
$variable_id = 'Var123456789';
$variable_property = fetchGlobalVariableProperty($variable_id);
$variable_value = fetchGlobalVariableValue($variable_id);
$this->SetVariable('global_variable_value', $variable_value);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Изменить глобальную переменную
Чтобы изменить глобальную переменную, обычно получают её свойство через
getById(), меняют значение Default и сохраняют через
upsert().
\Bitrix\Main\Loader::includeModule('bizproc');
/**
* Изменяет значение глобальной переменной.
*/
function updateGlobalVariableDefault(string $variable_id, $value): bool
{
$global_variable = \Bitrix\Bizproc\Workflow\Type\GlobalVar::getById($variable_id);
if (!is_array($global_variable)) {
return false;
}
$global_variable['Default'] = $value;
\Bitrix\Bizproc\Workflow\Type\GlobalVar::upsert($variable_id, $global_variable);
return true;
}
try {
$variable_id = 'Var123456789';
$is_updated = updateGlobalVariableDefault($variable_id, 'Y');
$this->WriteToTrackingService(
$is_updated ? 'Глобальная переменная обновлена' : 'Глобальная переменная не найдена',
0,
\CBPTrackingType::Report
);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Отчёт и отладка
Отчёт бизнес-процесса — самый удобный способ понять, что произошло внутри PHP-блока.
Запись в отчёт
Для записи в отчёт используется WriteToTrackingService(). Первый параметр
должен быть строкой.
$this->WriteToTrackingService('Some message!');
$this->WriteToTrackingService(
'Some message!',
0,
\CBPTrackingType::Report
); | Тип | Когда использовать |
|---|---|
\CBPTrackingType::Report | Обычные сообщения: старт, результат, найденные ID, промежуточные значения. |
\CBPTrackingType::Error | Ошибки, которые не обязательно ломают весь процесс. |
\CBPTrackingType::FaultActivity | Критическая ошибка в действии. |
\CBPTrackingType::Custom | Сообщение произвольного типа. |
Массивы и ошибки
Если нужно вывести массив, его нужно преобразовать в строку. Для быстрой отладки
удобен print_r($array, true). Для более компактной записи можно использовать
json_encode().
/**
* Записывает массив в отчёт через print_r.
*/
function writeArrayToReport($context, array $items): void
{
$context->WriteToTrackingService(
print_r($items, true),
0,
\CBPTrackingType::Report
);
}
/**
* Записывает массив в отчёт через JSON.
*/
function writeJsonToReport($context, array $items): void
{
$context->WriteToTrackingService(
json_encode($items, JSON_UNESCAPED_UNICODE),
0,
\CBPTrackingType::Report
);
}
try {
$items = [
'deal_id' => 123,
'status' => 'success',
];
writeArrayToReport($this, $items);
writeJsonToReport($this, $items);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} Функции и условия
В PHP-блоках можно использовать свои функции, но важно помнить про область видимости и контекст бизнес-процесса.
Функции в PHP-блоке
Функции можно объявить внутри PHP-блока, но они не должны переопределять уже
существующие функции. Если такая же функция уже есть в init.php или была
объявлена в другом PHP-блоке этого же процесса, повторное объявление может привести к
ошибке.
В циклах функции лучше не объявлять. Сначала объявить функцию, потом использовать её внутри цикла.
/**
* Считает сумму двух чисел.
*/
function calculateBpSum(int $first_number, int $second_number): int
{
return $first_number + $second_number;
}
try {
$numbers = [10, 20, 30];
$sum = 0;
foreach ($numbers as $number) {
$sum = calculateBpSum($sum, (int) $number);
}
$this->SetVariable('sum', $sum);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
}
Если функция нужна в нескольких процессах, лучше вынести её в init.php или
в модуль, а в PHP-блоке только вызывать.
Передача контекста
При вызове своей функции контекст бизнес-процесса сам по себе не передаётся. Если
внутри функции нужен доступ к переменным, константам или отчёту, передайте
$this отдельным параметром.
/**
* Считает сумму и сохраняет её в переменную процесса.
*/
function calculateAndSaveBpSum(int $first_number, int $second_number, $context): int
{
$sum = $first_number + $second_number;
$context->SetVariable('sum', $sum);
return $sum;
}
try {
$sum = calculateAndSaveBpSum(10, 20, $this);
$this->WriteToTrackingService(
'Сумма: ' . $sum,
0,
\CBPTrackingType::Report
);
} catch (\Throwable $exception) {
$this->WriteToTrackingService(
$exception->getMessage(),
0,
\CBPTrackingType::FaultActivity
);
} PHP-код в условии
PHP-код в условии отличается от обычного PHP-блока. На практике надёжнее использовать
$ownerActivity. Привычный контекст $this может быть недоступен
или работать не так, как в обычном действии PHP-кода.
Пример условия: выполнить ветку, если значение переменной id больше или
равно 10.
$ownerActivity->GetVariable('id') >= 10
Если нужно условие по полю документа, удобный рабочий вариант — сначала в обычном
PHP-блоке или блоке изменения переменных сохранить нужное значение в переменную
процесса, а уже в условии читать эту переменную через $ownerActivity.
(int) $ownerActivity->GetVariable('pay_system_id') === 14