Skip to content

Instantly share code, notes, and snippets.

@dazedbear
Last active June 7, 2023 07:11
Show Gist options
  • Save dazedbear/a974738dbcddc3b482aa88e11ab20940 to your computer and use it in GitHub Desktop.
Save dazedbear/a974738dbcddc3b482aa88e11ab20940 to your computer and use it in GitHub Desktop.
WordPress 前端開發

WordPress 前台開發

這篇是針對使用 WordPress.org 作為 Quick Deploy Solution,快速製作 POC (Proof of Concept) 時前台該如何開發的介紹。由於牽涉到各團隊的組成,有的團隊有 RD,有的團隊沒有,因此這套 Solution 必須要能滿足不同使用情境。

基本概念

術語 Terminology 概念 Concept
Theme 佈景主題,也就是網站的外觀
Plugin 外掛,一些網站額外的功能組件
Hooks WP 核心的 functions ,用來串接資料庫的資料
CMS Content Management System,內容管理系統
Monolithic CMS 前後端耦合,theme 提供外觀、plugin 提供功能,透過 WP 的核心串在一起,直連 DB
Headless CMS 前後端分離,前台是另一個獨立的網站(可以是 static site 或 server),WP 提供管理後台和 Rest API,透過 API 溝通資料。
Route 對外開放的端點 URI
Endpoints 處理 response 的 Functions
Controllers 決定這個 Route 該使用哪個 Endpoint 處理
Schema 傳入 Endpoints 的結構化資料

架設 WordPress Server

為了之後方便 Deploy ,建議採用 Docker 架設 WP Server。

Docker

  1. 參考 官方指南 安裝 Docker CE Stable
  2. Docker Store 下載 WordPress Container
  3. 建立專案資料夾,新建 compose.yml
$ mkdir wp-server && cd wp-server
$ touch compose.yml
  1. 撰寫 compose.yml
version: '3.1'
services:
  wordpress:
    image: wordpress
    restart: always
    ports:
      - 5000:80 # 前面是本機開的 port,後面是 container 開的 port
    environment:
      WORDPRESS_DB_PASSWORD: password # MySQL DB 的密碼
    volumes: # 把 WordPress 核心程式碼與本機的某個資料夾(wp)同步
      - ~/Projects/wp-server/wp:/var/www/html
  mysql:
    image: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: password # MySQL DB 的密碼

# 注意:這是最簡易的架設,並無考慮資安議題。
# 如需要架設 Production,請參照 Docker Store 的說明,安全地設定 DB 連線。
  1. 啟動 / 停止 Server
# 啟動
$ docker-compose up

# 停止,請記得不要 docker kill container,否則所有資料會消失
$ docker-compose down

Docker 參考資源

開發情境

# 情境 做法 參考資源
1 使用現成 theme,但沒有 RD 可以改 code 安裝現成的 theme builder 或 page builder 外掛,直接在後台製作網站外觀 - 7 Page Builder Plugins
- Page Builder Framework
2 修改現成 theme(WP 原生方法)
開發全新的 theme(WP 原生方法)
參考官方手冊,直接改 code 吧 - Theme Handbook
- Plugin Handbook
3 開發全新的 theme(開發模板工具) 使用 theme 相關的開發模板工具。需參考部分 WP 官方手冊做進一步客製化。 - Sage
- Timber
4 獨立的前台 前端自由發揮,串 API 就好 - REST API Handbook
- 官方 plugin
- 第三方套件

#1 ~ #3 使用 Hooks 串接資料 #4 使用 API 串接資料

情境 #1 後台製作頁面

Page Builder Plugin

注意!這些版面製作工具的自訂彈性皆有限,若要照設計給的 mockup 製作頁面,必須對 HTML、CSS 有一定的瞭解,花在熟悉工具、測試自訂彈性的時間會滿多的。故適合靜態頁面、版面設計需求不高的情境。若需要互動性、版面較複雜的情境,建議尋求前端資源協助。

| Plugin | 收費 | 說明 | |--|--|--|--|--| | Elementor | 免費版、付費版 | 三個工具裡面操作最直覺、最容易上手。友善的中文介面,拖拉編輯版面,支援 RWD、依裝置設定不同外觀(桌機、平板、手機)。不需要很了解 CSS 即可編輯。 | | Visual Composer | 免費版、付費版 | 英文介面,需自己中文化,拖拉編輯版面,使用相對直覺,支援圖層、local / global css。
Widget 選項彈性較少,必須懂 css 才能客製化版面。 | | SiteOrigin | 免費 | 包含三個工具:
- Page Builder:建立頁面外觀,要搭配 widget,
- Widgets Bundle:小工具,圖片輪播、header...,有限的自訂選項
- CSS:用編輯器 or Live Editor 修改 CSS

使用上一定要懂 css 才能製作版面,彈性相當有限 |

還有其他幾款 Page Builder 請參考這篇文章介紹。 7 Great Page Builder Plugins For WordPress: Design Made Easy

Page Builder Framework

有時候 Page Builder 的自訂性很有限,可以搭配 Page Builder Framework 這個 theme 去擴充彈性。

情境 #2 原生方法開發、修改 Theme

按照 WordPress.org 官方手冊去建構原生的 WP Theme。

理想中 Theme 負責網站外觀、Plugin 負責功能,Theme 串接資料需要透過 WordPress 內建的 functions ,按一定的生命週期取得資料,也就是 hooks。除了內建 hooks ,也能自定義新的 hooks ,一般建議後端在 plugin 中定義可用的 hooks,前端在 theme 的 functions.php 中自訂 template_tag 接收 hooks 的回傳結果,以便於在任何頁面中顯示結果。

hooks

  • theme:使用約定的 hooks,do_actionapply_filters
  • plugin:註冊約定的 hooks,add_acitonadd_filter

do_action 不會 return 任何東西,而 apply_filters 會 return 資料,使用上要注意

theme/01-basic-theme-demo/functions.php

<?php
/* Custom Template Tag */
// 按回吐資料狀況,執行 UI render logic
function get_movie_title(){
    /* Action 版本 */
    // do_action('get_movie_title');
    /* Filtet 版本 */
    $des = apply_filters('get_movie_title', []);
    echo is_null($des) ? 'No description loaded' : "電影名稱:$des";
}

theme/01-basic-theme-demo/index.php

<main class="shadow vertical-center round">
    <h1>Event Site Demo Theme</h1>
    <p class="description round text-ellipsis">
	    <?php 
		    /* hooks */
		    do_action('get_movie_title');
		    apply_filters('get_movie_title', []);
		    
		    /* custom template_tag */
		    get_movie_title();
		?>
    </p>
    <button class="btn round">Login</button>
</main>

plugin/01-hooks-demo.php

<?php
/*
Plugin Name:  WP Hooks Demo Plugin
Plugin URI:   https://developer.wordpress.org/plugins/the-basics/
Description:  Basic WordPress Plugin for Using Custom Hooks
Version:      20160911
Author:       WP QDS
Author URI:   https://developer.wordpress.org/
License:      GPL2
License URI:  https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  wporg
Domain Path:  /languages
*/

// 負責打 API 並回傳部分資料
function fetch_movie_title() {    
    $res = wp_remote_get('https://cloud.culture.tw/frontsite/trans/SearchShowAction.do?method=doFindTypeJ&category=8');
    $body = json_decode(wp_remote_retrieve_body( $res ));
    $des = $body[0]->title;

    // Action return 無法被 do_action 接收到,只能夠 echo
    // echo $des;
    
    // Filter return 可以被 apply_filters 接收到
    return $des;
}

/* Action 版本 */
// add_action('get_movie_title', 'fetch_movie_title');

/* Filter 版本 */
add_filter('get_movie_title', 'fetch_movie_title');

情境 #3 開發模板工具製作 Theme

這個方法是情境 #2 的延伸,需要先理解 Theme、Plugin、Hooks。

由於原生 WP Theme 的資料、頁面 render 邏輯全部混雜在 php 中,對前端開發相當受限。因此,透過 PHP 模板引擎將 Theme 的資料、頁面分離,方便前端使用現代化的 web 開發,又兼顧與 WordPress 的相容性。

不需要再透過前端切版、後端拆版套用的模式協作開發,開發完的模板即可直接給後端使用。

目前有三套可行方案:

  • Sage(使用 Laravel Blade)
  • Timber(使用 Twig)
  • 使用 Handlebars & WP原生開發

至於要不要讓前端負責 Theme 的資料串接(會碰到 php),這部分可以按團隊的狀況做彈性調整。

SageBlade

目前最推薦這個方案,也是星星數最多的工具。建議直上 v9 版,工具已經整合了 Sass、Webpack、Bootstrap 4...等,可以立即開始開發。Blade 寫法比較特別,可以參考官方文件說明。

語法與 EJS 很相似,除了引用其他模板的 include 以外,多了模板繼承 extends 的概念(類似圖層),可以使用 block 蓋掉指定區塊的內容。

此開發工具僅有 twig 模板、php 頁面,並不包含 Sass、Babel、Webpack 等工具,需要自己額外安裝設置。一般建議載完 repo 後,裝上必要的 Sass、babel 等工具,直接在 local 起一個 WP Server �開發,以節省 mock 資料的踩雷過程。

  • 官方安裝設定指南
  • 開發模板工具
    • 整合了 104 前端開發工具 & Timber Starter Theme,方便使用 ES6、SCSS 開發 twig ,npm run build 產生能直接部署到 WP Server 的 Theme。�
    • 這個工具僅能用於 view 開發,背後需要自己 mock 掉� Timber、Twig 相關的內建 function、filter。
    • 資料串接仍需部署到 WP Server ,使用 php 操作。
# 前端 協作約定 後端
1 維護 Theme,製作 Twig 模板 模板所需變數 schema 維護 Theme 資料串接、Plugin
2 維護 Theme,製作 Twig 模板、資料串接 (in php) 約定好 hooks 維護 Plugin

注意! 開發工具使用 twig-loader 處理模板,背後的 twig.js 目前尚未完全實現PHP 端的所有 feature支援度),此外,部分寫法是 Timber 額外擴充,在使用上需要特別小心!必要的時候得自己 mock。

目前測到有問題的部分

名稱 語法範例 說明
resize filter `{{post.thumbnail.src resize(1200, 300)}}`
function() {{function('wp_head')}} Timber 擴充的功能
concat operator {% include ['./tease-' ~ post.post_type ~ '.twig'] %} twig.js 照理說有實現,但這個會出錯,目前原因不明

Handlebars

Handlebars 是另一個 PHP 的模板引擎,目前 WordPress 社群沒有相關的開發模板,因此需要自己安裝、設定,可以搭配上方 Timber 所使用的 104 前端開發工具進行開發。

  • 原生 WP Theme 開發請參考這篇
  • Laravel 、Client Side 共用 Handlebars 模板請參考這篇

其他補充

  • 後端可以使用 Laravel 和 WordPress 做搭配:corcel

情境 #4 獨立前台開發細節

plugins/02-custom-api-demo.php

主要透過 WP REST API 與前台溝通資料,官方有內建的接口,亦可以擴充自訂的接口。因此前端可以自由決定要使用哪個框架。

需特別注意的是,這種情境無法搭配 SEO plugin 使用,兩大常見的 plugin 如 Yoast SEOAll in One SEO Pack 開放的 API 接口很少、甚至沒有,因此 SEO 的部分必須自己處理(server-side render)。

WP REST API

  • WordPress 4.7 以後內建,4.7 以前需加裝官方 Plugin WordPress REST API (Version 2)
  • 支援 jsonp、envelop(把原始的 response 包起來,status code 永遠回傳 200)、X-HTTP-Method-Override(相容性考量,用於未支援特殊請求方法的情境:DELETE、HEAD...)
  • 支援 link(回傳與這個 resource 相關的所有 URI)、embed(將相關的 URI 資料一併包在同一個 response)
  • 採用 cookie 驗證 + nonce URI
  • 若需要其他驗證機制,需安裝對應的 Plugin
  • Client Libraries
  • 支援自訂 route 、 namespace
  • 需要啟用 pretty permalinks,也就是 固定網址 來建立 RESTful Endpoints,否則需改用 WP_Query 的方式存取資源

快速上手

啟用 Pretty Permalink

  • 管理後台 => 設定 => 固定網址,調整成一般以外的網址選項。
  • WordPress 預設已啟用 mod_write,如果沒有請參考文件,按 server 不同 (nginx、apache...) 做設定。

查看網站所有的 API 接口

# No Pretty Permalinks,固定網址選「一般」
http://localhost:5000/?rest_route=/

# Pretty Permalinks,固定網址選「一般」以外的選項
http://localhost:5000/wp-json/

其他作法請參考文件

內建Route

請參閱官方文件。相關的名詞解釋請參閱這篇

自訂 Route

  1. 撰寫 Endpoint (Function),可以使用的 return value
// 取得 WordPress 內部的文章
function get_all_posts( $data ) {
	$posts = get_posts();
	return empty( $posts ) ? null : $posts;
}
  1. 決定 Namespace 並註冊新的 Route,這裡可以 validate 傳入參數 args
  1. register_rest_route() 一定要放在 rest_api_init 的 callback 中,避免在 API 未啟動前就執行
  2. 通常以 vendor/version 來命名 Namespace,例如:myplugin/v1
  3. Route 的 regex 有點不同,格式為 /(?P<[param_name]>[param_value_regex]) ,例如:/(?P<id>\d+) 等同於 express 的 /:id
<?php
add_action( 'rest_api_init', function () {
  $namespace = 'myplugin/v1';
  register_rest_route( $namespace, '/postlist', array(
    'methods' => 'GET',
    'callback' => 'get_all_posts',
  ) );
} );
  1. 試打 Route
# No Pretty Permalinks
http://localhost:5000/?rest_route=/myplugin/v1/postlist

# Pretty Permalinks
http://localhost:5000/wp-json/myplugin/v1/postlist
  1. 自訂限制存取權限的 Route

限定某些 Route 要驗證權限才能存取,可以使用 Permissions Callback ,亦可搭配其他驗證的 plugin 如 JWT Authentication for WP REST API

// 取得機敏資料
function get_private_data() {
	return rest_ensure_response( 'This is private data.' );
}

// 驗證身份
function get_private_data_permissions_check( WP_REST_Request $request ) {
	// 簡單的檢查 token 是否相同
	$token = $request['token'];
	if ( $token === 'thisistesttoken' ) {
		return true;
	}

	// 驗證使用者是否有編輯文章權限
	/* 
	if ( current_user_can( 'edit_posts' ) ) {
		return true;
	} 
	*/
  
	return new WP_Error( 'rest_forbidden', esc_html__( 'You cannot view private data.', 'my-text-domain' ), array( 'status' => 401 ) );
}

// 註冊新的 Route
add_action( 'rest_api_init', function () {
  $namespace = 'myplugin/v1';
  register_rest_route( $namespace, '/private-data', array(
    'methods'  => 'GET',
    'permission_callback' => 'get_private_data_permissions_check',  // 陣列順序沒有影響,這個會比下方的 main callback 早執行
    'callback' => 'get_private_data',
    'args' => array(
      'token' => array(
        'required' => true // 必填,否則回 400
      )
    )
  ) );
} );
  1. Route 比較多與複雜時,建立 controller 便於擴充維護,請參考這篇

更多詳細的用法,請參考文件

推薦安裝清單

名稱 說明
Yoast SEO 經典的 SEO Plugin 之一
All in One SEO Pack 經典的 SEO Plugin 之一
WooCommerce 經典的電子商務 Plugin,快速建置電商平台

開發者

名稱 說明
PHP Settings 修改 php.ini 設定
Query Monitor 檢視 wp 的 query 開發工具
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment