WEBLOG

WordPress > サイト毎の設定

WordPressカスタム投稿タイプをプラグインなしで設定

Create:

Update:

Category:WordPressサイト毎の設定

Series:WordPressサイト毎の設定

[ Version.19 ]

概要

カスタム投稿タイプの設定。プラグインは使用しません。

functions.php には設定値のみ記載して functions_setup.php に記述しinclude。

functions.php

設定定数「CUSTOM_POSTTYPE」については下部に補足とサンプルがあります。

設定定数「TINYMCE_INITS」はクラッシックエディター使用時の共通設定です。

// カスタム投稿タイプ
const CUSTOM_POSTTYPE = [
	'xxx' => [
		'name' => 'サンプルカスタム投稿タイプ名称',
		'posts_per_page' => 10,
		'editor' => [
			'type' => 'classic',
			'tinymce' => ['link', 'unlink'],
		],
		'eyecatch' => false,
		'route' => [
			'archive_path' => 'auto',
			'single_auto_slug' => true,
			'page_in_posttype' => [],
		],
		'sitemap' => [
			'archive' => '0.5', // アーカイブがない場合は「false」
			'single' => '0.6', // シングルがない場合は「false」
			'add_arg' => [],
		],
		'columns' => [
			'author' => false,
			'column' => [
			],
		],
	],
];
const TINYMCE_INITS = [
	'block_formats' => '段落=p;大見出し=h4;小見出し=h5;',
	'fontsize_formats' => '1.3rem 1.5rem 1.6rem 1.8rem 2.0rem 2.2rem 2.4rem 2.8rem 3.2rem',
	'body_class' => 'entry_wrap',
];
include_once 'xxx/functions_setup.php';

先ず xxx にカスタム投稿タイプ slug を記載します。

name

カスタム投稿タイプ名

posts_per_page

wp_query で取得される標準のアーカイブ投稿数

editor

classic(クラッシックエディター)、gutenberg(ブロックエディター)から選択。

デフォルトは「classic」。※ advanced_custom_fields との相性が良いため

eyecatch

アイキャッチ投稿欄の有無

route

アーカイブパスやシングルスラッグのルーティング設定。

投稿タイプ内に固定ページを設置する際もここで設定。

sitemap

sitemap.xmlの自動生成を行います。数値(少数)は0.1~1までのpriorityです。

columns

管理画面一覧で表示するものの設定、カスタムフィールドの画像や値、カスタムタクソノミーのラベルなどを表示します。

functions_setup.php

上記の設定定数を元にカスタム投稿タイプを追加します。

このファイルはバージョン管理され、各サイト共通で設置しています。

※ カスタム投稿タイプの設定はDBに保存されず、ここで返される各値がマスターになります。

カスタム投稿タイプの追加

設定値を元にカスタム投稿タイプが追加されます。

固定ページの下層に投稿タイプを配置するなどルーティングを変更することも可能です。

ブロックエディター、クラッシックエディターの切り替えも追加時に行います。

add_action('init', function () {
	global $post_type;
	$check_taxonomy = [];
	$check_eycatch = [];
	foreach (CUSTOM_TAXONOMY_FIXED as $k => $v) {
		$check_taxonomy[$v['post_type']][] = $k;
	}
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		$args = [
			'labels' => [
				'name' => $v['name'],
				'singular_name' => $v['name'],
				'all_items' => '投稿一覧',
				'menu_name' => $v['name']
			],
			'public' => true,
			'exclude_from_search' => false,
			'has_archive' => true,
			'rewrite' => (isset($v['route']['archive_path']) && is_array($v['route']['archive_path'])) ? $v['route']['archive_path'] : true,
			'show_in_rest' => $v['editor']['type'] === 'gutenberg',
		];
		if (isset($check_taxonomy[$k])) {
			$args['taxonomies'] = $check_taxonomy[$k];
		}
		if (isset($v['eyecatch']) && $v['eyecatch']) {
			$args['supports'] = ['title', 'editor', 'thumbnail', 'excerpt', 'author', 'page-attributes'];
			$check_eycatch[] = $k;
		}
		if ($k !== 'post') {
			register_post_type($k, $args);
		}
	}
	if (in_array($post_type, $check_eycatch)) {
		add_theme_support('post-thumbnails');
	}
});

ルーティング追加

ルーティングはプラグインを使用せず管理しています。

foreach (CUSTOM_POSTTYPE_FIXED as $posttype => $v) {
	add_rewrite_rule('^' . $posttype . '/?$', 'index.php?post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/page/([0-9]{1,})/?$', 'index.php?post_type=' . $posttype . '&paged=$matches[1]', 'top');
	add_rewrite_rule('^' . $posttype . '/([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$', 'index.php?year=$matches[1]&monthnum=$matches[2]&paged=$matches[3]&post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/([0-9]{4})/([0-9]{1,2})/?$', 'index.php?year=$matches[1]&monthnum=$matches[2]&post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/([0-9]{4})/page/?([0-9]{1,})/?$', 'index.php?year=$matches[1]&paged=$matches[2]&post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/([0-9]{4})/?$', 'index.php?year=$matches[1]&post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/author/([^/]+)/page/?([0-9]{1,})/?$', 'index.php?author_name=$matches[1]&paged=$matches[2]&post_type=' . $posttype . '', 'top');
	add_rewrite_rule('^' . $posttype . '/author/([^/]+)/?$', 'index.php?author_name=$matches[1]&post_type=' . $posttype . '', 'top');
}

アーカイブ表示投稿数

一部、カスタムタクソノミーの設定値によるタクソノミーアーカイブの設定も同時に行っています。

add_action('pre_get_posts', function ($query) {
	if (is_admin() || !$query->is_main_query()) {
		return;
	}
	$is_category_archive = true;
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		if (is_post_type_archive($k)) {
			$query->set('posts_per_page', $v['posts_per_page']);
			$is_category_archive = false;
			break;
		}
	}
	if ($is_category_archive) {
		foreach (CUSTOM_TAXONOMY_FIXED as $k => $v) {
			if (is_tax($k)) {
				$query->set('posts_per_page', $v['posts_per_page']);
				break;
			}
		}
	}
});

エディターのデフォルトを「classicエディター」に

実際には、固定ページの設定と重複するためここには記載せず、固定ページの処理と一緒に記載しています。

add_filter('use_block_editor_for_post', function ($use_block_editor, $this_post) {
	global $post_type;
	if (in_array($post_type, array_keys(CUSTOM_POSTTYPE_FIXED))) {
		if (isset(CUSTOM_POSTTYPE_FIXED[$post_type]['editor']['type']) && CUSTOM_POSTTYPE_FIXED[$post_type]['editor']['type'] === 'gutenberg') {
			return $use_block_editor;
		}
	} else {
		return false;
	}
}, 10, 2);

エディター(tinymceのアイコン)設定

//tinymce ボタン1行目
add_filter('mce_buttons', function ($buttons) {
	global $post, $post_type;
	if ($post_type === 'page') {
		if (in_array($post->ID, array_keys(PAGES_SETTING_FIXED)) && isset(PAGES_SETTING_FIXED[$post->ID]['editor']['tinymce'])) {
			$buttons = PAGES_SETTING_FIXED[$post->ID]['editor']['tinymce'];
		}
	} else {
		if (in_array($post_type, array_keys(CUSTOM_POSTTYPE_FIXED)) && isset(CUSTOM_POSTTYPE_FIXED[$post_type]['editor']['tinymce'])) {
			$buttons = CUSTOM_POSTTYPE_FIXED[$post_type]['editor']['tinymce'];
		}
	}
	return $buttons;
}, 1);
//tinymce ボタン2行目(共通)
add_filter('mce_buttons_2', function ($buttons) {
	return [];
}, 1);
//段落・見出し要素、フォントサイズの設定(共通)
add_filter('tiny_mce_before_init', function ($inits) {
	if (defined('TINYMCE_INITS')) {
		$inits['block_formats'] = TINYMCE_INITS['block_formats'] ?? $inits['block_formats'];
		$inits['fontsize_formats'] = TINYMCE_INITS['fontsize_formats'] ?? $inits['fontsize_formats'];
		$inits['body_class'] = TINYMCE_INITS['body_class'] ?? $inits['body_class'];
	}
	return $inits;
});
//tinymce 表(テーブル)
add_filter('mce_external_plugins', function ($plugins) {
	$plugins['table'] = home_url() . '/wp/wp-content/themes/base/xxx/asset/tinymce_table_plugin.min.js';
	return $plugins;
});

tinymce の tableに関するJavaScriptはCDNで読み込んでいましたが、ある日突然切れてしまいました。今は、ダウンロードして読み込んでいます。途中の「xxx(ディレクトリ名)」は、ご自由に。

シングルページの日本語スラッグ自動変換

このスラッグは、wp-posts テーブルの「postname」としてDB保存され、URLの一部としても使用されます。理由はいくつかありますが、弊社では日本語ではなく、英語に変換するようにしています。

add_filter('wp_unique_post_slug', function ($slug, $post_id, $post_status, $post_type) {
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		if ($k === $post_type && $v['route']['single_auto_slug']) {
			if ($post_id && preg_match('/(%[0-9a-f]{2})+/', $slug)) {
				$slug = 'post' . $post_id;
			}
		}
	}
	return $slug;
}, 10, 4);

カスタム投稿タイプ内に固定P追加

add_action('init', function () {
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		if (isset($v['add_page']) && is_array($v['add_page']) && count($v['add_page'])) {
			foreach ($v['add_page'] as $vv) {
				if (isset($vv['name']) && isset($vv['pade_id'])) {
					add_rewrite_rule('^' . $k . '/' . $vv['name'] . '/?$', 'index.php?page_id=' . $vv['pade_id'], 'top');
				}
			}
		}
	}
});

管理画面内の投稿一覧の表示

管理画面内の投稿一覧にカスタムフィールド(wp_pos_meta)の情報やカスタムタクソノミーの値などを表示します。

各投稿画面を開かず確認したい情報や並べ替えの基準になる情報を表示することで、管理画面がとても使いやすくなります。

WordPress 管理画面内の投稿一覧のカスタマイズ

弊社では下記のデータをすぐに表示できるよう準備しています。

  • カスタムフィールドの画像
  • カスタムフィールドの日付
  • カスタムフィールドのラジオ、プルダウンの選択済値
  • カスタムフィールドのファイル名
  • カスタムフィールドのチェックボックス ※ 「/」区切り全て
  • カスタムフィールドの関連投稿
  • 日付から判定される NEWマークの有無
  • カスタムタクソノミーの name値
  • 独自関数により複数の値による返り値
add_filter('manage_posts_columns', function ($columns) {
	$this_post_type = get_query_var('post_type');
	/* common : remove default feild */
	unset($columns['tags'] , $columns['comments']); // タグ&カスタムフィールド
	/* post_type */
	$add_columns = [];
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		if ($this_post_type === $k) {
			$count = 0;
			foreach ($v['columns']['column'] as $vv) {
				$add_columns['column' . $count] = $vv['name'];
				$count++;
			}
			if (!$v['columns']['author']) {
				unset($columns['author']);
			}
			break;
		}
	}
	wp_oo_array_insert($columns, 2, $add_columns);
	return $columns;
});
add_action('manage_posts_custom_column', function ($column, $post_id) {
	$post_type = get_query_var('post_type');
	$arr = [];
	foreach (CUSTOM_POSTTYPE_FIXED as $k => $v) {
		if ($k === $post_type) {
			$arr = $v['columns']['column'] ?? [];
			$count = 0;
			foreach ($arr as $vv) {
				if ($column === 'column' . $count) {
					res_column($vv['type'], $vv['arg'], $post_id);
					break;
				}
				$count++;
			}
			break;
		}
	}
}, 10, 2);
// 一覧での表示項目
function res_column($type, $arg, $post_id)
{
	switch ($type) {
		case 'debug':
			$val = get_field($arg, $post_id);
			var_dump($val);
			break;
		case 'normal':
			$val = get_field($arg, $post_id);
			echo $val ? $val : '--';
			break;
		case 'image':
			$img_arr = get_field($arg, $post_id);
			echo isset($img_arr['sizes']['thumbnail']) ? '<img src="' . $img_arr['sizes']['thumbnail'] . '" width="90">' : '--';
			break;
		case 'date':
			$date = get_field($arg, $post_id);
			echo $date ? date_i18n('Y-m-d', $date) : '--';
			break;
		case 'time':
			$date = get_field($arg, $post_id);
			echo $date ? date_i18n('H:i', $date) : '--';
			break;
		case 'radio_select':
			$both = get_field($arg, $post_id); // 返り値:arr
			echo isset($both['label']) ? $both['label'] : '--';
			break;
		case 'file':
			$file = get_field($arg, $post_id);
			if ($file) {
				echo '<a href="' . $file . '" target="_blank">PDFファイル</a>';
			} else {
				echo '--';
			}
			break;
		case 'checkbox':
			$boths = get_field($arg, $post_id); // 返り値:arr
			$boths = is_array($boths) ? $boths : [];
			$arr = [];
			foreach ($boths as $both) {
				$arr[] = $both['label'];
			}
			echo $arr ? join(' , ', $arr) : '--';
			break;
		case 'taxonomy':
			$terms = is_array(get_the_terms($post_id, $arg)) ? get_the_terms($post_id, $arg) : [];
			$arr = [];
			foreach ($terms as $term) {
				// カテゴリー3階層まで取得
				$temp = '';
				if ($term->parent != 0) {
					$temp_parent = get_term($term->parent, $arg);
					if ($temp_parent->parent != 0) {
						$temp .= get_term($temp_parent->parent, $arg)->name . ' > ';
					}
					$temp .= $temp_parent->name . ' > ';
				}
				$temp .= $term->name;
				$arr[] = $temp ;
			}
			echo $arr ? join(' / ', $arr) : '--';
			break;
		case 'relation':
			$relations = is_array(get_field($arg, $post_id)) ? get_field($arg, $post_id) : [];
			$arr = [];
			foreach ($relations as $post_id) {
				$arr[] = get_the_title($post_id);
			}
			echo $arr ? join('/', $arr) : '--';
			break;
		case 'post_object':
			$temp_post_id = get_field($arg, $post_id) ? get_field($arg, $post_id) : false;
			if ($temp_post_id) {
				echo get_the_title($temp_post_id);
			} else {
				echo '--';
			}
			break;
		case 'newmark':
			$date_compare = date_i18n('Ymd', strtotime(date_i18n('Y-m-d') . '-1 month'));
			$temp_date = get_the_date('Ymd', $post_id);
			if ($temp_date > $date_compare) {
				echo '○:初回投稿日より1カ月以内';
			} else {
				echo '✕:初回投稿日より1カ月経過';
			}
			break;
		case 'replace':
			$acf_val = get_field($arg['acf_key'], $post_id);
			echo isset($arg['acf_key'][$acf_val]) ? $arg['acf_key'][$acf_val] : $acf_val;
			break;
		case 'supple':
			echo $arg;
			break;
		case 'linktype':
			$arr = get_field($arg, $post_id) ;
			if ($arr['type']['value'] === 'type_nolink') {
				echo 'リンクなし';
			} elseif ($arr['type']['value'] === 'type_url') {
				echo 'リンク-URL';
			} elseif ($arr['type']['value'] === 'type_detail') {
				echo 'リンク-詳細ページ';
			} elseif ($arr['type']['value'] === 'type_pdf') {
				echo 'リンク-PDFファイル';
			} else {
				echo 'リンク設定なし';
			}
			break;
		case 'func':
			$func = $arg['func_name'];
			echo $func($post_id, $arg);
			break;
		default:
			echo '-';
	}
}

設定定数 CUSTOM_POSTTYPE 補足とサンプル

上記の設定定数をサンプルとコード内コメントで補足しています。

const CUSTOM_POSTTYPE = [
	'xxx' => [
		'name' => 'カスタム投稿タイプ名称',
		'posts_per_page' => 10,
		// エディタ
		'editor' => [
			'type' => 'classic', // classic || gutenberg
			'tinymce' => [
				'link',
				'unlink',
				'|',
				'formatselect',
				'bullist',
				'numlist',
				'|', // 区切り線
				'formatselect', // 見出し、段落の設定
				'bullist', // ul li
				'numlist', // ol li
				'|', // 区切り線
				'link', // リンク
				'unlink', // リンク解除
				'|', // 区切り線
				'forecolor', // テキストカラー
				'table', // 表
				'fontsizeselect', // フォントサイズ
				'bold', // フォントサイズ
				'italic', // フォントサイズ
				'styleselect ', // テキストスタイル、見出し、ブロックなど
				'alignleft',
				'aligncenter',
				'alignright',
				'alignjustify',
				'outdent', // padding -40px
				'indent', // padding +40px
			],
		],
		'eyecatch' => false,
		// routing
		'route' => [
			'archive_path' => 'auto', // 特定のディレクトリ(zzz)内にアーカイブする場合は右記の配列を記述 [ 'slug' => 'zzz/addposttypename' ]
			'single_auto_slug' => true, // slugの自動生成 * 'post' . $this_post_id
			'page_in_posttype' => [
				[
					'name' => 'pageneme', // リンクURL: /xxx/pageneme/
					'pade_id' => '3' // 固定ページのID
				]
			],
		],
		// sitemap_xml
		'sitemap' => [
			'archive' => '0.5', // アーカイブがない場合は「false」
			'single' => '0.6', // シングルがない場合は「false」
			'add_arg' => []
		],
		// 一覧での表示項目
		'columns' => [
			'author' => false,
			'column' => [
				[
					'name' => '一覧表示項目',
					'type' => 'normal',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => '画像',
					'type' => 'image',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => '日付',
					'type' => 'date',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => 'ラジオボタン or SELECT',
					'type' => 'radio_select',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => 'チェックボックス',
					'type' => 'checkbox',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => 'ファイル',
					'type' => 'file',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => 'カスタムタクソノミー',
					'type' => 'taxonomy',
					'arg' => 'cat_xxx' // taxonomy
				],
				[
					'name' => '関連',
					'type' => 'relation',
					'arg' => 'acf_xx' // acf
				],
				[
					'name' => 'NEWマーク',
					'type' => 'newmark',
					'arg' => '-1 month' // option
				],
				[
					'name' => '文字列変換',
					'type' => 'replace',
					'arg' => [
						'acf_key' => 'acf_xx', // acf : 返り値の文字列を変更
						'replace' => [
							'show' => '表示',
							'hide' => '非表示'
						]
					]
				],
				[
					'name' => '注釈',
					'type' => 'supple',
					'arg' => '*注釈文面' // acf
				],
				[
					'name' => '独自関数設定',
					'type' => 'func',
					'arg' => [
						'acf_key' => 'acf_xx',
						'func_name' => 'create_column_loop_xxx'
					],
				],
			],
		]
	],
];

routing補足

ルーティングとは、リクエストされたパスから「テンプレート選択」「DBの値を取得」などを行い、特定のページを表示することです。こちらを標準表示を変更します。

変更した後に「WordPress管理画面 > 設定 > パーマリンク設定」からフラッシュ(設定保存)することで、DBが更新されます。

columns

コメントで「// acf」と記載のある箇所には、advanced_custom_fields で設定したカスタムフィールドのkeyを記載します。

独自関数設定する場合は、arg は配列で 関数名をfuncname に設定します。

関数サンプル

function create_column_loop_xxx($this_post_id, $arg)
{
	$acf_loop = get_field($arg['acf_key'], $this_post_id) ?? [];
	$res_arr = [];
	foreach ($acf_loop as $v) {
		$res_arr[] = '<a href="' . $v['aaa'] . '" target="_blank">' . $v['bbb'] . '</a>';
	}
	return join(' / ', $res_arr);
}
pagetop
loading