WordPressを大規模カスタマイズして新規サイトを構築する際、わかりやすい設計を目指すために、MVCの概念を元に設計したものをまとめてみました。
Gutenbergのカスタムブロックに対応しています。
特にWordPressカスタマイズ用pluginの項が肝で、他はおまけのメモです。
assetsディレクトリ
ルートに設置する(のが好き)。
/assets/内には以下を設置する。
- css/
- fonts/
- images/
- js/
- scss/
後々の変更も考慮して、オリジナルフォント用svgはfonts/svg/に置いておく。
scssファイル設計
scssファイルは/assets/scss/に設置し、/assets/css/にcssファイルを書き出す。
ディレクトリ構造は吉本式BEM設計に従う。
SCSSのファイル設計について Vol.1 (吉本式BEM設計)
http://tsudoi.org/guide/detail/8.html
自作プラグイン
WordPressのカスタマイズについては以下の2つのプラグインにまとめる。
カスタムブロック用plugin
Create Guten Block Toolkitでサクッと開発環境を構築。
src内は、1ブロック=1ディレクトリで作成する。
カスタムブロック開発について、詳細はまた別でまとめようと思います。
Create Guten BlockでGutenbergのカスタムブロック開発環境を構築する
https://www.webopixel.net/wordpress/1420.html
Create Guten Block Toolkit
https://ahmadawais.com/create-guten-block-toolkit/
【追記】
カスタムブロック開発について別記事にまとめました。
【Gutenberg】カスタムフィールド職人からカスタムブロック職人へ
themeカスタマイズ用plugin
themeカスタマイズ用pluginには、WordPressデフォルトのfunctions.phpをスパゲッティにしないためのhook用ファイルと、各テンプレートファイルのHTMLを見やすくするためのcontrollerファイルを設置する。
デフォルトのfunctions.phpの位置付け
header, footer用のフック等、全体に関わるもののみを記述する。
小規模なカスタマイズであれば、functions.phpだけで十分だと思います。
ディレクトリ構造
ファイルは、カスタム投稿タイプごとにディレクトリにまとめる。
1ファイルごとにカプセル化する。
plugin.php
post_type_1/
├ init.php
├ setting.php
├ hooks/
├ post-type.php
├ category.php
├ blocks.php
├ controllers/
├ utility.php
├ single.php
├ archive.php
├ taxonomy.php
page/
├ init.php
├ setting.php
├ hooks/
├ controllers/
admin/
├ init.php
├ hooks/
├ css/
├ js/
hooks/とcontrollers/ 内は必要に応じてファイルを作成。
必要があれば、上記の構成図にないファイルも作成して良い。
例) the_contentにフックさせる関数用のhooks/content.php
例) タクソノミーAのテンプレート用のcontrollers/taxonomy-a.php
ファイルの説明
簡単な例を用いて、各ファイルの説明を書いておきます。
plugin.php
メインファイル。各カスタム投稿タイプ用init.php呼び出しのみ。
<?php
/**
* Plugin Name: My Theme Customize
* Plugin URI:
* Description: themeカスタマイズ用プラグイン
* Version: 1.0.0
* Author: hoge
* Author URI: https://hoge.jp/
*/
defined( 'ABSPATH' ) || die();
/**
* Post Type 1
*/
require_once plugin_dir_path(__FILE__) . 'post_type_1/init.php';
// 以下略
post_type_1/
あるカスタム投稿タイプ用のディレクトリ。名称は任意。
init.php
add_actionとadd_filterのみ
defined( 'ABSPATH' ) || die();
/**
* Post Type
*/
require_once(dirname(__FILE__) . '/hooks/post-type.php');
$post_type = new PostTypePostType1();
// Create Post type
add_action('init', array($post_type, 'create_post_type'), 10);
// 以下略
/**
* Category
*/
require_once(dirname(__FILE__) . '/hooks/category.php');
$category = new CategoryPostType1();
// Create Post type
add_action('init', array($category, 'create_taxonomy'), 10);
// 以下略
/**
* Blocks
*/
require_once(dirname(__FILE__) . '/hooks/blocks.php');
$blocks = new BlocksPostType1();
// Add Costom Block Category
add_filter('block_categories', array($blocks, 'add_custom_block_category'), 10, 2);
// Allowed Block
add_filter('allowed_block_types', array($blocks, 'custom_allowed_block_types'), 10, 2);
// Register Meta Field
add_action('init', array($blocks, 'register_meta_field'));
// 以下略
setting.php
public変数をまとめておく。必要なければ、このファイルはなくても良い。
if (!class_exists('SettingPostType1')) :
class SettingPostType1 {
public $post_type = array(
'type' => 'post_type_1',
'label' => '投稿タイプ1',
);
public $taxonomy_1 = array(
'cat' => 'cat1',
'label' => 'タクソノミー1',
);
public $block_types = array(
'myplugin/title',
'myplugin/content',
'myplugin/image',
);
public $meta_fields = array(
'meta_title' => array('タイトル', 'string'),
'meta_content' => array('説明文', 'string'),
'meta_image_id' => array('画像ID', 'integer'),
'meta_image_url' => array('画像URL', 'string'),
);
// 以下略
}
endif;
hooks/
init.phpで呼び出されるフック用の関数をまとめておくディレクトリ。
hooks/post-type.php
register_post_typeなど投稿タイプの設定用関数をまとめる。
例えば、この投稿タイプの投稿を保存した際に、save_postにフックさせる関数などもこのファイルに記述。
require_once(dirname(__FILE__) . '/../setting.php');
if (!class_exists('PostTypePostType1')) :
class PostTypePostType1 extends SettingPostType1 {
/**
* Create Post type
*/
public function create_post_type() {
$template = array(
array($this->block_types[0], array()),
array($this->block_types[1], array()),
array($this->block_types[2], array()),
);
register_post_type($this->post_type['type'], array(
'label' => $this->post_type['label'],
'public' => true,
'query_var' => false,
'menu_position' => 5,
'taxonomies' => array($this->taxonomy_1['cat']),
'supports' => array('title', 'editor', 'thumbnail', 'author', 'custom-fields', 'page-attributes'),
'show_in_rest' => true,
'template' => $template,
'rewrite' => array(
'with_front' => false
)
));
add_theme_support('post-thumbnails', array($this->post_type['type']));
}
// 以下略
}
endif;
hooks/category.php
register_taxonomyなどカスタムタクソノミーの設定用関数をまとめる。
require_once(dirname(__FILE__) . '/../setting.php');
if (!class_exists('CategoryPostType1')) :
class CategoryPostType1 extends SettingPostType1 {
/**
* Create Taxonomy
*/
public function create_taxonomy() {
register_taxonomy(
$this->taxonomy_1['cat'],
array($this->post_type['type']),
array(
'label' => $this->taxonomy_1['label'],
'show_in_rest' => true,
'hierarchical' => true,
'show_admin_column' => true,
'rewrite' => array(
'with_front' => false
)
)
);
}
// 以下略
}
endif;
hooks/blocks.php
カスタムブロック用関数をまとめる。
require_once(dirname(__FILE__) . '/../setting.php');
if (!class_exists('BlocksPostType1')) :
class BlocksPostType1 extends SettingPostType1 {
/**
* Add Costom Block Category
*/
public function add_custom_block_category($categories, $post) {
$categories = array_merge(
array(
array(
'slug' => 'block-' . $this->post_type['type'],
'title' => $this->post_type['label'] . '用ブロック',
'icon' => 'media-document',
),
),
$categories
);
return $categories;
}
/**
* Allowed Block
*/
public function custom_allowed_block_types($allowed_block_types, $post) {
if ($post->post_type === $this->post_type['type']) {
$allowed_block_types = array_merge($this->block_types, $allowed_block_types);
}
return $allowed_block_types;
}
/**
* Register Meta Field
*/
public function register_meta_field() {
foreach ($this->meta_fields as $key => $lavel) {
register_meta('post', $key, array(
'show_in_rest' => true,
'single' => true,
'type' => $lavel[1]
));
}
}
}
endif;
controllers/
各テンプレートファイル用のcontrollerをまとめておくディレクトリ。
controllers/utility.php
テンプレートファイル用の共通関数用ファイル。settingを継承。
require_once(dirname(__FILE__) . '/../setting.php');
if (!class_exists('UtilityPostType1')) :
class UtilityPostType1 extends SettingPostType1 {
// 以下略
}
endif;
controllers/single.php
テンプレートファイルsingle.php用のcontroller、関数。utilityを継承。
require_once(dirname(__FILE__) . '/utility.php');
if (!class_exists('SinglePostType1')) :
class SinglePostType1 extends UtilityPostType1 {
private $post_id = null;
private $meta_data = array();
public $detail = array();
public function __construct() {
parent::__construct();
$this->post_id = get_the_ID();
$this->meta_data = get_registered_metadata('post', $this->post_id);
$this->set_detail_data();
}
private function set_detail_data() {
foreach ($this->meta_fields as $key => $lavel) {
if (isset($this->detail[$key]) && $this->detail[$key] !== '') {
continue;
}
if ($lavel[1] === 'string') {
$this->detail[$key] = isset($this->meta_data[$key][0]) ? $this->meta_data[$key][0] : '';
} else if ($lavel[1] === 'integer') {
$this->detail[$key] = isset($this->meta_data[$key][0]) ? $this->meta_data[$key][0] : null;
}
}
$this->set_image_tag();
}
private function set_image_tag() {
$this->detail['image_tag'] = '<img src="' . $this->detail['meta_image_url'] . '" alt="' . $this->detail['meta_title'] . '" />';
}
}
endif;
single-post_type_1.php
theme/内のテンプレートファイルsingle-post_type_1.phpのコード例は以下。
定数'CUSTOM_THEME_PLUGIN_DIR'はfunctions.phpで定義しておく。
$ctlClass = new stdClass();
$plugin_file = CUSTOM_THEME_PLUGIN_DIR . 'post_type_1/controllers/single.php';
if (file_exists($plugin_file)) {
require_once($plugin_file);
$ctlClass = new SinglePostType1();
}
get_header();
the_post();
$detail = isset($ctlClass->detail) ? $ctlClass->detail : array();
?>
<article>
<div class="container">
<h1><?php the_title(); ?></h1>
<section class="content">
<?php the_content(); ?>
</section>
<section class="image">
<h2><?php echo $detail['meta_title']; ?></h2>
<p><?php echo $detail['image_tag']; ?></p>
<p><?php echo $detail['meta_content']; ?></p>
</section>
</div>
</article>
<?php
controllers/archive.php, controllers/taxonomy.phpなど
上記single.phpと同様。
page/
固定ページ用のディレクトリ。
admin/
管理画面のカスタマイズ用フックやcss, js。