Заготовка. Регистрация Битрикс. main.register


Вызов компонента

<?$APPLICATION->IncludeComponent(
    "kochnev:main.register",
    "flat",
    Array(
        "AUTH" => "Y",
        "REQUIRED_FIELDS" => array("EMAIL","NAME","PERSONAL_PHONE"),
        "SET_TITLE" => "Y",
        "SHOW_FIELDS" => array("EMAIL","NAME","PERSONAL_PHONE"),
        "SUCCESS_PAGE" => "/personal/index.php",
        "USER_PROPERTY" => array(),
        "USER_PROPERTY_NAME" => "",
        "USE_BACKURL" => "Y"
    )
);?>

template.php

<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) {
    die();
}

use Bitrix\Main\Localization\Loc;

global $USER;
if ($USER->IsAuthorized()) {
    LocalRedirect('/personal/');
}

if ($arResult["SHOW_SMS_FIELD"] == true) {
    CJSCore::Init('phone_auth');
}

Loc::loadMessages(__FILE__);
?>

<div class="auth-form register-form">
    <div class="container">
        <div class="auth-form__inner">
            <div class="auth-form__header d-none">
                <h3 class="auth-form__title"><?=Loc::getMessage('AUTH_REGISTER', null, 'Регистрация')?></h3>
            </div>

            <? if (!empty($arResult["ERRORS"])): ?>
                <div class="auth-form__errors">
                    <? foreach ($arResult["ERRORS"] as $key => $error): ?>
                        <?
                        if (intval($key) == 0 && $key !== 0) {
                            $error = str_replace(
                                "#FIELD_NAME#",
                                "&quot;" . GetMessage("REGISTER_FIELD_" . $key) . "&quot;",
                                $error
                            );
                        }
                        ?>
                        <div class="auth-form__error"><?=$error?></div>
                    <? endforeach; ?>
                </div>
            <? elseif ($arResult["USE_EMAIL_CONFIRMATION"] === "Y"): ?>
                <div class="auth-form__errors">
                    <div class="auth-form__error"><?=GetMessage("REGISTER_EMAIL_WILL_BE_SENT")?></div>
                </div>
            <? endif; ?>

            <? if ($arResult["SHOW_SMS_FIELD"] == true): ?>
                <form method="post" action="<?=POST_FORM_ACTION_URI?>" name="regform" class="auth-form__form">
                    <? if ($arResult["BACKURL"] <> ''): ?>
                        <input type="hidden" name="backurl" value="<?=$arResult["BACKURL"]?>"/>
                    <? endif; ?>
                    <input type="hidden" name="SIGNED_DATA" value="<?=htmlspecialcharsbx($arResult["SIGNED_DATA"])?>"/>

                    <div class="auth-form__field form-field">
                        <label class="form-field__label auth-form__label" for="sms_code">
                            <?=GetMessage("main_register_sms")?><span class="starrequired">*</span>
                        </label>
                        <input
                            type="text"
                            id="sms_code"
                            name="SMS_CODE"
                            value="<?=htmlspecialcharsbx($arResult["SMS_CODE"])?>"
                            autocomplete="off"
                            class="form-control auth-form__input"
                            placeholder="Введите код из SMS"
                        />
                    </div>

                    <div class="auth-form__submit">
                        <button type="submit" name="code_submit_button" class="btn auth-form__submit-btn">
                            <?=GetMessage("main_register_sms_send")?>
                        </button>
                    </div>
                </form>

                <script>
                    new BX.PhoneAuth({
                        containerId: 'bx_register_resend',
                        errorContainerId: 'bx_register_error',
                        interval: <?= $arResult["PHONE_CODE_RESEND_INTERVAL"] ?>,
                        data: <?= CUtil::PhpToJSObject(['signedData' => $arResult["SIGNED_DATA"]]) ?>,
                        onError: function (response) {
                            var errorDiv = BX('bx_register_error');
                            var errorNode = BX.findChildByClassName(errorDiv, 'errortext');
                            errorNode.innerHTML = '';
                            for (var i = 0; i < response.errors.length; i++) {
                                errorNode.innerHTML = errorNode.innerHTML + BX.util.htmlspecialchars(response.errors[i].message) + '<br>';
                            }
                            errorDiv.style.display = '';
                        }
                    });
                </script>

                <div id="bx_register_error" style="display:none"><? ShowError("error") ?></div>
                <div id="bx_register_resend"></div>

            <? else: ?>
                <form method="post" action="<?=POST_FORM_ACTION_URI?>" name="regform" enctype="multipart/form-data"
                      class="auth-form__form">
                    <? if ($arResult["BACKURL"] <> ''): ?>
                        <input type="hidden" name="backurl" value="<?=$arResult["BACKURL"]?>"/>
                    <? endif; ?>

                    <? foreach ($arResult["SHOW_FIELDS"] as $FIELD): ?>
                        <div class="auth-form__field form-field">
                            <label for="regFormControlInput_<?=$FIELD?>" class="form-field__label auth-form__label">
                                <?=GetMessage("REGISTER_FIELD_" . $FIELD)?>
                                <? if ($arResult["REQUIRED_FIELDS_FLAGS"][$FIELD] === "Y"): ?>
                                    <span class="starrequired">*</span>
                                <? endif ?>
                            </label>

                            <? switch ($FIELD):
                                case "PASSWORD": ?>
                                    <div class="auth-form__password-wrapper">
                                        <? if ($arResult["SECURE_AUTH"]): ?>
                                            <div class="auth-form__secure-note" id="bx_auth_secure"
                                                 style="display:none">
                                                <div class="ui-note">
                                                    <span class="auth-form__secure-icon">🔒</span>
                                                    <?=GetMessage("AUTH_SECURE_NOTE")?>
                                                </div>
                                            </div>
                                            <script>
                                                document.getElementById('bx_auth_secure').style.display = '';
                                            </script>
                                        <? endif ?>
                                        <input
                                            type="password"
                                            id="regFormControlInput_<?=$FIELD?>"
                                            class="form-control auth-form__input"
                                            name="REGISTER[<?=$FIELD?>]"
                                            value="<?=$arResult["VALUES"][$FIELD]?>"
                                            autocomplete="off"
                                            placeholder="Введите пароль"
                                        />
                                    </div>
                                    <? break;
                                case "CONFIRM_PASSWORD": ?>
                                    <input
                                        type="password"
                                        id="regFormControlInput_<?=$FIELD?>"
                                        class="form-control auth-form__input"
                                        name="REGISTER[<?=$FIELD?>]"
                                        value="<?=$arResult["VALUES"][$FIELD]?>"
                                        autocomplete="off"
                                        placeholder="Повторите пароль"
                                    />
                                    <? break;
                                case "PERSONAL_PHOTO":
                                case "WORK_LOGO": ?>
                                    <input
                                        type="file"
                                        id="regFormControlInput_<?=$FIELD?>"
                                        name="REGISTER_FILES_<?=$FIELD?>"
                                        class="form-control auth-form__input auth-form__input--file"
                                    />
                                    <? break;
                                case "EMAIL": ?>
                                    <input
                                        type="email"
                                        id="regFormControlInput_<?=$FIELD?>"
                                        class="form-control auth-form__input"
                                        name="REGISTER[<?=$FIELD?>]"
                                        value="<?=$arResult["VALUES"][$FIELD]?>"
                                        placeholder="Введите email"
                                    />
                                    <? break;
                                default: ?>
                                    <?
                                    $placeholder = $FIELD === 'PERSONAL_PHONE'
                                        ? '+7 (000) 000-00-00'
                                        : GetMessage("REGISTER_FIELD_" . $FIELD);
                                    $maskPhoneClass = $FIELD === 'PERSONAL_PHONE' ? ' _mask_phone' : '';
                                    ?>
                                    <input
                                        type="text"
                                        id="regFormControlInput_<?=$FIELD?>"
                                        class="form-control auth-form__input<?=$maskPhoneClass?>"
                                        name="REGISTER[<?=$FIELD?>]"
                                        value="<?=$arResult["VALUES"][$FIELD]?>"
                                        placeholder="<?=$placeholder?>"
                                    />
                                <? endswitch; ?>
                        </div>
                    <? endforeach; ?>

                    <? if ($arResult["USE_CAPTCHA"] == "Y"): ?>
                        <input type="hidden" name="captcha_sid" value="<?=$arResult["CAPTCHA_CODE"]?>"/>
                        <div class="auth-form__field form-field">
                            <label class="form-field__label auth-form__label" for="captcha_word">
                                <?=GetMessage("REGISTER_CAPTCHA_PROMT")?><span class="starrequired">*</span>
                            </label>
                            <div class="auth-form__captcha">
                                <img
                                    src="/bitrix/tools/captcha.php?captcha_sid=<?=$arResult["CAPTCHA_CODE"]?>"
                                    width="180"
                                    height="40"
                                    alt="CAPTCHA"
                                    class="auth-form__captcha-image"
                                />
                            </div>
                            <input
                                type="text"
                                id="captcha_word"
                                name="captcha_word"
                                maxlength="50"
                                value=""
                                autocomplete="off"
                                class="form-control auth-form__input"
                                placeholder="Введите символы с картинки"
                            />
                        </div>
                    <? endif; ?>

                    <div class="auth-form__submit">
                        <input type="submit" class="btn auth-form__submit-btn" name="register_submit_button"
                               value="<?=GetMessage("AUTH_REGISTER")?>" />
                    </div>
                </form>

                <? if ($arResult["GROUP_POLICY"]["PASSWORD_REQUIREMENTS"]): ?>
                <div class="auth-form__password-requirements">
                    <?=$arResult["GROUP_POLICY"]["PASSWORD_REQUIREMENTS"]?>
                </div>
            <? endif; ?>

                <div class="auth-form__links">
                    <a href="/personal/auth/" class="btn-link">
                        Уже есть аккаунт? Войти
                    </a>
                </div>

            <? endif; ?>

            <div class="auth-form__required-note">
                <span class="starrequired">*</span><?=GetMessage("AUTH_REQ")?>
            </div>
        </div>
    </div>
</div>

CSS

.register-form .auth-form__title {
  margin-bottom: 30px;
}

.auth-form__inner {
  max-width: 460px;
  padding: clamp(20px, 3vw, 40px);
  border-radius: 8px;
  border: 1px solid var(--gray-40);
}

.auth-form__form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.auth-form__input--file {
  padding: 8px 12px;
  font-size: 14px;
}

.auth-form__input--file::-webkit-file-upload-button {
  background-color: var(--gray-10);
  border: 1px solid var(--gray-30);
  border-radius: 4px;
  padding: 6px 12px;
  margin-right: 12px;
  font-size: 13px;
  color: var(--gray-80);
  cursor: pointer;
  transition: var(--tr25);
}

.auth-form__input--file::-webkit-file-upload-button:hover {
  background-color: var(--gray-20);
  border-color: var(--gray-40);
}

.auth-form__password-requirements {
  margin-top: 20px;
  padding: 12px 16px;
  background-color: var(--gray-10);
  border: 1px solid var(--gray-30);
  border-radius: 6px;
  font-size: 14px;
  color: var(--gray-70);
}

.auth-form__required-note {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--gray-20);
  font-size: 14px;
  color: var(--gray-60);
  font-style: italic;
}

.starrequired {
  color: var(--red);
  margin-left: 2px;
}

.auth-form__errors {
  margin-bottom: 20px;
}

.auth-form__error {
  background-color: rgba(209, 92, 92, 0.1);
  border: 1px solid var(--red);
  border-radius: 6px;
  padding: 12px 16px;
  color: var(--red);
  font-size: 14px;
  margin-bottom: 8px;
}

.auth-form__error:last-child {
  margin-bottom: 0;
}

.ui-checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  line-height: 1.4;
}

.ui-checkbox {
  position: relative;
  width: 18px;
  height: 18px;
  border: 2px solid var(--gray-40);
  border-radius: 3px;
  background-color: var(--white);
  transition: var(--tr25);
  flex-shrink: 0;
  margin-top: 2px;
}

.ui-checkbox-label input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.ui-checkbox-label input[type="checkbox"]:checked + .ui-checkbox {
  background-color: var(--primary);
  border-color: var(--primary);
}

.ui-checkbox-label input[type="checkbox"]:checked + .ui-checkbox::after {
  content: '✓';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: var(--white);
  font-size: 12px;
  font-weight: bold;
}

.ui-checkbox-label input[type="checkbox"]:focus + .ui-checkbox {
  box-shadow: var(--box-shadow-focus);
}

.ui-checkbox-text {
  color: var(--gray-80);
}

.auth-form__submit {
  margin-top: 10px;
}

.auth-form__submit-btn {
  width: 100%;
}

.auth-form__links {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--gray-20);
}

@media (max-width: 768px) {
  .auth-form {
    padding: 40px 0;
  }

  .auth-form__inner {
    margin: 0 20px;
    padding: 30px 20px;
  }

  .auth-form__links {
    gap: 8px;
  }

  .auth-form__title {
    font-size: 22px;
  }
}

@media (max-width: 480px) {
  .register-form .auth-form__inner {
    margin: 0 10px;
    padding: 24px 16px;
  }

  .auth-form__title {
    font-size: 20px;
  }

  .form-control {
    padding: 10px 14px;
    font-size: 16px; /* Важно для iOS, чтобы не зумило */
  }
}

.auth-form .ui-checkbox-label {
  margin-top: 4px;
}

Опционально

Если в проекте требуется работа с номером телефона в других компонентах, они работают с полем PHONE_NUMBER, которое хранится в другой таблице нежели поля Пользователя. Поэтому поле PERSONAL_PHONE нам не подойдет.

Заберем номер телефона в форме регистрации через PERSONAL_PHONE и запишем его в PHONE_NUMBER.

Копируем компонент в свое пространство имен /local/components/my_folder/main.register.

Далее в файле component.php после

// check emptiness of required fields
foreach ($arResult["SHOW_FIELDS"] as $key)
{
    if ($key != "PERSONAL_PHOTO" && $key != "WORK_LOGO")
    {
        $arResult["VALUES"][$key] = $_REQUEST["REGISTER"][$key];
        if (in_array($key, $arResult["REQUIRED_FIELDS"]) && trim($arResult["VALUES"][$key]) == '')
            $arResult["ERRORS"][$key] = GetMessage("REGISTER_FIELD_REQUIRED");
    }
    else
    {
        $_FILES["REGISTER_FILES_".$key]["MODULE_ID"] = "main";
        $arResult["VALUES"][$key] = $_FILES["REGISTER_FILES_".$key];
        if (in_array($key, $arResult["REQUIRED_FIELDS"]) && !is_uploaded_file($_FILES["REGISTER_FILES_".$key]["tmp_name"]))
            $arResult["ERRORS"][$key] = GetMessage("REGISTER_FIELD_REQUIRED");
    }
}

пишем проверку

// Проверяем пользователя по номеру телефона.
// Добавляем номер телефона в PHONE_NUMBER
if(isset($_REQUEST["REGISTER"]["PERSONAL_PHONE"])) {
    $phone = '+' . \Helper\ToolsHelper::formatPhoneForTel($_REQUEST["REGISTER"]["PERSONAL_PHONE"]);
    $userExists = \Helper\UserHelper::getUserByPhone($phone);
    if ($userExists) {
        $arResult["ERRORS"]['PERSONAL_PHONE'] = GetMessage("REGISTER_USER_WITH_PHONE_EXIST");
    } else {
        $arResult["VALUES"]['PHONE_NUMBER'] = $phone;
    }
}

Вспомогательные функции

Все опционально и зависит от масок и их наличия.

/**
 * Форматируем номер телефона с удалением лишних символов для вставки в ссылку.
 * Добавляем добавочный номер по разделителю "_".
 * Из строки (8-38553) 25-3-01_2 делаем:
 * 1) 83855325301,2 - formatPhoneForTel()
 * 2) (8-38553) 25-3-01 доб.2 - formatPhoneForDisplay()
 */
public static function formatPhoneForTel($phone): string
{
    // Убираем пробелы и заменяем символ "_".
    $phone = str_replace('_', ',', $phone);

    // Удаляем символы, которые не являются цифрами, кроме запятой.
    return preg_replace('/[^\d,]/', '', $phone);
}

public static function formatPhoneForDisplay($phone): string
{
    return str_replace('_', ' доб.', $phone);
}
<?php

namespace Helper;

class UserHelper
{
    /**
     * Регистрация пользователя по Эл. Почте.
     */
    public static function registerByEmailEvent(array &$arFields):array
    {
        $arFields["LOGIN"] = $arFields["EMAIL"];
        return $arFields;
    }

    /**
     * Поиск пользователя по номеру телефона
     *
     * @param string $phone Номер телефона
     *
     * @return int|false ID пользователя или false если не найден
     */
    public static function getUserByPhone(string $phone): int | false
    {
        //$phone = $this->normalizePhone($phone);

        // Пробуем найти пользователя.
        $rawData = \Bitrix\Main\UserPhoneAuthTable::getList([
            'filter' => [
                'PHONE_NUMBER' => $phone,
            ],
            'select' => [
                'USER_ID',
            ],
        ])->fetch();

        if ($rawData) {
            return $rawData['USER_ID'];
        }

        return false;
    }

}