WordPress как на ладони

Как сделать обязательным одно из полей: почта или телефон

Contact Form 7

Бывают ситуации, когда нужно обязательно получить от пользователя телефон или почту, но не обязательно и то и другое вместе. Из коробки CF7 не даёт такого функционала, поэтому ниже напишем его сами.

Вариант 1: без проверок на форму

Исходная форма:

<label> Ваша почта
	[email user-email] </label>

<label> Ваш телефон
	[tel user-phone] </label>

<label> Сообщение
	[textarea user-message 5x0] </label>

[submit "Отправить"]

Шорткод формы:

[contact-form-7 id="454" title="Тестовая форма"]

Как видно из разметки, поля user-email и user-phone не обязательные. Проверим их в коде ниже.

Если бы они были обязательные, то форма требовала бы заполнения их обоих, а нам нужно: телефон или почта (оба необязательно).

Проверим нашу форму перед отправкой — заполнено хотя бы одно из двух полей:

add_filter( 'wpcf7_validate', 'my_form_validate', 10, 2 );

/**
 * Проверяет поля формы.
 *
 * @param WPCF7_Validation $result
 * @param WPCF7_FormTag[]  $tags
 *
 * @return WPCF7_Validation
 */
function my_form_validate( $result, $tags ) {
	// Получим данные об отправляемой форме
	$form = WPCF7_Submission::get_instance();

	// Получаем данные полей
	$email = $form->get_posted_data( 'user-email' );
	$phone = $form->get_posted_data( 'user-phone' );

	// Текст ошибки
	$error_msg = 'Заполните телефон или email';

	// Если оба поля не заполонены - выдать ошибку
	if ( empty( $phone ) && empty( $email ) ) {
		$result->invalidate( 'user-email', $error_msg );
		$result->invalidate( 'user-phone', $error_msg );
	}

	return $result;
}

Этот способ подойдет, если у вас:

  • одна форма на сайте.
  • в форме используются уникальные относительно всех форм сайта имена полей.
  • во всех формах нужна такая проверка.

Вариант 2: проверка формы по скрытому полю

Чтобы точно знать, что проверяются поля нужной формы, необходимо эту формы как-то вычислить (пометить). В роли метки можно использовать скрытое поле.

Исходная форма:

<!-- Скрытое поле в роли метки -->
[hidden my-form-check-field "1"]

<label> Ваша почта
	[email user-email] </label>

<label> Ваш телефон
	[tel user-phone] </label>

<label> Сообщение
	[textarea user-message 5x0] </label>

[submit "Отправить"]

Шорткод формы:

[contact-form-7 id="454" title="Тестовая форма"]

Теперь проверим, пришла ли наша метка с нужным значением. Если "Да" - проверяем поля.

add_filter( 'wpcf7_validate', 'my_form_validate', 10, 2 );

/**
 * Проверяет поля формы.
 *
 * @param WPCF7_Validation $result
 * @param WPCF7_FormTag[]  $tags
 *
 * @return WPCF7_Validation
 */
function my_form_validate( $result, $tags ) {
	$form = WPCF7_Submission::get_instance();

	// Получаем данные полей
	$marker = $form->get_posted_data( 'my-form-check-field' );
	$email  = $form->get_posted_data( 'user-email' );
	$phone  = $form->get_posted_data( 'user-phone' );

	// Текст ошибки
	$error_msg = 'Заполните телефон или email';

	// Проверяем наличие метки с нужным значением.
	// Затем, если оба поля не заполонены - выдаем ошибку
	if ( $marker === '1' && empty( $phone ) && empty( $email ) ) {
		$result->invalidate( 'user-email', $error_msg );
		$result->invalidate( 'user-phone', $error_msg );
	}

	return $result;
}

Этот способ хорош, но есть маленький недостаток. Бывает формы не создают с нуля, а дублируют уже созданные, а потом редактируют. Есть шанс, что скрытое поле забудут удалить и описанная нами логика проверок может помешать корректной работе продублированной формы. Впрочем, вероятность такого события очень мала и описанный способ подойдёт в 99% случаев.

Вариант 3: проверка формы по переданному в шорткод CSS классу

Этот способ решает недостаток способа описанного выше.

Исходная форма:

<label> Ваша почта
	[email user-email] </label>

<label> Ваш телефон
	[tel user-phone] </label>

<label> Сообщение
	[textarea user-message 5x0] </label>

[submit "Отправить"]

Шорткод формы:

[contact-form-7 id="454" title="Тестовая форма" html_class="my-form-check-marker"]

Мы в шорткоде использовали его стандартный атрибут html_class со значением my-form-check-marker, то есть передали css класс как метку. Да, этот атрибут предназначен для вывода у формы указанных css классов (их может быть несколько), но для решения нашей задачи подойдёт.

Проблема в том, что при отправке формы мы можем получить на стороне сервера только значения полей этой самой формы. То есть нам не доступны атрибуты шорткода, ведь они используются только при генерации (рендере) самой формы.

Тогда зачем нам атрибут html_class? Благодаря ему при рендере формы мы можем «опознать» нашу форму и на лету добавить скрытое поле.

По сути в этом варианте, мы воспроизводим предыдущий способ, но выносим "метку" из шаблона формы в атрибут шорткода html_class.

add_filter( 'wpcf7_form_hidden_fields', 'my_form_add_hidden_field' );
add_filter( 'wpcf7_validate', 'my_form_validate', 10, 2 );

/**
 * Добавляет скрытое поле в форму.
 *
 * @param array $arr
 *
 * @return array
 */
function my_form_add_hidden_field( $arr ) {
	/**
	 * @var WPCF7_ContactForm $form
	 */
	$form = WPCF7_ContactForm::get_current();

	if ( strpos( $form->shortcode_attr( 'html_class' ), 'my-form-check-marker' ) !== false ) {
		// Добавляем скрытое поле динамически
		// Ключ и значение будут проверяться в my_form_validate()
		$arr['my-form-check-field'] = 1;
	}

	return $arr;
}

/**
 * Проверяет поля формы.
 *
 * @param WPCF7_Validation $result
 * @param WPCF7_FormTag[]  $tags
 *
 * @return WPCF7_Validation
 */
function my_form_validate( $result, $tags ) {
	$form = WPCF7_Submission::get_instance();

	// Получаем данные полей
	$marker = $form->get_posted_data( 'my-form-check-field' );
	$email  = $form->get_posted_data( 'user-email' );
	$phone  = $form->get_posted_data( 'user-phone' );

	// Текст ошибки
	$error_msg = 'Заполните телефон или email';

	// Проверяем наличие метки с нужным значением.
	// Затем, если оба поля не заполонены - выдаем ошибку
	if ( $marker === '1' && empty( $phone ) && empty( $email ) ) {
		$result->invalidate( 'user-email', $error_msg );
		$result->invalidate( 'user-phone', $error_msg );
	}

	return $result;
}

Минус тут один — атрибут html_class, который мы используем не по назначению. Чтобы это поправить давайте создадим свой атрибут!

Вариант 4: проверка формы по переданному в шорткод своему атрибуту

Исходная форма:

<label> Ваша почта
	[email user-email] </label>

<label> Ваш телефон
	[tel user-phone] </label>

<label> Сообщение
	[textarea user-message 5x0] </label>

[submit "Отправить"]

Шорткод формы:

[contact-form-7 id="454" title="Тестовая форма" my_key="my-form-check-attr"]

К коду из прошлого примера теперь нужно дописать поддержку собственного атрибута в шорткоде формы.

add_filter( 'shortcode_atts_wpcf7', 'my_shortcode_atts_wpcf7', 10, 3 );
add_filter( 'wpcf7_form_hidden_fields', 'my_form_add_hidden_field' );
add_filter( 'wpcf7_validate', 'my_form_validate', 10, 2 );

/**
 * Добавляет новые атрибуты шорткода формы в белый список.
 *
 * @param array $out
 * @param array $pairs
 * @param array $atts
 *
 * @return mixed
 */
function my_shortcode_atts_wpcf7( $out, $pairs, $atts ) {
	// Добавляем наш атрибут my_key, что указан в шорткоде, в белый список,
	// чтобы его значение могло прийти в my_form_add_hidden_field()
	if ( isset( $atts['my_key'] ) ) {
		$out['my_key'] = $atts['my_key'];
	}

	return $out;
}

/**
 * Добавляет скрытое поле в форму.
 *
 * @param array $arr
 *
 * @return array
 */
function my_form_add_hidden_field( $arr ) {
	/**
	 * @var WPCF7_ContactForm $form
	 */
	$form = WPCF7_ContactForm::get_current();

	// Проверяем атрибут и его значение, что указали в шорткоде
	if ( $form->shortcode_attr( 'my_key' ) === 'my-form-check-attr' ) {
		// Ключ, который будет проверяться в my_form_validate()
		$arr['my-form-check-field'] = 1;
	}

	return $arr;
}

/**
 * Валидирует поля.
 *
 * @param WPCF7_Validation $result
 * @param WPCF7_FormTag[]  $tags
 *
 * @return WPCF7_Validation
 */
function my_form_validate( $result, $tags ) {
	$form = WPCF7_Submission::get_instance();

	// Получаем данные полей
	$marker = $form->get_posted_data( 'my-form-check-field' );
	$email  = $form->get_posted_data( 'user-email' );
	$phone  = $form->get_posted_data( 'user-phone' );

	// Текст ошибки
	$error_msg = 'Заполните телефон или email';

	// Проверяем наличие метки с нужным значением.
	// Затем, если оба поля не заполонены - выдаем ошибку
	if ( $marker === '1' && empty( $phone ) && empty( $email ) ) {
		$result->invalidate( 'user-email', $error_msg );
		$result->invalidate( 'user-phone', $error_msg );
	}

	return $result;
}

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

Но и тут есть свой минус - помнить какой атрибут и с каким значением надо дописывать в шорткод.

В общем, всегда есть какие минусы и плюсы, выбирайте вариант который больше подходит для вашего случая.

Напоследок стоит упомянуть, что передавать значение в атрибут необязательно и можно упростить шорткод:

[contact-form-7 id="454" title="Тестовая форма" my_key="my-form-check-attr"]

можно вполне сделать так:

[contact-form-7 id="454" title="Тестовая форма" my_key]

или для понятности так:

[contact-form-7 id="454" title="Тестовая форма" phone_or_email]

Теперь, форма остаётся без изменений, а вот код немного изменится:

add_filter( 'shortcode_atts_wpcf7', 'my_shortcode_atts_wpcf7', 10, 3 );
add_filter( 'wpcf7_form_hidden_fields', 'my_form_add_hidden_field' );
add_filter( 'wpcf7_validate', 'my_form_validate', 10, 2 );

/**
 * Добавляет новые атрибуты шорткода формы в белый список.
 *
 * @param array $out
 * @param array $pairs
 * @param array $atts
 *
 * @return array
 */
function my_shortcode_atts_wpcf7( $out, $pairs, $atts ) {
	/**
	 * Добавляем наш атрибут my_key, что указан в шорткоде, в белый список,
	 * чтобы его значение могло прийти в my_form_add_hidden_field().
	 *
	 * Так как атрибут без значения, то вместо пары ключ => значение
	 * будет [индекс] => 'phone_or_email', поэтому проверяем через in_array().
	 */
	if ( in_array( 'phone_or_email', $atts ) ) {
		// Любой ключ и значение в виде строки, которые потом надо проверить в my_form_add_hidden_field()
		$out['my_key'] = 'my-form-check-attr';
	}

	return $out;
}

/**
 * Добавляет скрытое поле в форму.
 *
 * @param array $arr
 *
 * @return array
 */
function my_form_add_hidden_field( $arr ) {
	/**
	 * @var WPCF7_ContactForm $form
	 */
	$form = WPCF7_ContactForm::get_current();

	// Проверяем атрибут и его значение, что указали в шорткоде
	if ( $form->shortcode_attr( 'my_key' ) === 'my-form-check-attr' ) {
		// Ключ, который будет проверяться в my_form_validate()
		$arr['my-form-check-field'] = 1;
	}

	return $arr;
}

/**
 * Валидирует поля.
 *
 * @param WPCF7_Validation $result
 * @param WPCF7_FormTag[]  $tags
 *
 * @return WPCF7_Validation
 */
function my_form_validate( $result, $tags ) {
	$form = WPCF7_Submission::get_instance();

	// Получаем данные полей
	$marker = $form->get_posted_data( 'my-form-check-field' );
	$email  = $form->get_posted_data( 'user-email' );
	$phone  = $form->get_posted_data( 'user-phone' );

	// Текст ошибки
	$error_msg = 'Заполните телефон или email';

	// Проверяем наличие метки с нужным значением.
	// Затем, если оба поля не заполонены - выдаем ошибку
	if ( $marker === '1' && empty( $phone ) && empty( $email ) ) {
		$result->invalidate( 'user-email', $error_msg );
		$result->invalidate( 'user-phone', $error_msg );
	}

	return $result;
}

-

Какой из способов вам больше всего понравился? А может вы используете уже готовые решения в виде плагина? Поделитесь своим мнением в комментариях!

2 комментария
    Войти