Для самых нетерпеливых, кому лень читать статью полностью, публикую в самом начале код добавления профиля покупателя в CMS-1С-Битрикс
<?
//Подключаем модуль «Интернет-магазин»
\Bitrix\Main\Loader::includeModule('sale');
//Массив полей профиля покупателя
$profileFields = [
'NAME' => 'Тестовый профиль',//Название профиля
'USER_ID' => (int) $USER->GetID(),//ID пользователя, в данном случае это ID текущего пользователя
'PERSON_TYPE_ID' => $personTypeId,//ID типа плательщика (физическое лицо, юридическое лицо)
];
$profileAddResult = \Bitrix\Sale\Internals\UserPropsTable::Add($profileFields);//Создаем профиль покупателя
//Если профиль создался, то заполняем значения свойств заказа в этом профиле
if($profileAddResult->isSuccess()) {
$prop = [
'USER_PROPS_ID' => $profileAddResult->getId(),//ID профиля (созданного выше)
'ORDER_PROPS_ID' => $saleOrderPropertyId,//ID свойства заказа
'NAME' => 'Имя',//Название свойства заказа
'VALUE' => 'Иван',//Подставляем нужное значение свойства заказа
];
//Добавляем значение свойства заказа в профиль покупателя
$propertyAddResult = \Bitrix\Sale\Internals\UserPropsValueTable::Add($prop);
//Проверка, добавилось ли значение свойства в профиль
if($propertyAddResult->isSuccess()) {
}
}
?>
Теперь опишу подробно все шаги, для более внимательного читателя, как создать функционал для добавления профиля покупателя в личном кабинете пользователя.
По умолчанию в битриксе профиль покупателя создается в момент оформления заказа, с помощью компонента bitrix:sale.order.ajax, но некоторые, шибко умные заказчики сайтов, просят создать функционал в личном кабинете пользователя, по добавлению профилей покупателя. И данный функционал (на момент написания этой статьи) в коробочном варианте Битрикс отсутствует. Поэтому опишу все шаги подробно по созданию данного функционала.
- Получение списка типов плательщика
- Получение списка свойств заказа
- Вывод формы для создания профиля покупателя на странице
- Скрипт для создания профиля покупателя
Получение списка типов плательщика
<?
//Подключаем модуль «Интернет-магазин»
\Bitrix\Main\Loader::includeModule('sale');
//Создадим массив с нужными данными, для вывода формы добавления профиля покупателя
$profiles = [];
$rs = \Bitrix\Sale\Internals\PersonTypeTable::getList(
[
'order' => [
'SORT' => 'ASC',
'ID' => 'ASC',
],
'filter' => [
'ACTIVE' => 'Y',//Только активные типы плательщиков
'LID' => SITE_ID,//ID сайта (в данном случает ID текущего сайта)
],
'select' => [
'ID',
'NAME',
],
]
);
while ($personType = $rs->fetch()) {
$profiles[$personType['ID']] = [
'ID' => $personType['ID'],//ID типа плательщика
'NAME' => $personType['NAME'],//Название типа плательщика
'PROPERTIES' => [],//Сюда ниже мы добавим список всех свойств заказа для данного типа плательщика
];
}
?>
Получение списка свойств заказа
<?
$rs = \Bitrix\Sale\Internals\OrderPropsTable::getList(
[
'order' => [
'SORT' => 'ASC',
'ID' => 'ASC',
],
'filter' => [
'ACTIVE' => 'Y',//Только активные свойства
'USER_PROPS' => 'Y',//Только свойства входящие в профиль пользователя
'UTIL' => 'N',//Свойство не является служебным (служебные свойства заказа могут добавляться например модулями служб доставок, они заполняются автоматически в момент создания заказа и не нужно давать пользователю тут что то заполнять самостоятельно)
],
'select' => [
'ID',
'PERSON_TYPE_ID',//ID типа плательщика
'CODE',//Символьный код
'NAME',//Название
'DEFAULT_VALUE',//Значение по умолчанию
'TYPE',//Тип поля (например NUMBER, STRING, LOCATION)
'REQUIRED',//Признак обязательности для заполнения
'MULTIPLE',//Признак множественности
],
]
);
while ($orderProperty = $rs->fetch()) {
if(isset($profiles[$orderProperty['PERSON_TYPE_ID']])) {
$profiles[$orderProperty['PERSON_TYPE_ID']]['PROPERTIES'][$orderProperty['ID']] = $orderProperty;
}
}
?>
Вывод формы для создания профиля покупателя на странице
В данном примере рассмотрим случай когда типы свойств заказа ограничиваются списком (NUMBER, STRING, LOCATION), для остальных типов вы сможете сделать сами по аналогии.
Выше мы набрали массив $profiles в котором есть информация о типах плательщика и свойствах заказа для каждого типа. Теперь создаем форму.
Предположим что форма будет находиться по адресу /personal/profiles/, а скрипт по созданию самого профиля по адресу /personal/profiles/profile-add.php.
Код формы (/personal/profiles/):
<?
<div class="profile-tabs">
<?
$j = 0;
foreach($profiles as $profile) {
$j++;
?>
<input id="PERSON_TYPE_<?=$profile['ID']?>" type="radio" name="PERSON_TYPE" value="<?=$profile['ID']?>" <?=$j === 1 ? 'checked' : ''?>>
<label for="PERSON_TYPE_<?=$profile['ID']?>"><?=$profile['NAME']?></label>
<form method="POST" action="/personal/profiles/profile-add.php" enctype="multipart/form-data">
<input type="hidden" name="PERSON_TYPE_ID" value="<?=$profile['ID']?>">
<label>
<p>Название профиля:</p>
<input autocomplete="off" type="text" name="PROFILE_NAME" value="" placeholder="Название профиля" required>
</label>
<?
foreach($profile['PROPERTIES'] as $orderProperty) {
$inputName = 'ORDER_PROP_' . $orderProperty['ID'];
if (
$orderProperty['TYPE'] == 'STRING'
|| $orderProperty['TYPE'] == 'NUMBER'
) {//Для типов строка и число
//Если свойство множественное
if($orderProperty['MULTIPLE'] == 'Y') {
?>
<span><?=$orderProperty['NAME']?>:</span>
<?
//Тут задаете нужное количество значений множественного свойства, в данном примере их выведется 5
for($k = 0; $k < 5; $k++) {
?>
<div>
<input type="<?=strtolower($orderProperty['TYPE'])?>" name="<?=$inputName?>[]" value="<?=isset($orderProperty['DEFAULT_VALUE'][$k]) ? $orderProperty['DEFAULT_VALUE'][$k] : ''?>" placeholder="<?=$orderProperty['NAME']?>" <?=$orderProperty['REQUIRED'] == 'Y' && $k === 0 ? 'required' : ''/*если поле обязательно для заполнения, то делаем обязательным только первый инпут, потому что могут ввести всего одно значение*/?>>
</div>
<?
}
} else {//Если свойство НЕ множественное
?>
<label>
<span><?=$orderProperty['NAME']?>:</span>
<input type="<?=strtolower($orderProperty['TYPE'])?>" name="<?=$inputName?>" value="<?=$orderProperty['DEFAULT_VALUE']?>" placeholder="<?=$orderProperty['NAME']?>" <?=$orderProperty['REQUIRED'] == 'Y' ? 'required' : ''?>>
</label>
<?
}
} elseif ($orderProperty['TYPE'] == 'LOCATION') {
//Для типа свойства «Местоположение» выводим стандартный код Битрикс для полей этого типа
\CSaleLocation::proxySaleAjaxLocationsComponent(
[
'AJAX_CALL' => 'N',
'CITY_OUT_LOCATION' => 'Y',
'COUNTRY_INPUT_NAME' => $inputName.'_COUNTRY',
'CITY_INPUT_NAME' => $inputName,
'LOCATION_VALUE' => $orderProperty['DEFAULT_VALUE'],
],
[],
'popup',//Название шаблона компонента bitrix:sale.ajax.locations
true,
'location-block-wrapper'//CSS-класс для контейнера этого поля
);
}
}
?>
<button type="submit">Создать профиль</button>
</form>
<?
}
?>
</div>
?>
CSS-стили для этой формы, а вдруг пригодятся (потому что табы сделаны на чистом CSS)
<style>
.profile-tabs {
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: wrap;
}
.profile-tabs input[type="radio"] {
display: none;
}
.profile-tabs input[type="radio"] + label {
line-height: 1;
background-color: #f8f8f8;
border-radius: 10px;
white-space: nowrap;
padding: 15px;
display: block;
width: calc(50% - 15px);
cursor: pointer;
order: 0;
}
.profile-tabs input[type="radio"] + label:hover, .profile-tabs input[type="radio"]:checked + label {
color: #fff;
background-color: #e54b4b;
}
.profile-tabs input[type="radio"] + label + form {
display: none;
order: 10;
width: 100%;
flex: 0 0 100%;
}
.profile-tabs input[type="radio"]:checked + label + form {
display: block;
}
.profile-tabs label {
display: block;
}
.profile-tabs label span {
display: block;
margin: 20px 0 5px 0;
}
.profile-tabs label input {
border: 1px solid #000;
line-height: 1;
padding: 5px 10px;
}
</style>
Скрипт для создания профиля покупателя
<?
//На приемной стороне /personal/profiles/profile-add.php
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
header('Content-Type: application/json');//Это только для случая когда вы делаете запрос через AJAX и используете тип JSON
//Подключаем модуль «Интернет-магазин»
\Bitrix\Main\Loader::includeModule('sale');
//Снова получаем список типов плательщика и список свойств заказа, что бы проверить правильность заполненных данных в форме
$profiles = [];//Массив с данными свойств заказа
//Массив с результатом создания профиля
$result = [
'success' => false,
'errors' => [],
];
//Проверяем передано ли название профиля
if(
!isset($_POST['PROFILE_NAME'])//Не передано «Название профиля»
|| !is_string($_POST['PROFILE_NAME'])//«Название профиля» не строка
|| !$_POST['PROFILE_NAME']//Пустое «Название профиля»
) {
$result['errors'][] = [
'INPUT_NAME' => 'PROFILE_NAME',
'TEXT' => 'Не заполнено обязательное поле «Название профиля»',
];
}
//Получаем список ID типов плательщиков, сортировка нам тут не нужна
$rs = \Bitrix\Sale\Internals\PersonTypeTable::getList(
[
'filter' => [
'ACTIVE' => 'Y',//Только активные типы плательщиков
'LID' => SITE_ID,//ID сайта (в данном случает ID текущего сайта)
],
'select' => [
'ID',
],
]
);
while ($personType = $rs->fetch()) {
$profiles[$personType['ID']] = [
'PROPERTIES' => [],//Сюда ниже мы добавим список свойств заказа для данного типа плательщика
];
}
//Проверяем, существует ли в базе тип плательщика с ID $_POST['PERSON_TYPE_ID']
if(
!isset($_POST['PERSON_TYPE_ID'])//Не передан ID типа плательщика
|| !isset($profiles[$_POST['PERSON_TYPE_ID']])//Переданный ID типа плательщика не существует
) {
$result['errors'][] = [
'INPUT_NAME' => 'PERSON_TYPE_ID',
'TEXT' => 'Не заполнено обязательное поле «Тип плательщика»',
];
} else {
$profileAddPropsValue = [];//Массив с данными свойств заказа для сохранения в профиль покупателя
//Получаем список всех свойств заказа входящих в профиль покупателя и не являющихся системными
$rs = \Bitrix\Sale\Internals\OrderPropsTable::getList(
[
'filter' => [
'ACTIVE' => 'Y',//Только активные свойства
'USER_PROPS' => 'Y',//Только свойства входящие в профиль пользователя
'UTIL' => 'N',//Свойство не является служебным (служебные свойства заказа могут добавляться например модулями служб доставок, они заполняются автоматически в момент создания заказа и не нужно давать пользователю тут что то заполнять самостоятельно)
'PERSON_TYPE_ID' => $_POST['PERSON_TYPE_ID'],//Выбираем свойство только для выбранного типа плательщика
],
'select' => [
'ID',
'NAME',//Название
'REQUIRED',//Признак обязательности для заполнения
'MULTIPLE',//Признак множественности
],
]
);
while ($orderProperty = $rs->fetch()) {
//Набираем массив значений свойств заказа, заполненных в форме
$inputName = 'ORDER_PROP_' . $orderProperty['ID'];
$inputValue = '';
if(isset($_POST[$inputName])) {//Если это свойство передано в POST-запросе
if($orderProperty['MULTIPLE'] == 'Y') {//Если свойство множественное
$inputValue = [];
if(is_array($_POST[$inputName])) {
foreach($_POST[$inputName] as $value) {
if(
is_string($value)
&& $value
) {
$inputValue[] = $value;
}
}
}
} else {
if(
is_string($_POST[$inputName])
&& $_POST[$inputName]
) {
$inputValue = $_POST[$inputName];
}
}
}
$profileAddPropsValue[] = [
'ORDER_PROPS_ID' => $orderProperty['ID'],
'NAME' => $orderProperty['NAME'],
'VALUE' => $inputValue,
];
//Проверка заполнения свойств заказа в форме
if(
$orderProperty['REQUIRED'] == 'Y'//Если свойство обязательно к заполнению
&& !$inputValue//Свойство не заполнено
) {
$result['errors'][] = [
'INPUT_NAME' => $inputName,
'TEXT' => 'Не заполнено обязательное поле «' . $orderProperty['NAME'] . '»',
];
}
}
}
if(empty($result['errors'])) {//Если нет ошибок в заполнении формы, то создаем профиль
$profileFields = [
'NAME' => htmlspecialchars($_POST['PROFILE_NAME']),
'USER_ID' => (int) $USER->GetID(),
'PERSON_TYPE_ID' => $_POST['PERSON_TYPE_ID'],
];
$profileAddResult = \Bitrix\Sale\Internals\UserPropsTable::Add($profileFields);
if($profileAddResult->isSuccess()) {//Профиль создался
$result['success'] = true;
$profileId = $profileAddResult->getId();//ID созданного профиля покупателя
//Заполняем значения свойств профиля
foreach($profileAddPropsValue as $value) {
$value['USER_PROPS_ID'] = $profileId;
//Сохраняем значение свойства в профиле
$propertyAddResult = \Bitrix\Sale\Internals\UserPropsValueTable::Add($value);
}
} else {
$result['errors'][] = [
'TEXT' => 'Не удалось создать профиль',
];
}
}
//Если вы делаете запрос через AJAX и используете тип JSON, то выводите массив с результатом который обрабатываете результат, в этом массиве присутствует информация о неверно заполненных полях, которые можно будет подсветить
echo json_encode($result);
require_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php');
?>
Для удаления профиля используйте метод \CSaleOrderUserProps::Delete($profileId);
ОЧЕНЬ ВАЖНО!!! Не используйте для удаления профайла метод \Bitrix\Sale\Internals\UserPropsTable::Delete($profileId); он удалит только профиль, но свойства заказа этого профиля останутся в базе, поэтому используйте \CSaleOrderUserProps::Delete($profileId);