iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 26
1
自我挑戰組

WordPress 客製化從 0 開始系列 第 26

Day 26 WordPress 客製化實務:替 Contact Form 7 加上地址欄位

大家好,我是 Eric。

我們在昨天透過短代碼 (shortcode) 的機制,客製化投影片輪播。今天,我們要再透過勾點 (hook) 機制,來替自己的聯絡表單外掛 Contact Form 7 加上台灣縣市選單的功能。

找尋可用的函式庫

上網搜尋「郵遞區號 javascript」後,我們可以看到有熱心網友提供的 JavaScript 函式庫,如 jQuery TWZipcodeTW City Selector。我們這邊以 TW City Selector 為例進行。

查詢可用的勾點 (hook)

根據 Contact Form 7 的官方文件,我們需要透過 wpcf7_init 這個勾點。以今天的例子,我們要加入 address 這個元素:

新增可用的標記

首先,我們先使用 wpcf7_init 這個勾點,註冊我們想要的標籤名稱。舉例來說 [address] 與必填欄位的 [adress*]

add_action( 'wpcf7_init', 'wpcf7_add_address_tag' );
function wpcf7_add_address_tag() {
	wpcf7_add_form_tag( 
		array( 'address', 'address*' ),
		'wpcf7_add_address_tag_handler',
		array( 'name-attr' => true )
	);
}

引用函式庫

接著,我們透過類似 wp_enqueue_scripts 的勾點 wpcf7_enqueue_scripts 來引用 TW City Selector 的函式庫。

add_action( 'wpcf7_enqueue_scripts', 'wpcf7_address_script' );
function wpcf7_address_script() {
	wp_enqueue_script( 'address-picker', 'https://cdn.jsdelivr.net/npm/tw-city-selector@2.1.1/dist/tw-city-selector.min.js', array( 'jquery' ) );
}

製作顯示於前端的 HTML

我們在第一步註冊標籤的時候,wpcf7_add_form_tag 的第二個參數 'wpcf7_add_address_tag_handler' 指的就是轉譯來顯示前端 HTML 代碼的方法,以下用註解的方式介紹各程式碼的意涵:

function wpcf7_add_address_tag_handler( $tag ) {
    /* 如果沒有定義 name 這個屬性,就不會出現內容。 */
	if ( empty( $tag->name ) ) {
		return '';
    }

    /* 顯示 Contact Form 7 的錯誤訊息 */
	$validation_error = wpcf7_get_validation_error( $tag->name );

    /* 如果有定義 CSS 類型的話,顯示 CSS 類型 */
    $class = wpcf7_form_controls_class( $tag->type );
    
    /* 加入 wpcf7-validates-as-text 這個 CSS 類型 */
    $class .= ' wpcf7-validates-as-text';
	
    /* 如果回傳錯誤,新增 wpcf7-validates-as-text 這個 CSS 類型 */
	if ( $validation_error ) {
		$class .= ' wpcf7-not-valid';
    }
	
    /* 將相關類型以 $atts 這個變數儲存,方便 */
	$atts = array();
	
	$atts['class'] = $tag->get_class_option( $class );
	$atts['id'] = $tag->get_id_option();
	if ( $tag->has_option( 'readonly' ) ) {
		$atts['readonly'] = 'readonly';
	}

	if ( $tag->is_required() ) {
		$atts['aria-required'] = 'true';
    }
	
	$value = (string) reset( $tag->values );
	
	$atts['aria-invalid'] = $validation_error ? 'true' : 'false';
	
	if ( $tag->has_option( 'placeholder' ) || $tag->has_option( 'watermark' ) ) {
		$atts['placeholder'] = $value;
		$value = '';
    }
	
	$value = $tag -> get_default_option( $value );
	
	$value = wpcf7_get_hangover( $tag->name, $value );
	
	$atts['value'] = $value;
    if ( wpcf7_support_html5() ) {
		$atts['type'] = $tag->basetype;
	} else {
		$atts['type'] = 'text';
	}

	$atts['name'] = $tag->name;
    
    /* 將 $atts 從陣列的型別,轉換為格式化後的字串 */
	$atts = wpcf7_format_atts( $atts );
	
	/* 開始拼裝 HTML 元素 */
    /* TW City Selector 的基本標籤 */
	$html = '<div role="tw-city-selector" data-has-zipcode></div>';
    /* 讓使用者填寫詳細地址 */
	$html .= '<input id="street" type="text" class="street" placeholder="OO 路 O 段 O 號 O 樓" />';
    /* 這才是實際要回傳的欄位,等等用 jQuery 的方式來組合完整地址 */
	$html .= sprintf( '<span class="wpcf7-form-control-wrap %1$s"><input id="address-string" %2$s hidden/>%3$s</span>',
					sanitize_html_class( $tag->name ),
					$atts,
					$validation_error
					);
	return $html;
	
}

插入啟用函式庫的 JavaScript 程式碼

因為我們實際上要傳遞的資料是來自 #address-string 這個隱藏的欄位,因此我們利用 jQuery,當偵測到使用者填寫詳細地址 (#street) 的時候,自動將郵遞區號 (.zipcode)、縣市 (.county)、鄉鎮市區 (.district) 與詳細地址 (#street) 連接起來,拼成完整的地址。

add_action( 'wp_footer', 'wpcf7_address_inline_script' );
function wpcf7_address_inline_script() { ?>
	<script id="tw-selector">
		new TwCitySelector();
		(function($){
			$("document").ready( function() {
				$("#street").change(function() {
					$("#address-string").val($( ".zipcode" ).val() + $(".county").val() + $(".district").val() + $(".street").val());
				});
			})
		})(jQuery);
	</script>
<?php }

驗證是否為必填欄位

利用 wpcf7_validate_{tag name} 的勾點,來驗證是否為必填欄位,如果必填欄位沒有值,則會顯示錯誤訊息。

add_filter( 'wpcf7_validate_address', 'wpcf7_address_validation_filter', 10, 2 );
add_filter( 'wpcf7_validate_address*', 'wpcf7_address_validation_filter', 10, 2 );

function wpcf7_address_validation_filter( $result, $tag ) {
	$name = $tag->name;

	$value = isset( $_POST[$name] )
		? trim( wp_unslash( strtr( (string) $_POST[$name], "\n", " " ) ) )
		: '';
	
	if ( $tag->is_required() and '' === $value ) {
		$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
	}
	
	return $result;
}

結語

今天沒有多餘的介紹,是直接透過程式碼說明我們如何利用勾點 (hook) 的方式,直接擴充 Contact Form 7 的功能。是的,WordPress 透過勾點的機制,不僅能夠讓我們在巨人的肩膀上看得更遠,我們也可以在自己開發的外掛中,讓自己也成為另一個小巨人的肩膀。
除了可以透過勾點的方式來進行客製化外,我們也可以藉由 WordPress 內部的作法,自行定義 AJAX 所需使用的端點,來製作無限捲動的功能。明天的主題,我們將透過實作無限捲動的方式,說明自訂 AJAX 端點的方法。
(有關 AJAX 的介紹,可以參考第 18 天的文章。)

延伸閱讀

Adding a custom form-tag
Using values from a form-tag
Custom validation


上一篇
Day 25 WordPress 的短代碼 (shortcode) 機制與客製化
下一篇
Day 27 WordPress 客製化:用 AJAX 製作無限捲動
系列文
WordPress 客製化從 0 開始30

尚未有邦友留言

立即登入留言