Physical Address

304 North Cardinal St.
Dorchester Center, MA 02124

[WordPress 筆記] Day 7 客製化延伸閱讀功能-使用 Shortcode 短代碼

為什麼要懂短代碼 (shortcode)?

短代碼與勾點可以說是讓 WordPress 實現模組化的核心功臣,短代碼的出現,也使 WordPress 的專案可以將 PHP 程式希望達成的功能,呼叫到頁面編輯器當中。在說明 Code Snippets 的時候,曾經提到過直接在頁面中執行 PHP 程式碼,對於管理會十分不方便,尤其當需要進行重複指令的時候。

因此,利用 Code Snippets 集中管理短代碼的方式,可以實現「在頁面執行的 PHP 程式」的需求。這次使用的例子「延伸閱讀」就是使用了這樣的概念。

事實上,許多知名的頁面編輯器,如 Elementor、Oxygen 等,都有透過短代碼儲存頁面屬性,再加以匯出、再製的功能。而知名的聯絡表單外掛 Contact Form 7,也是用短代碼的方式將聯絡表單產出的。

如何使用短代碼?

短代碼的基本寫法如下:

<?php
add_shortcode( 'mycode', 'my_shortcode' );//在編輯器中輸入 [mycode] 就可以呼叫代碼
function my_shortcode( $atts ){
    $a = shortcode_atts( array(
        'class' => '', //短代碼要使用的參數
    ), $atts );
}

其中 $atts 並非要的。$atts 代表的是短代碼使用的參數,譬如 [mycode class=1] 等。

「文章延伸閱讀」的功能規劃

這次的延伸閱讀,主要是透過搭配 ACF Pro 的方式,實現半外掛化的功能。先看看實際上操作的結果:

這項功能的操作邏輯如下:

  1. 使用者在編輯文章時,有自訂欄位可以選擇相關文章 (ACF Pro – Repeater)
  2. 在文章中插入要使用編輯文章時,用短代碼插入 [相關文章] 的區塊
  3. 插入的 [相關文章] 區塊,需要有自己的樣式

實際操作

新增欄位

首先我們需要先建立一個重複器 (repeater) 欄位,因為一篇文章可能會有不只一篇的相關文章。而重複器中,我們需要再建立一個文章物件 (Post Object) 欄位,回傳格式我們選用文章物件 (Post Object),來符合後續短代碼的程式碼。

接下來,我們將自訂欄位與 [文章] 進行關聯。

建立短代碼

/**
 * Relevant Posts
 */
 /**
 * Use this to register custom fields and the shortcode for relevant posts.
 * Required: ACF Pro or ACF Repeater Extension
 */
if ( !function_exists( 'hyc_relevant_posts' ) ){
    function hyc_relevant_posts( $atts ) {
        $output = '';
        $arr    = [];
        if ( ! function_exists( 'get_field' ) ) :
            return esc_html__( 'This shortcode requires the plugin ACF, please install it.', 'applemint' );
        else :
            $a = shortcode_atts( array(
                'key'  => 0,
            ), $atts );
            if ( have_rows( 'relevant_posts' ) ) {
                while( have_rows( 'relevant_posts') ) {
                    the_row( 'relevant_posts' );
                    $arr[] = get_sub_field( 'relevant_post' );
                }
            }
            $pid = $arr[$a['key']]->ID;
            $output = '<div class="relevant-post custom-loop-list">';
            $output .= '<fieldset><legend>' . esc_html__( 'More to read', 'applemint' ) . '</legend>';
            $output .= '<div class="flex flex-wrap"><div class="f-w-4"><a href="' . get_permalink( $pid ) . '" target="_blank">';
            $output .= get_the_post_thumbnail( $pid, 'blog-loop', array(
                'class' => 'post-thumbnail aligncenter lozad',
                'data-src' => get_the_post_thumbnail_url( $pid, 'blog-loop' ),
                'src' => ''
            ) ) . '</a></div>';
            $output .= '<div class="f-w-8"><a href="'. get_permalink( $pid ) . '" target="_blank"><h3 class="entry-title">'. get_the_title( $pid ) . '</h3></a>';
            $output .= get_post_meta( $pid, '_aioseop_description' )[0] . '</div>';
            $output .= '</div><!-- .flex -->';
            $output .= '</fieldset></div><!-- .relevant-post -->';
            return $output;
        endif; //ACF check
    }
}
add_shortcode( 'hyc_relevant', 'hyc_relevant_posts' );

在這個步驟中,我們建立名為 hyc_relevant 的短代碼,其中會使用到的參數是 key。需要注意的是 PHP 當中 key 會從 0 開始計算,因此我們的預設值先設定為 0(如果今天只使用 [hyc_relevant],那就會自動帶入第一篇文章。

透過 if ( have_rows( ‘relevant_posts’ ) ) 來驗證我們的重複器欄位中是不是有任何內容,如果有的話,我們會在 $arr 這個變數裡儲存每一篇所選文章的物件。

接著我們就可以透過 $output 這個變數,將希望建立的 html 標記。

這邊注意一下 get_post_meta( $pid, ‘_aioseop_description’ )[0] ,這是因為我們公司使用了 All in One SEO Pack 這套外掛,用來擷取 description 的中繼資料用,這裡也可以改為 get_the_excerpt( $pid ); 來擷取文章的內容摘要。

以上面圖片中的例子來說,我們會透過這個短代碼得出以下的 html 標記:

<div class="relevant-post custom-loop-list">
  <fieldset>
    <legend>More to read</legend>
    <div class="flex flex-wrap">
      <div class="f-w-4">
        <a href="https://www.applemint.tech/blog/ja-blog-kol/" target="_blank">
          <img width="270" height="180" src="https://www.applemint.tech/wp-content/uploads/2017/10/influencer1024x540-270x180.jpg" class="post-thumbnail aligncenter lozad wp-post-image" alt="台湾インフルエンサー" data-src="https://www.applemint.tech/wp-content/uploads/2017/10/influencer1024x540-270x180.jpg" srcset="" data-loaded="true">
        </a>
      </div>
      <div class="f-w-8">
        <a href="https://www.applemint.tech/blog/ja-blog-kol/" target="_blank">
          <h3 class="entry-title">
            <span id="2019-KOL">【2019年版】台湾インフルエンサー裏話-台湾のブロガー/KOL の費用</span>
          </h3>
        </a>
        台湾でインフルエンサーやブロガー、KOL のお探しにお困りですか? applemint ではすでにインフルエンサーやブロガーを手配した実績があり、 台湾インフルエンサーのメリットはもちろんデメリット、及び代理店の実情まで知っています。台湾でインフルエンサーをお探しの場合まずはご相談ください
      </div>
    </div><!-- .flex -->
  </fieldset>
</div>

[相關文章] 樣式設定

這邊直接附上 [相關文章] 的 SCSS 設置。

.relevant-post {
		h3 {
			margin-top: 0;
		}
		> fieldset {
			border: 1px solid #00c1d1;
			max-width: 90%;
			margin: 0 auto 1em auto;
		}
		> fieldset > div {
			padding: 8px 0 20px 0;
		}
		> fieldset legend {
			background: #00c1d1;
			border-radius: 5px;
			color: #fff;
			margin-left: 1em;
			padding: 8px 16px;
		}
		> fieldset legend::before {
			content: url('./assets/svg/post.svg');
			margin-right: 0.5rem;
		}
		a::after {
			content: '';
		}
	}
.flex {
  display: flex;
  flex-wrap: wrap;
  .f-w-4 {
    width: 30%;
    flex-basis: auto;
    flex-grow: 0;
    margin-left: 0;
    margin-right: 0;
  .f-w-8 {
    width: 60%;
    flex-basis: auto;
    flex-grow: 0;
    margin-left: 0;
    margin-right: 0;
    }
}
@media screen and (max-width: $screen__break-point-s){
    .flex {
        [class*="f-w"] {
            width: 100%;
        }
        .f-w-4:nth-child(3n+1),
        .f-w-4:nth-child(3n+2),
        .f-w-3:nth-child(4n+1),
        .f-w-3:nth-child(4n+2),
        .f-w-3:nth-child(4n+3),
        .f-w-6:not(.site-info):nth-child(2n+1) {
            margin-right: 0;
        }
    }
}

post.svg 的內容如下:

<svg height="16" viewBox="0 0 512 512" width="16" xmlns="http://www.w3.org/2000/svg" fill="#fff">
    <path d="m458.903 114.538c-11.106-15.146-26.587-32.85-43.589-49.852s-34.706-32.482-49.852-43.589c-25.787-18.91-38.296-21.097-45.462-21.097h-248c-22.056 0-40 17.944-40 40v432c0 22.056 17.944 40 40 40h368c22.056 0 40-17.944 40-40v-312c0-7.166-2.186-19.675-21.097-45.462zm-66.216-27.225c15.35 15.35 27.4 29.199 36.29 40.687h-76.977v-76.973c11.492 8.89 25.339 20.939 40.687 36.286zm55.313 384.687c0 4.336-3.664 8-8 8h-368c-4.336 0-8-3.664-8-8v-432c0-4.336 3.664-8 8-8 0 0 247.978-.001 248 0v112c0 8.836 7.163 16 16 16h112z"/><path d="m368 416h-224c-8.836 0-16-7.163-16-16s7.164-16 16-16h224c8.837 0 16 7.163 16 16s-7.163 16-16 16z"/><path d="m368 352h-224c-8.836 0-16-7.163-16-16s7.164-16 16-16h224c8.837 0 16 7.163 16 16s-7.163 16-16 16z"/><path d="m368 288h-224c-8.836 0-16-7.163-16-16s7.164-16 16-16h224c8.837 0 16 7.163 16 16s-7.163 16-16 16z"/>
</svg>

結語

短代碼適合用的情境,在於他可以高度客製化,同時又可以透過參數,讓使用者保有一定的操作彈性。因此在設計上,需要先設想好每個參數的預設值,以及例外狀況(學程式的時候都需要考慮的議題)。

由於 ACF Pro 從版本 5.8 開始,就開始支援註冊區塊的功能,因此未來應該也可以把這項功能改用 ACF 的自訂區塊包裝起來。

Eric Chuang
Eric Chuang

正職是廣告行銷人員,因為 Google Tag Manager 的關係開始踏入網站製作的領域,進一步把 WordPress 當成 PHP + HTML + CSS + JavaScript 的學習教材。此外,因為工作的關係,曾經用 Automattic 的 Underscores (_s) 替客戶與公司官網進行全客製化佈景主題開發。

1 則留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料