6
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MVCモデル風WordPressカスタマイズ設計

Last updated at Posted at 2019-04-24

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呼び出しのみ。

plugin.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のみ

init.php(コード例)
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変数をまとめておく。必要なければ、このファイルはなくても良い。

setting.php(コード例)
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にフックさせる関数などもこのファイルに記述。

hooks/post-type.php(コード例)

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などカスタムタクソノミーの設定用関数をまとめる。

hooks/category.php(コード例)

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

カスタムブロック用関数をまとめる。

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を継承。

controllers/utility.php(コード例)

require_once(dirname(__FILE__) . '/../setting.php');

if (!class_exists('UtilityPostType1')) :
class UtilityPostType1 extends SettingPostType1 {

	// 以下略
}
endif;

controllers/single.php

テンプレートファイルsingle.php用のcontroller、関数。utilityを継承。

controllers/single.php(コード例)

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で定義しておく。

single-post_type_1.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。

6
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?