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

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


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


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

![druplicon-small.png](https://qiita-image-store.s3.amazonaws.com/0/39468/24b10b8d-69f6-0eb1-7ba2-694d74514284.png)

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

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

![スクリーンショット 2016-12-01 3.47.55.png](https://qiita-image-store.s3.amazonaws.com/0/39468/889e5f72-22a6-c0da-0680-e34324e00489.png)


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



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

![drupal 8 logo isolated CMYK 72.png](https://qiita-image-store.s3.amazonaws.com/0/39468/cfa9f444-b392-84b9-e138-a7b01ec1f6e3.png)

D8ではHTML5がサポートされて、同梱テーマやモジュールがHTML5対応になってことが大きな変化です。
PHPTemplateエンジンに代わって、Twigがデフォルトエンジンになりました。これによってPHPTemplate エンジンは削除されています。


つまり、<font color="MediumBlue">* .tpl.php</font>ファイルのすべてが使用できなくなりました。
D8のテンプレートファイルは、新しいTwigの拡張子を利用します。

```
* .html.twig
```

##Twigとは

![symfony_black_02.png](https://qiita-image-store.s3.amazonaws.com/0/39468/0208bcd9-2637-3cf5-a123-7fa7584cfbc7.png)


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

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


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

D7のphpコードが自由に書けたときと比べると自由度は落ちますが、シンプルで可読性が高く、Webデザイナーさんでも編集しやすいように設計されています。
これは、モデル、ビュー、コントローラという3つの処理に分割（※）されていることにより、テンプレートの品質向上が保たれます。
※Twigはモデル、ビュー、コントローラ（MVCパターン）のビューの部分に当たります。

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


###Drupal7 + PHPTemplateエンジン

例）<font color="MediumBlue">views-view-unformatted.tpl.php</font> ファイルの中身

```html+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エンジン

例）<font color="MediumBlue">views-view-unformatted.html.twig </font>ファイルの中身

```text: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 の基本的な構文です。

* <font color="MediumBlue">{{ 〜〜 }}</font> : 変数や計算式の結果を出力します。
* <font color="MediumBlue">{% 〜〜 %}</font> :if文やfor文などのロジックを制御するタグです。
* <font color="MediumBlue">{# 〜〜 #}</font> : コメントを残します。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関数ではおなじみの <font color="MediumBlue">striptags</font> や <font color="MediumBlue">nl2br</font>、 <font color="MediumBlue">trim</font> などが用意されています。
その他、フィルターがいろいろ用意されていますので公式ドキュメントでご確認ください。

**Twig Filters - Documentation**
http://twig.sensiolabs.org/doc/filters/index.html


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

```
{{ dump(var) }}
```

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

```
{{ dump() }}
```

![スクリーンショット 2016-11-30 22.32.29.png](https://qiita-image-store.s3.amazonaws.com/0/39468/d3fe983b-e9d9-a4b0-52bd-f49e3fa636bc.png)


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

```
{{ kint(var) }}
```

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

```
{{ kint() }}
```

![スクリーンショット 2016-11-30 22.30.22.png](https://qiita-image-store.s3.amazonaws.com/0/39468/7e976a5b-0004-3bad-5552-57edb8cf3243.png)

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

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


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


###Twig debugging options

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

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

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

```php
$conf['theme_debug'] = 1;
```

一方、D8からは <font color="MediumBlue">sites/default/services.yml</font> ファイル上の <font color="MediumBlue">twig.config</font> セクションにて管理されるようになりました。
（もし <font color="MediumBlue">services.yml</font> がまだ設置されていない場合は <font color="MediumBlue">default.services.yml</font> をコピーして <font color="MediumBlue">services.yml</font> へリネームしてください）

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

```
debug: true    （デフォルト：false）
```

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


![スクリーンショット 2016-12-01 2.51.58.png](https://qiita-image-store.s3.amazonaws.com/0/39468/c7f1f9be-face-b633-fc1c-b5f20a2d20d3.png)

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

###Twig auto-reload

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

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

```
auto_reload: true  （デフォルト：null）
```

<font color="MediumVioletRed">true</font>にしておけば、テンプレートが変更されるたびに再コンパイルされます。
ここを<font color="MediumVioletRed">true</font>にしておかないと、Drupalキャッシュクリアするまでテンプレートの編集内容が確認できなくなってしまうので制作効率が下がってしまいます。


###Twig cache

```
cache: false  （デフォルト：true）
```

Twigテンプレートのコンパイル結果をディスクにキャッシュ保存することで高速化を行っています。
<font color="MediumVioletRed">false</font>を指定すればディスクへのキャッシュ保存を無効化できますが、通常は <font color="MediumBlue">**auto_reload: true**</font> だけでも足りるので基本的にはデフォルトの<font color="MediumVioletRed">true</font>のままにしておきましょう。


###Drupal render cache

D8は高速化のためにデフォルトでブロックとエンティティをキャッシュするので、それらの変更内容はキャッシュクリアするまでページ反映されません。
キャッシュを無効化するには <font color="MediumBlue">sites/default/settings.php</font> の次の2行を探してコメントアウトします。

```php
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コードは使えませんので、あらかじめフックを利用して変数を操作する必要があります。

フックを記述する場所はテーマフォルダ配下の <font color="MediumBlue">*.theme</font> になります。
拡張子がphpではありませんが問題なくPHPコードが書けますのでご安心ください。

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

* <font color="MediumBlue">templete_preprocess_breadcrumb(&$vars)</font>
* <font color="MediumBlue">templete_preprocess_block(&$vars)</font>
* <font color="MediumBlue">templete_preprocess_node(&$vars)</font>
* <font color="MediumBlue">templete_preprocess_field(&$vars)</font>


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

* <font color="MediumBlue">templete_preprocess_views_view_unformatted(&$vars)</font>
* <font color="MediumBlue">templete_preprocess_views_view_field(&$vars)</font>


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

なお、フックの先頭の <font color="MediumBlue">templete</font> の部分はテーマ名に置き換えてご利用ください。

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

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

**利用例：**

```php
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を動的にロードするときは <font color="MediumBlue">drupal_add_css()</font>, <font color="MediumBlue">drupal_add_js()</font> が利用できました。

####drupal_add_css関数

```php
$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関数

```php
$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 と仮定して説明していきます。

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

![スクリーンショット 2016-12-01 2.27.14.png](https://qiita-image-store.s3.amazonaws.com/0/39468/2cde4cf5-e459-4925-6b5c-2f8da231f7ff.png)


気をつける点は、ライブラリを記述するときのインデントが重要だということです。
ライブラリ名は一番左端にくるよう、タブやスペースが入らないようにしてください。
そのほか、css、jsセクションのインデントもしっかりしておく必要があります。

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

```yaml
    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ファイルを出力させたい場合は明示的に記述する必要があります。

```yaml
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](https://qiita-image-store.s3.amazonaws.com/0/39468/0f2e2b3d-d3f1-6244-9d0d-d344b0e88dab.png)


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


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

![スクリーンショット 2016-12-01 0.44.38.png](https://qiita-image-store.s3.amazonaws.com/0/39468/dcbf20ee-64ed-2c13-6a58-55f0bde6a1d4.png)


<font color="MediumBlue">libraries</font> セクションに追加されたライブラリはすべてのページでロードされます。
ただ、パフォーマンス向上のためにはやはり特定ページでのみロードされるように実装するべきかと思います。



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

D8の人気テーマとして **Bootstrap** や **Adminimal**、 **AdaptiveTheme** が人気です。
**Adminimal**、 **AdaptiveTheme** は導入した初期状態でそれなりのデザインなのでお手頃感がありますが、やはり、業務で利用するならなるべくベースがシンプルで汎用性の高い **OMEGA5** テーマを導入したいです。
（D7ではカスタマイズし易いテーマとして OMEGA と双璧を成す **Zen** テーマがありましたが、D8版はまだstable版がリリースされていないようなので検討からは外しました）


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

OMEGA5テーマはここからダウンロードできます。 
https://www.drupal.org/project/omega

ダウンロードしたテーマは <font color="MediumBlue">themes</font> フォルダ配下にコピーします。

![スクリーンショット 2016-12-01 1.59.08.png](https://qiita-image-store.s3.amazonaws.com/0/39468/792e9326-ea97-58f0-14cb-ae25f632957d.png)


そしてDrupal管理画面のテーマ一覧 /admin/appearance にアクセスして、
OMEGA5テーマの <font color="MediumBlue">install</font> リンクをクリックすればテーマを有効化できます。

![スクリーンショット 2016-12-01 2.01.42.png](https://qiita-image-store.s3.amazonaws.com/0/39468/a6db3537-5525-7e84-2d8e-fb0bda2a91af.png)


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

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


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

まず、<font color="MediumBlue">Create Subtheme</font> リンクをクリックします。

![スクリーンショット 2016-12-01 2.07.17.png](https://qiita-image-store.s3.amazonaws.com/0/39468/ad27910f-0f2f-fd9d-0cb6-635c68476d3b.png)


**Theme name** を入力します。
このとき、既存モジュールと同じ名前をテーマに付けると、テーマが読み込まれなくなるようなのでご注意ください。
プロジェクト名をそのままテーマ名に付けて、モジュールのとりあえずプロジェクト名を付けたものを設置するなんてことはよくやりがちです。D7ではそれでも支障なく動作していましたがD8ではダメみたいです。

**Description** と **Version** は任意の内容を入力しておきます。
**SUB-THEME OPTIONS** は Clone を選択します。
SCSSを利用する場合は、**Enable SCSS Customizations** にチェックを入れます。
最後に <font color="MediumBlue">Export Subtheme</font> ボタンをクリックしましょう。

![スクリーンショット 2016-12-01 2.11.13.png](https://qiita-image-store.s3.amazonaws.com/0/39468/ac559af0-2a82-2257-b7eb-e1d9a02e2796.png)


テーマ一覧の **Uninstalled themes** セクションに作成したサブテーマが追加されます。
<font color="MediumBlue">Install and set as default</font> リンクをクリックして有効化します。

有効化したら <font color="MediumBlue">Settings</font> リンクをクリックして設定画面を開きます。

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

![スクリーンショット 2016-12-01 2.16.37.png](https://qiita-image-store.s3.amazonaws.com/0/39468/7976a6ec-4882-747b-1ea2-f9cf03a333b9.png)


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

**Compile SCSS Directly** にチェックを入れた状態で <font color="MediumBlue">Save ＆ Update Styles</font> ボタンをクリックするとSCSSコンパイルが実行されます。
これは scssphp というコンパイラが組み込まれていて、わざわざSCSSコンパイル環境を構築しなくてもいいのでとくにWebデザイナーさんには優しい機能だと思います。

![スクリーンショット 2016-12-01 2.30.34.png](https://qiita-image-store.s3.amazonaws.com/0/39468/60fdae70-3971-9700-3e2e-953f3bf466ee.png)



**CSS/JS Libraries** タブでは example_theme.libraries.yml ファイルに記述しておいたライブラリが表示されます。
デフォルトでチェックが入った状態になっていますが、このままだとライブラリが常時ロードされてしまうようです。
<font color="MediumBlue">templete_preprocess_page</font>フックで動的にロードしたい場合はチェックを外しましょう。

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

![スクリーンショット 2016-12-01 2.46.20.png](https://qiita-image-store.s3.amazonaws.com/0/39468/d678abdc-dd0b-21be-b2e7-e24d02ccb2c4.png)



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

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


##OMEGA5サブテーマの公開画面

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

![スクリーンショット 2016-12-01 2.58.22.png](https://qiita-image-store.s3.amazonaws.com/0/39468/4ace4a24-a984-7a51-7f3a-e0d48667b645.png)

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

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

<img width="943" alt="スクリーンショット 2016-12-01 14.16.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/39468/ccee033e-916c-743e-ec5c-59918f734207.png">

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


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


#最後に

D8のテーマ周りを簡単に紹介しましたが、如何でしたでしょうか。
この辺の内容ならD7を利用していた方なら比較的スムーズに移行できるかと思います。

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

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

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