Web事業部_クリエイティブ
Web事業部_クリエイティブ
2018.12.07

【2018年WordPress魔改造TOP3】第3位:特定のテンプレートだけ別テーマを使う

リョウタ

魔改造」。過剰なカスタマイズを施したものを、我々は時にこう呼びます。この字面に溢れる厨二感……。

こんにちは、バックエンドエンジニアのリョウタです。

WordPress系のイベントに登壇する際のスライドにも書いているのですが、私はWordPressの魔改造が大好きです

「これやるんだったらスクラッチで作ればいいじゃん」って言われそうなものを、なかば無理やりにでもWordPressで実装するのが楽しいんです。わかりますかね? この気持ち。

今年も今年とて、新規・リニューアル・保守とさまざまなWordPress案件に関わってきましたが、その中でも特に記憶に残った「魔改造」3つを順に振り返っていこうと思います。今回は第3位の発表です。

第3位:特定のテンプレートだけ別テーマを使う

予算や納期の兼ね合いで、段階的なリリースになる案件はままあります。

新規制作案件の場合、リリース後に待つフェーズは「ページや機能の単純な追加」になることが多いのであまり問題になりませんが、リニューアル案件で「過去のデータ・仕様を引き継いだ上で段階リリース」となると、どこからともなく「魔」の臭いが立ち込めてきます。

さて、今回の要件です。

  • リニューアル前のテーマは商用テーマのさらに子テーマ。リニューアル後も一部のテンプレートはこのテーマを使う
  • リニューアル前のテーマにはウィジェットの設定やプラグインと連携する機能が含まれているようだ
  • iPhoneのWordPressアプリを使って更新することもあるため、アプリからの新テーマでのプレビューも可能にしたい
  • 年別・月別アーカイブや、カテゴリー、タグアーカイブなども対象

闇属性確定。ワクワクしますね。

「templateSwitcher」プラグインを作ってみました

テーマが読み込まれる前に分岐しなくてはならないため、諸々の処理はプラグインを作ってその中で行います。

class templateSwitcher
{
    public $templateName = '';

    public function __construct($templateName)
    {
        //引数は切り替えるテーマのディレクトリ名
        $this->templateName = $templateName;

        //プラグインのロード時に呼び出す
        add_action('plugins_loaded', array($this, 'template_switcher_filter_hooks'));

        //共通で読み込むファイル
        foreach (glob(__DIR__ . '/lib/*.php') as $file) {
            require_once $file;
        }
    }

    public function template_switcher_filter_hooks()
    {
        if (!is_admin()) {
            //テーマ切り替えのフィルターフックはこれ
            add_filter('pre_option_template', array($this, 'template_switcher'));
            add_filter('pre_option_stylesheet', array($this, 'template_switcher'));
        }
    }

    public function template_switcher($array)
    {
        //対象の固定ページを指定
        $pages = array(
            '/', //トップページ,
            '/about/', //固定ページ
            '/about/history/', //固定ページ
        );
		
        //対象のリライトルールを指定。
        $rules = array(
            '([0-9]{4})/?$', //年別アーカイブ
            '([0-9]{4})/page/?([0-9]{1,})/?$',
            '([0-9]{4})/([0-9]{1,2})/?$', //月別アーカイブ
            '([0-9]{4})/([0-9]{1,2})/page/?([0-9]{1,})/?$',
            'category/(.+?)/?$', //カテゴリーアーカイブ
            'category/(.+?)/page/?([0-9]{1,})/?$',
            'tag/([^/]+)/?$', //タグ別アーカイブ
            'tag/([^/]+)/page/?([0-9]{1,})/?$',
            '([0-9]{4})/([0-9]{1,2})/([^/]+)(?:/([0-9]+))?/?$', //詳細ページ(パーマリンク設定が /%year%/%monthnum%/%postname%/ の場合
        );

        //$rulesを繋げて、正規表現のパターンを作成
        $pattern = '/(^\/' . str_replace('/', '\/', implode(')|(^/', $rules)) . ')/';

        if (
            in_array($_SERVER['REQUEST_URI'], $pages, true) || //固定ページ
            preg_match($pattern, $_SERVER['REQUEST_URI']) || //リライトルール
            preg_match('/wp-iphone/', $_SERVER['HTTP_USER_AGENT']) || //iPhoneアプリプレビュー用
            preg_match('/.*\?.*preview=(true|1)(&.*$|$)/', $_SERVER['REQUEST_URI']) //プレビュー用
        ) {
            //TRUEならテーマを切り替える
            return $this->templateName;
        } else {
            return $array;
        }
    }
}

if (!isset($templateSwicher)) {
    //テーマを読みに行く前に処理しなければならないので、ここでnewする。
    $templateSwicher = new templateSwitcher('newthemename');
}

「魔」ポイントを解説します

ポイント①

コンストラクタから’plugins_loaded’のアクションフックでtemplate_switcher_filter_hooks()を実行します。

WordPressはコアファイル→プラグイン→テーマの順にロードされます。しかし、テーマ内から全体的にフックできるように設計されているため、プラグインロード完了(plugin_loaded)からテーマを読みに行くまで(setup_theme)の間に用意されているフックは多くありません。

‘plugin_loaded’時点でis_admin()は使用できますので、管理画面だった場合の分岐はここで行います。

また、両テーマ共通の処理もここで読み込みます。

ポイント②

template_switcher()を実行します。フィルターフックは’pre_option_template’と’pre_option_stylesheet’です。

pre_option_(option)系のフィルターはoptionsの値をフィルタリングします。

ポイント③

テーマ切り替えのための条件を$pagesと$rulesに指定します。

残念ながらこの段階で、is_page()や、is_post_type_archive()、is_tax()などの関数を使うことはできません。ですので、rewrite_ruleや、URLを指定して、$_SERVER[‘REQUEST_URI’]と比較します。

アプリからのアクセスもここで$_SERVER[‘HTTP_USER_AGENT’]を見て判定します。iPhoneのWordPressアプリからのアクセスの場合、wp-iphoneという文字列を含むUAになるようです。

あとがき

「こんな機能いる?」って言われてこその魔改造ですが、きっといつか、どこかで、誰かの役に立つと思って書いてみました。第2位、第1位はもっと役に立たないはず。お楽しみに。

この記事が公開されるころ、私のバンド「GRAND FAMILY ORCHESTRA」はちょうど東名阪ツーマンツアー直前です。ご都合つくようでしたらぜひ遊びに来てくださいね。

それではまた。