Twig拡張関数を作ってデザインをカスタマイズしてみよう

EC-CUBE Advent Calendar 2016 の17日目です。

# Twig拡張関数を作ってデザインをカスタマイズしてみよう

EC-CUBE 3.0ではテンプレートエンジンにTwigを採用しています。
今回はTwig拡張関数を作成して、Twigコードの中で利用する方法をご紹介します。

## この記事のゴール

Topページに商品名と在庫を表示させます。
![image](https://qiita-image-store.s3.amazonaws.com/0/113212/0564a046-cb9a-f687-5d69-1b892282267b.png)

## Twig拡張関数を使わない場合

ページにはパラメータとして`app`が渡ってきているので、下記のように書けば商品情報にアクセスすることが可能です。ただし、Viweが他の実装に依存してしまいますし、可読性も良くないですね。

```twig:twig
{% set Product = app['eccube.repository.product'].get(2) %}

{{ Product.Name }}：残りあと{{ Product.getStockMax }}個！
```

## Twig拡張関数を使う場合
### 最終的な実装の例
`TwigExtensionSample_getProduct`というProductEntityを返すTwig拡張関数を作ることで、下記のような実装ができるようになります。

- この関数は、引数で商品IDを渡せばProductEntityが返ってきます。
- ページに関係なく利用することができます。

```twig:twig
{% set Product = TwigExtensionSample_getProduct(２) %}

{{ Product.Name }}：残りあと{{ Product.getStockMax }}個！
```

（getProductみたいな関数名でもいいのですが、関数名が重複するトラブルを避けるために関数名にプラグインコードを付けています）

### 実装方法
まずはこちらから雛形となるプラグインをダウンロードし、EC-CUBEにインストール/有効化してください。（有効にするのを忘れないでください）
https://github.com/ryo-endo/eccube3-twig-extension-sample-plugin/releases/tag/1.0.0
[TwigExtensionSamplePlugin-blank.tar.gz]

`/app/Plugin/TwigExtensionSample/ServiceProvider/TwigExtensionSampleServiceProvider.php`の`register`関数に次のコードを追加することで、Twigコード内で`TwigExtensionSample_getProduct`関数が利用できるようになります。

```php:TwigExtensionSampleServiceProvider.php
public function register(BaseApplication $app)
{
    $app['twig'] = $app->share($app->extend('twig', function (\Twig_Environment $twig, \Silex\Application $app) {
        // 商品を取得するTwig拡張関数
        $twig->addFunction(new \Twig_SimpleFunction('TwigExtensionSample_getProduct', function ($id) use ($app) {
            return $app['eccube.repository.product']->get($id);
        }));

        return $twig;
    }));
}
```

あとは、EC-CUBEの`管理画面>コンテンツ管理>ページ管理>TOPページ>ページ編集`からTOPページのtwigファイルに以下のコードを追加すれば、この記事の最初にあるキャプチャのように表示されるはずです。

```twig:TOPページ
{% block main %}
    {#--ここから--#}
    <div class="row">
        <div class="col-sm-12">
            {% set Product = app['eccube.repository.product'].get(2) %}
            {{ Product.Name }}：残りあと{{ Product.getStockMax }}個！
        </div>
    </div>
    {#--ここまで--#}
```

完成したプラグインはこちらにアップしています。
https://github.com/ryo-endo/eccube3-twig-extension-sample-plugin/releases/tag/1.0.0
[TwigExtensionSamplePlugin-1.0.0.tar.gz]

### 簡単な解説
- `$twig->addFunction`に`Twig_SimpleFunction`のインスタンスを渡すことで、新しい拡張関数を登録しています。
- １つめの引数は、作成する拡張関数の名前です。
- ２つめの引数は、拡張関数が呼び出されたときの処理(無名関数)です。

### 拡張関数を別のクラスに分ける場合
上記のように無名関数で登録していくのが簡単ですが、クラスに分けることも可能です。
以下にサンプルコードを用意しましたので参考にしてください。

拡張関数を`Twig/Extension/ProductExtension.php`に分割。
ServiceProviderではクラスの登録だけをしています。
https://github.com/ryo-endo/eccube3-twig-extension-sample-plugin/tree/use_class_file

## ご注意
プラグインを削除後、Twigファイル内に拡張関数が残っていると「不明な関数の呼び出し」としてTwigコードのコンパイルがエラーになりますのでご注意ください。

>Twig_Error_Syntax in ExpressionParser.php line 574:
>Unknown "TwigExtensionSample_getProduct" function in 
>"__string_template__29dc7e2fb366c012654f4e32f1588c5ddaa81f6708677d059e47634bf7f5342a" at line 43.

## 最後に
Twig拡張関数を作り、それを使ってデザインをカスタマイズする方法をご紹介しました。
ある程度、汎用的に使える拡張関数を作っておけば、フロントの担当者の方もデザインをカスタマイズしやすいんじゃなかろうかと思います。

今回のサンプルプラグインをベースにしてもらえれば、`$twig->addFunction`に追加するだけで簡単に作れますので、ぜひ試してみてください。

さて明日は @chihiro-adachi による、人気シリーズ「PhpStormでSilexのコード補完(３)」Eです。