PHP
scss
CMS
Drupal
twig
DrupalDay 1

Drupal7と8でテーマ制作がどう変わったか

More than 1 year has passed since last update.

Drupal8ではHTML5対応やレスポンシブデザインへのサポート機能、そのほかにもモダンな技術が数多く取り込まれていて、WordPressやMovableTypeなどほかのCMSを利用している方から見ても魅力的なものになりました。

今回はおもにフロントエンド周りのとくにテーマ制作について、Drupal7から8へと乗り換えたときのチェックポイントを紹介したいと思います。

(この記事は Drupal Advent Calendar 2016 のために書いたものです)


Drupal7のテーマ制作を振り返る

druplicon-small.png

D7では PHPTemplate と呼ばれるテーマエンジンがあり、Drupal4からデフォルトで採用されて移行、扱いやすさからD7以前から長らく標準搭載されて利用されてきました。

PHPTemplateは拡張子がphpになっていて、テンプレートファイル上で純粋なphpコードを利用することができて Drupal API やモジュール上で定義したクラス・関数を呼び出したりと、書式に制限がなく柔軟に扱うことができていました。

スクリーンショット 2016-12-01 3.47.55.png

ただ、PHPTemplateエンジンは実行速度がとても遅くて、そのことでページ表示が重いことがネックになっていました。


Drupal8のテーマ制作はどう変わったか

drupal 8 logo isolated CMYK 72.png

D8ではHTML5がサポートされて、同梱テーマやモジュールがHTML5対応になってことが大きな変化です。

PHPTemplateエンジンに代わって、Twigがデフォルトエンジンになりました。これによってPHPTemplate エンジンは削除されています。

つまり、* .tpl.phpファイルのすべてが使用できなくなりました。

D8のテンプレートファイルは、新しいTwigの拡張子を利用します。

* .html.twig


Twigとは

symfony_black_02.png

D8はフレームワーク「symfony2」を採用したことで、デフォルトテーマエンジンがTwigに置き換えられました。

Twigは独自の記法によりテンプレートを記述することが前提ですが、テンプレートがキャッシュされた状態だと非常に高速にレンダリングされます。

ただ、PHPTemplateエンジンのときのようにテンプレートファイル上にHTMLソースとphpコードを混ぜて書くことができません。

D7のphpコードが自由に書けたときと比べると自由度は落ちますが、シンプルで可読性が高く、Webデザイナーさんでも編集しやすいように設計されています。

これは、モデル、ビュー、コントローラという3つの処理に分割(※)されていることにより、テンプレートの品質向上が保たれます。

※Twigはモデル、ビュー、コントローラ(MVCパターン)のビューの部分に当たります。

そして人によってテーマ設計がブレにくいなどのメリットがあります。


Drupal7 + PHPTemplateエンジン

例)views-view-unformatted.tpl.php ファイルの中身


views-view-unformatted.tpl.php

<?php if (!empty($title)): ?>

<h3><?php print $title; ?></h3>
<?php endif; ?>
<?php foreach ($rows as $delta => $row): ?>
<div class="<?php echo $classes_array[$delta]; ?>">
<?php print $row; ?>
</div>
<?php endforeach; ?>


Drupal8 + Twigエンジン

例)views-view-unformatted.html.twig ファイルの中身


views-view-unformatted.html.twig

{% if title %}

<h3>{{ title }}</h3>
{% endif %}
{% for row in rows %}
{% set parity = cycle(['odd', 'even'], loop.index0) %}
{%
set row_classes = [
default_row_class ? 'views-row ' ~ parity,
]
%}
<div{{ row.attributes.addClass(row_classes) }}>
{{ row.content }}
</div>
{% endfor %}

以上、構文の違いが見て取れると思います。

Twig の基本的な構文です。



  • {{ 〜〜 }} : 変数や計算式の結果を出力します。


  • {% 〜〜 %} :if文やfor文などのロジックを制御するタグです。


  • {# 〜〜 #} : コメントを残します。HTMLには出力されません。

テンプレートファイルをincludeすることも用可能です。

{% include directory ~ '/templates/inc/header.html.twig' %}

参考)https://www.drupal.org/docs/8/theming-drupal-8/including-part-template

なお、includeは相対パスを指定できないのでご注意ください。

Twig フィルターを利用すれば、変数に対して簡単な処理をかけることもできます。

{{ variable|filter }}.

フィルターにはphp関数ではおなじみの striptags や nl2br、 trim などが用意されています。

その他、フィルターがいろいろ用意されていますので公式ドキュメントでご確認ください。

Twig Filters - Documentation

http://twig.sensiolabs.org/doc/filters/index.html

また、変数をデバッグするときは Twigが提供するデバッグ関数 dump() が利用できます。

{{ dump(var) }}

引数に何も指定しなければすべての変数が確認できます。

{{ dump() }}

スクリーンショット 2016-11-30 22.32.29.png

DrupalのDevelサブモジュール「Kint」を有効化している場合は、D7のdpm関数と同じようなアコーディオン形式で変数を表示させることができます。

{{ kint(var) }}

引数に何も指定しなければすべての変数が確認できます。

{{ kint() }}

スクリーンショット 2016-11-30 22.30.22.png

kintの描画結果は表示に時間がかかるので、アコーディオンが展開可能な状態になるまでに数十秒ほど待たされることがあります。

以上、デバッグ方法の紹介でした。


Drupal8でテーマ制作をしやすくするための環境設定


Twig debugging options

Drupalテーマは読み込まれているテンプレートファイル名を表示することができます。

さらにどのようなテンプレートファイル名でファイルを設置すればいいのかを教えてくれる、いわゆるサジェスチョンという機能ですね。

D7のときは sites/default/settings.php ファイル上で以下1行を追加していました。

$conf['theme_debug'] = 1;

一方、D8からは sites/default/services.yml ファイル上の twig.config セクションにて管理されるようになりました。

(もし services.yml がまだ設置されていない場合は default.services.yml をコピーして services.yml へリネームしてください)

以下の通りに値を書き換えれば有効化されます。

debug: true    (デフォルト:false)

これにより、HTMLソース上にテンプレートファイル名のサジェスチョンが表示されるようになります。

スクリーンショット 2016-12-01 2.51.58.png

×印の行が今現在ロードされているテンプレートファイルです。


Twig auto-reload

Twigはパフォーマンス向上のためにコンパイルされた上でキャッシュされて、次の画面表示からはキャッシュが読み込まれます。

テーマ制作中はキャッシュの内容ではなく編集内容をすぐに確認できるようにしたいので auto reload を有効化します。

auto_reload: true  (デフォルト:null)

trueにしておけば、テンプレートが変更されるたびに再コンパイルされます。

ここをtrueにしておかないと、Drupalキャッシュクリアするまでテンプレートの編集内容が確認できなくなってしまうので制作効率が下がってしまいます。


Twig cache

cache: false  (デフォルト:true)

Twigテンプレートのコンパイル結果をディスクにキャッシュ保存することで高速化を行っています。

falseを指定すればディスクへのキャッシュ保存を無効化できますが、通常は auto_reload: true だけでも足りるので基本的にはデフォルトのtrueのままにしておきましょう。


Drupal render cache

D8は高速化のためにデフォルトでブロックとエンティティをキャッシュするので、それらの変更内容はキャッシュクリアするまでページ反映されません。

キャッシュを無効化するには sites/default/settings.php の次の2行を探してコメントアウトします。

if (file_exists(__DIR__ . '/settings.local.php')) {

include __DIR__ . '/settings.local.php';

ファイルをコピーします。

コピー元)/sites/example.settings.local.php

コピー先)/sites/default/settings.local.php

あとはDrupalキャッシュクリアを行えば設定が反映されます。

なお、これらの設定はパフォーマンスのために本番サイト上ではデフォルトの状態に戻すことをオススメします。

参考)https://www.drupal.org/docs/8/theming/twig/debugging-compiled-twig-templates


Twigテンプレート上で出力する変数をあらかじめ加工するには

Twigフィルターを利用すれば、変数に対してちょっとした加工などの操作は行えますがやはり限界があります。テンプレート上にPHPコードが書けたらと思うことがあると思います。

ですが、Twigテンプレート上ではPHPコードは使えませんので、あらかじめフックを利用して変数を操作する必要があります。

フックを記述する場所はテーマフォルダ配下の *.theme になります。

拡張子がphpではありませんが問題なくPHPコードが書けますのでご安心ください。

エンティティオブジェクトやフィールド値の操作を行う上で、よく利用するフックは例えば次のものが挙げられます。


  • templete_preprocess_breadcrumb(&$vars)

  • templete_preprocess_block(&$vars)

  • templete_preprocess_node(&$vars)

  • templete_preprocess_field(&$vars)

Viewsの出力内容を操作するには以下のフックなども利用可能です。


  • templete_preprocess_views_view_unformatted(&$vars)

  • templete_preprocess_views_view_field(&$vars)

フィールド値を加工する場合、Nodeなどのエンティティが対象の場合、templete_preprocess_fieldフックを利用しますが、Viewsが出力するフィールド値を加工するには templete_preprocess_views_view_fieldフックを利用します。

なお、フックの先頭の templete の部分はテーマ名に置き換えてご利用ください。

小ネタとして、例えば templete_preprocess_views_view_fieldフックを特定のViewsでのみ利用する場合は以下の命名をすると限定的に利用可能です。

templete_views_view_field__[Views name]__[Display name](&$vars)

利用例:

function example_theme_views_view_field__article_list__title(&$vars) {

preg_match("|<a href=\"(.*?)\".*?>(.*?)</a>|mis", $vars['field']->original_value, $matches);
$url = $matches[1];
$name = $matches[2];
return '' . $name . '<a href="' . $url . '" class="category-link"><span>記事一覧へ</span></a>';
}

すごく適当なコードで恐縮ですが、こんな感じでTwigテンプレートに変数が渡される前に値を加工することができます。


CSSとJavascriptを動的にロードする方法


Drupal7で動的にロードする書き方

D7では、CSSとJavascriptを動的にロードするときは drupal_add_css(), drupal_add_js() が利用できました。


drupal_add_css関数

$options = array('group' => CSS_THEME,  //CSS_SYSTEM or CSS_DEFAULT or CSS_THEME

'type' => 'file', // file or inline or external
'weight' => 0);

drupal_add_css(drupal_get_path('theme', 'any_theme_name') . '/css/style.2nd.css', $css_option);


drupal_add_js関数

$options = array('group' => JS_THEME, //JS_LIBRARY or JS_DEFAULT or JS_THEME

'type' => 'file', // file or inline or external
'scope' => 'header', // header or footer
'weight' => 0);

drupal_add_js(drupal_get_path('theme', 'any_theme_name') . '/js/custom.js', $options);

これらコードをモジュール上のプリプロセス系フックに書いていましたね。


D8で動的にロードする書き方

一方、D8ではあらかじめライブラリを定義しておきます。

ライブラリには、CSSとJSファイルの両方を含むことができて、ほかのライブラリとの依存関係も明記することができます。

以下、テーマ名を example_theme と仮定して説明していきます。

ライブラリを定義するには、テーマフォルダ配下に example_theme.libraries.yml ファイルを設置して、次のように記述します。

スクリーンショット 2016-12-01 2.27.14.png

気をつける点は、ライブラリを記述するときのインデントが重要だということです。

ライブラリ名は一番左端にくるよう、タブやスペースが入らないようにしてください。

そのほか、css、jsセクションのインデントもしっかりしておく必要があります。

CSSを定義している箇所で、theme: とありますね。

    css:

theme:
style/css/jquery.bxslider.css: {}

theme: 以外にもほかに用意されたグループを指定すればheadタグ上での出力順序を調整できます。



  • base key assigns a weight of CSS_BASE = -200


  • layout key assigns a weight of CSS_LAYOUT = -100


  • component key assigns a weight of CSS_COMPONENT = 0;


  • state key assigns a weight of CSS_STATE = 100


  • theme key assigns a weight of CSS_THEME = 200

あと、D7のときはJSファイルをheadタグ内に出力されていましたが、D8からはHTMLソースのフッター部分に出力されるようになりました。

もし、D7のときのようにheadタグ内にJSファイルを出力させたい場合は明示的に記述する必要があります。

js-header:

header: true
js:
js/jquery.bxslider.js: {}
js/jquery.easing.1.3.js: {}
js/jquery.fitvids.js: {}

js-footer:
js:
footer.js: {}

参考)https://www.drupal.org/node/2274843

そして、特定ページで動的にライブラリをロードするにはテーマフォルダ配下の example_theme.themeファイル上で次のように実装します。

スクリーンショット 2016-12-01 0.40.48.png

上のコードを読むと分かると思いますが、トップページでのみjquery_bxsliderライブラリをロードされるようになります。

もし、すべてのページで常にライブラリをロードしたい場合は、example_theme.info.yml ファイル上の libraries セクションに追加します。

スクリーンショット 2016-12-01 0.44.38.png

libraries セクションに追加されたライブラリはすべてのページでロードされます。

ただ、パフォーマンス向上のためにはやはり特定ページでのみロードされるように実装するべきかと思います。


Drupal8で人気のテーマ「OMEGA5」を導入する

D8の人気テーマとして BootstrapAdminimalAdaptiveTheme が人気です。

AdminimalAdaptiveTheme は導入した初期状態でそれなりのデザインなのでお手頃感がありますが、やはり、業務で利用するならなるべくベースがシンプルで汎用性の高い OMEGA5 テーマを導入したいです。

(D7ではカスタマイズし易いテーマとして OMEGA と双璧を成す Zen テーマがありましたが、D8版はまだstable版がリリースされていないようなので検討からは外しました)


OMEGA5をインストールしてサブテーマを設置する

OMEGA5テーマはここからダウンロードできます。

https://www.drupal.org/project/omega

ダウンロードしたテーマは themes フォルダ配下にコピーします。

スクリーンショット 2016-12-01 1.59.08.png

そしてDrupal管理画面のテーマ一覧 /admin/appearance にアクセスして、

OMEGA5テーマの install リンクをクリックすればテーマを有効化できます。

スクリーンショット 2016-12-01 2.01.42.png

D7のときはOMEGAサブテーマを作るときはdrushコマンドから行う必要がありましたが、管理画面上の Create Subtheme リンクをクリックするだけで作成できてしまうので便利になりました。

基本的にOMEGA5テーマを利用するときはサブテーマを作成して設定を進めていきます。


OMEGA5サブテーマの設定を行う

まず、Create Subtheme リンクをクリックします。

スクリーンショット 2016-12-01 2.07.17.png

Theme name を入力します。

このとき、既存モジュールと同じ名前をテーマに付けると、テーマが読み込まれなくなるようなのでご注意ください。

プロジェクト名をそのままテーマ名に付けて、モジュールのとりあえずプロジェクト名を付けたものを設置するなんてことはよくやりがちです。D7ではそれでも支障なく動作していましたがD8ではダメみたいです。

DescriptionVersion は任意の内容を入力しておきます。

SUB-THEME OPTIONS は Clone を選択します。

SCSSを利用する場合は、Enable SCSS Customizations にチェックを入れます。

最後に Export Subtheme ボタンをクリックしましょう。

スクリーンショット 2016-12-01 2.11.13.png

テーマ一覧の Uninstalled themes セクションに作成したサブテーマが追加されます。

Install and set as default リンクをクリックして有効化します。

有効化したら Settings リンクをクリックして設定画面を開きます。

Default Optionsタブを見てみましょう。

スクリーンショット 2016-12-01 2.16.37.png

OMEGA5では SCSSファイルを管理画面からコンパイル可能になりました。

Compile SCSS Directly にチェックを入れた状態で Save & Update Styles ボタンをクリックするとSCSSコンパイルが実行されます。

これは scssphp というコンパイラが組み込まれていて、わざわざSCSSコンパイル環境を構築しなくてもいいのでとくにWebデザイナーさんには優しい機能だと思います。

スクリーンショット 2016-12-01 2.30.34.png

CSS/JS Libraries タブでは example_theme.libraries.yml ファイルに記述しておいたライブラリが表示されます。

デフォルトでチェックが入った状態になっていますが、このままだとライブラリが常時ロードされてしまうようです。

templete_preprocess_pageフックで動的にロードしたい場合はチェックを外しましょう。

OMEGA5以外のほかのテーマだと *.info.yml の libraries:セクションに書かない限りはライブラリが常時ロードされないと思うので、OMEGA5固有の気をつけるポイントですね。

スクリーンショット 2016-12-01 2.46.20.png

Layout Builder タブでは example_theme.breakpoints.yml に記述されているレスポンシブにおけるブレークポイントの数値を編集できるようです。

ただし、新たなブレークポイントを追加するときは YAMLファイル上で手動で追記する必要があるようです。


OMEGA5サブテーマの公開画面

参考までにOMEGA5の表画面はこのような感じです。

スクリーンショット 2016-12-01 2.58.22.png

デモモードを有効化している場合はリージョンが表示されるところはOMEGAテーマおなじみの機能です。

OMEGA5からは画面の横幅を表示させる機能が追加されて、レスポンシブデザイン対応する上で心強い機能です。

スクリーンショット 2016-12-01 14.16.33.png

管理画面の Debugging & Development タブで有効化しておけば機能を利用できます。

以上、OMEGA5テーマを利用する上での最低限のポイントの紹介でした。


最後に

D8のテーマ周りを簡単に紹介しましたが、如何でしたでしょうか。

この辺の内容ならD7を利用していた方なら比較的スムーズに移行できるかと思います。

これらに加えてPanelsモジュールDisplay Suiteモジュール を導入すればさらに高度にテーマ開発が可能です。

ブロックもエンティティ化されたことでフィールド追加できるようになり、D7のときのイマイチ感が払拭されました。一度定義したブロックを何度も使い回しできるようになのは便利だなと感じました。

あとは、もっと複雑なシステムを構築しようとすればモジュール開発が必要ですが、D8からはオブジェクト指向になっていたり、ネームスペースってなんだ!?みないな話になると思いますが、その辺はまた次の機会に書きたいと思います。