こんにちは、@ttskch です。

前回の記事 で、WordPress のテーマディレクトリ内に Silex アプリを構築することによって 固定ページをファイルベースで管理する というテクニックをご紹介しました。

今回はその発展編で、テンプレートに Twig を使う方法をご紹介します。 これでさらに WordPress サイトの管理が捗りますね。

はじめに

前回の記事 を読んで、Silex ベースのテーマを構築するところまでは終わっている前提で説明しますので、まだの方は確認してみてください。

また、Twig 導入後のサンプルを GitHub に置いておきました ので、参考にしながら読み進めてみてください。

目次

# まずは Twig をインストール

まずは Twig 自体のインストールです。composer で twig/twig をインストールします。

$ php composer.phar require twig/twig ">=1.8,<2.0-dev"

# TwigServiceProvider の設定

Silex のフロントコントローラにしてある、テーマディレクトリ直下の index.php に、TwigServiceProvider の設定を追記します。

<?php
$app = new Silex\Application();

// use twig.
$app->register(new Silex\Provider\TwigServiceProvider(), [
    'twig.path' => __DIR__ . '/views',
]);

これで、$app['twig']\Twig_Environment クラスのインスタンスがセットされた状態になります。テンプレートディレクトリのパスは、前回作った views ディレクトリを指定しています。

# レンダリング処理の変更

Twig 導入前は、テンプレート PHP から HTML をレンダリングするためのユーティリティを自前で用意していましたが、ここからはレンダリング処理はサービスにお任せでよいので、処理を変更します。

WordPress ページ用の処理

<?php
// for authors.
$app->get('/archives/author/{route}', function ($route) use ($app) {
    return $app['twig']->render('author.html.twig');
})->assert('route', '.*');

// for posts.
$app->get('/archives/{route}', function ($route) use ($app) {
    return $app['twig']->render('single.html.twig');
})->assert('route', '.*');

// for categories.
$app->get('/category/{route}', function ($route) use ($app) {
    return $app['twig']->render('category.html.twig');
})->assert('route', '.*');

// for tags.
$app->get('/tag/{route}', function ($route) use ($app) {
    return $app['twig']->render('tag.html.twig');
})->assert('route', '.*');

全部を記載しましたが、前回から変わったのはレンダリング処理だけです。 $app['twig]->render() に、レンダリングしたいテンプレートファイルのパス (views からの相対パス) を渡せば OK です。

静的ページ用の処理

<?php
// default.
$app->get('/{route}', function ($route) use ($app) {
    $template = 'pages/' . trim($route, '/') . '.html.twig';
    try {
        return $app['twig']->render($template);
    } catch (Twig_Error $e) {
        $template = 'pages/' . trim($route, '/') . '/index.html.twig';
        try {
            return $app['twig']->render($template);
        } catch (Twig_Error $e) {
            return $app['twig']->render('404.html.twig');
        }
    }
})->assert('route', '.*');

ここもほとんど変わってないのですが、前回 ファイルがなければ 404 をレンダリング、という処理を書いていたところを、Twig_Error の例外が発生したら という条件に変更しています。

# WordPress タグを Twig 関数化

これで単純に Twig を使えるようにはなりましたが、これだけだと、困ったことに Twig 内で WordPress タグが使えません (当たり前ですが)。

そこで、WordPress タグのうち必要なものを Twig 関数として使えるようにしておきましょう。 TwigEnvironment の拡張方法は、公式のドキュメント で言及されています。

WordPress タグを Twig 関数として追加したい場合なら、例えばこんな感じになるでしょう。

<?php
// use twig.
$app->register(new Silex\Provider\TwigServiceProvider(), [
    'twig.path' => __DIR__ . '/views',
]);

// add twig functions.
$app['twig'] = $app->share($app->extend('twig', function ($twig, $app) {
    $twig->addFunction('bloginfo', new \Twig_SimpleFunction('bloginfo', 'bloginfo'));
    $twig->addFunction('home_url', new \Twig_SimpleFunction('home_url', 'home_url'));
    return $twig;
}));

他にも必要なものはここで追加しておきましょう。

TwigExtension を追加する

同様に TwigExtension の追加も簡単にできます。例えば、truncate フィルタとかはあとあと使いたくなりそう (長いタイトルを 30 文字ぐらいに丸めたりとか) なので、試しに Twig_Extensions_Extension_Text を導入してみましょう。

まずはパッケージのインストールが必要です。例によって composer でインストールします。

$ php composer.phar require twig/extensions "~1.0"

あとは、さっきの Twig 関数の追加とほぼ同じ方法で追加するだけです。

<?php
$app['twig'] = $app->share($app->extend('twig', function ($twig, $app) {
    $twig->addExtension(new \Twig_Extensions_Extension_Text($app)); // added
    $twig->addFunction('bloginfo', new \Twig_SimpleFunction('bloginfo', 'bloginfo'));
    $twig->addFunction('home_url', new \Twig_SimpleFunction('home_url', 'home_url'));
    return $twig;
}));

自作のフィルタや関数は Twig Extension を作ってまとめておくとメンテしやすいかもしれませんね。

# テンプレートを Twig で書きなおす

ここまでで Twig の環境は準備万端整いましたので、実際にビューのテンプレートを Twig に書き換えてみましょう。

例えば 投稿表示テンプレート (single.php) ならこんな感じでしょう。

Before (single.php)

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
    <h2><?php the_title(); ?></h2>
    <p>著者:<?php the_author_posts_link(); ?></p>
    <p>カテゴリ:<?php the_category(); ?></p>
    <p><?php the_tags(); ?></p>
    <p><?php the_content(); ?></p>
<?php endwhile; endif; ?>

After (single.html.twig)

{% for post in get_posts() %}
    {% if have_posts() %}
        {{ the_post() }}
        <h2>{{ the_title() }}</h2>
        <p>著者:{{ the_author_posts_link() }}</p>
        <p>カテゴリ:{{ the_category() }}</p>
        <p>{{ the_tags() }}</p>
        <p>{{ get_the_content() }}</p>
    {% endif %}
{% endfor %}

他のテンプレートも同じような感じで Twig に置き換えていけば完成です。

繰り返しになりますが、実際に動作可能なサンプルを GitHub に置いてあります ので、参考にしてみてください。

# 番外編:もう少し index.php のコードをスッキリさせたい

見ての通り、このままだとかなりの数の WordPress タグを Twig 関数化する必要があります。 実際、サンプルコードの Twig 拡張定義箇所はこんな感じになっちゃってます。

<?php
// add twig functions.
$app['twig'] = $app->share($app->extend('twig', function ($twig, $app) {
    $twig->addExtension(new \Twig_Extensions_Extension_Text($app));
    $twig->addExtension(new \Twig_Extension_Sample($app));
    $twig->addFunction('bloginfo', new \Twig_SimpleFunction('bloginfo', 'bloginfo'));
    $twig->addFunction('query_posts', new \Twig_SimpleFunction('query_posts', 'query_posts'));
    $twig->addFunction('wp_reset_query', new \Twig_SimpleFunction('wp_reset_query', 'wp_reset_query'));
    $twig->addFunction('home_url', new \Twig_SimpleFunction('home_url', 'home_url'));
    $twig->addFunction('get_posts', new \Twig_SimpleFunction('get_posts', 'get_posts'));
    $twig->addFunction('have_posts', new \Twig_SimpleFunction('have_posts', 'have_posts'));
    $twig->addFunction('the_post', new \Twig_SimpleFunction('the_post', 'the_post'));
    $twig->addFunction('the_permalink', new \Twig_SimpleFunction('the_permalink', 'the_permalink'));
    $twig->addFunction('the_title', new \Twig_SimpleFunction('the_title', 'the_title'));
    $twig->addFunction('get_the_content', new \Twig_SimpleFunction('get_the_content', 'get_the_content'));
    $twig->addFunction('the_author', new \Twig_SimpleFunction('the_author', 'the_author'));
    $twig->addFunction('the_category', new \Twig_SimpleFunction('the_category', 'the_category'));
    $twig->addFunction('the_tags', new \Twig_SimpleFunction('the_tags', 'the_tags'));
    $twig->addFunction('single_cat_title', new \Twig_SimpleFunction('single_cat_title', 'single_cat_title'));
    $twig->addFunction('single_tag_title', new \Twig_SimpleFunction('single_tag_title', 'single_tag_title'));
    $twig->addFunction('the_author_posts_link', new \Twig_SimpleFunction('the_author_posts_link', 'the_author_posts_link'));
    return $twig;
}));

ここで登録している WordPress タグのほとんどは投稿の情報を取得するためのものなので、投稿の情報を配列で返してくれるユーティリティとかを作っておいて、

<?php
// routing for categories.
$app->get('/category/{route}', function ($route) use ($app, $util) {
    return $app['twig']->render('category.html.twig', [
        'posts' => $util->getPosts('post_type=post&posts_per_page=10'),
    ]);
})->assert('route', '.*');

みたいな感じでビューに渡してあげるようにしておけば、ビュー側で WordPress タグが必要になるケースはかなり減るんじゃないかと思います。

余力のある人はチャレンジしてみてください :)