EC-CUBE4でタグを埋めたい！

EC-CUBE4でタグを埋めたいときの方法をいくつか紹介します。
方法ごとにメリット、デメリットがあるので要件に合う方法を選択しましょう。

目次

- `default_frame.twig` に追加する
  - `default_frame.twig` に直接追加する
  - `app/template/` ディレクトリを利用して `default_frame.twig` をカスタマイズする
- ブロック管理でタグを追加する
  - 画面からブロックを追加
  - プラグインでブロックを追加
- TemplateEventでタグを追加する

# `default_frame.twig` に追加する

`default_frame.twig` はEC-CUBEで全体のレイアウトを定義しているテンプレートファイルです。
このファイルに `<head>` タグや `<body>` タグ等が定義されています。

- メリット
  - 早い
  - 手軽に全ページにタグが追加できる
- デメリット
  - 管理画面から確認変更不可
  - 変更履歴管理が必要

## `default_frame.twig` に直接追加する

フロント画面の `default_frame.twig` ファイルは以下に配置されています。
`src/Eccube/Resource/template/{ECCUBE_TEMPLATE_CODE}/default_frame.twig` [^1]
上記ファイルを編集してタグを追加すればフロントの全画面[^2]に追加されます。

```shell
vi src/Eccube/Resource/template/{ECCUBE_TEMPLATE_CODE}/default_frame.twig
```

反映されない場合は管理画面からキャッシュを消してみましょう。

## `app/template/` ディレクトリを利用して `default_frame.twig` をカスタマイズする

`app/template/` 配下に同名のテンプレートファイルを配置すると `src/Eccube/Resource/template/` より優先的に読み込まれます。

```shell
cp src/Eccube/Resource/template/{ECCUBE_TEMPLATE_CODE}/default_frame.twig app/template/{ECCUBE_TEMPLATE_CODE}/
```

コピーしたファイルを編集してタグを追加すればフロントの全画面[^2]に追加されます。

```shell
vi app/template/{ECCUBE_TEMPLATE_CODE}/default_frame.twig
```

反映されない場合は管理画面からキャッシュを消してみましょう。

# ブロック管理でタグを追加する

EC-CUBEにはブロック管理の機能があります。

- メリット
  - 管理画面からも追加可能
- デメリット
  - レイアウト単位でのタグ追加となる（ページを指定してタグ追加が難しい）
  - レイアウトを追加変更する際はタグのブロックのレイアウトも気にする必要がある
  - テーマを変更する際も同様に気にする必要がある

## 画面からブロックを追加

1. `管理画面 -> コンテンツ管理 -> ブロック管理` からタグのブロックを作成
2. `管理画面 -> コンテンツ管理 -> レイアウト管理` から任意のレイアウトにブロックを追加

## プラグインでブロックを追加

プラグインでもブロックの作成とレイアウトへの追加ができます。
[おすすめ商品管理プラグイン](https://github.com/EC-CUBE/Recommend-plugin)で同様の処理をしているので内容を確認してみましょう。

プラグインのインストール/有効化/無効化/削除時の処理は `PluginManager.php` というファイルに記述します。
プラグインを有効化した際には `PluginManager.php` の `enable()` 関数が実行されます。
画面からブロックを作成とレイアウトに追加する際には以下のような処理が行われておりますのでそれぞれ実装してやります。

1. app/template配下へブロックのtwigファイルを配置
2. ブロックの情報をDBに登録
3. ブロックのレイアウトへの配置情報をDBに登録

冗長なほどコメントを入れましたので頑張って理解してください。

```php:PluginManager.php
// プラグインのインストール時に実行されます
public function enable(array $meta = null, ContainerInterface $container)
{
    // 1. app/template配下へブロックのtwigファイルを配置
    $this->copyBlock($container);
    // ブロックのデータがすでに作成されている場合は新しいブロックを作りません。
    $Block = $container->get(BlockRepository::class)->findOneBy(['file_name' => $this->blockFileName]);
    if (is_null($Block)) {
        // 2. ブロックの情報をDBに登録、3. ブロックのレイアウトへの配置情報をDBに登録
        $this->createDataBlock($container);
    }
}

private function createDataBlock(ContainerInterface $container)
{
    // DBの操作をするときはentityManagerを利用します
    $em = $container->get('doctrine.orm.entity_manager');
    // DeviceTypeはPCにしておきます。（EC-CUBEのデフォルトでBlockのDeviceTypeは利用されていない）
    $DeviceType = $container->get(DeviceTypeRepository::class)->find(DeviceType::DEVICE_TYPE_PC);
    try {
        // 2. ブロックの情報をDBに登録
        // BlockのEntityを作成します。
        $Block = $container->get(BlockRepository::class)->newBlock($DeviceType);
        // Blockの各パラメータを設定してからDBに登録します。
        $Block->setName($this->blockName) // ブロック名
            ->setFileName($this->blockFileName) // templateファイルのファイル名
            ->setUseController(false) // ブロックにControllerを設定するか。trueにするとこのブロックが呼び出されたときに実行されるControllerを定義できる。
            ->setDeletable(false); // 管理画面から削除できるか。管理画面から削除する必要はないのでfalseを指定。
        $em->persist($Block); // Entityを永続化する。新しく作成したEntityはpersist()しないとDBに保存できない。
        $em->flush($Block); // EntityをDBに保存する。

        // 3. ブロックのレイアウトへの配置情報をDBに登録
        // BlockがどのLayoutのどの位置に配置されているかの情報はBlockPositionで定義されている
        // すでにブロックがレイアウトに配置されている場合は処理を終了
        $blockPos = $container->get(BlockPositionRepository::class)->findOneBy(['Block' => $Block]);
        if ($blockPos) {
            return;
        }
        // ブロックの順序はblock_rowで定義されている
        // ブロックを配置したいところにすでにブロックが登録されている場合には、それを考慮してblock_rowを設定する必要がある
        // ブロックを配置したいレイアウトの位置のブロックの一番下のブロックを取得する
        $blockPos = $container->get(BlockPositionRepository::class)->findOneBy(
            ['section' => Layout::TARGET_ID_MAIN_BOTTOM, 'layout_id' => Layout::DEFAULT_LAYOUT_UNDERLAYER_PAGE],
            ['block_row' => 'DESC']
        );
        // BlockPositionの作成
        $BlockPosition = new BlockPosition();
        // すでに配置されているブロックをもとにblock_rowを設定する
        $BlockPosition->setBlockRow(1);
        if ($blockPos) {
            // ブロックがすでに存在する場合はその一番下のブロックのさらに下にブロックを配置する
            $blockRow = $blockPos->getBlockRow() + 1;
            $BlockPosition->setBlockRow($blockRow);
        }
        // ブロックを配置するレイアウトを取得。ここでは「下層ページ用レイアウト」を指定している。
        $LayoutDefault = $container->get(LayoutRepository::class)->find(Layout::DEFAULT_LAYOUT_UNDERLAYER_PAGE);
        // $BlockPositionのパラメータを設定する
        $BlockPosition->setLayout($LayoutDefault) // レイアウト
            ->setLayoutId($LayoutDefault->getId()) // レイアウトのID
            ->setSection(Layout::TARGET_ID_MAIN_BOTTOM) // 配置位置。ここでは#main_bottomを指定
            ->setBlock($Block) // ブロック
            ->setBlockId($Block->getId()); // ブロックのID
        $em->persist($BlockPosition); // Entityを永続化する。新しく作成したEntityはpersist()しないとDBに保存できない。
        $em->flush($BlockPosition); // EntityをDBに保存する。
    } catch (\Exception $e) {
        throw $e;
    }
}

private function copyBlock(ContainerInterface $container)
{
    // 現在のテーマのテンプレートにファイルを配置するため現在のテーマのディレクトリを取得します。
    $templateDir = $container->getParameter('eccube_theme_front_dir');
    // コピー先にファイルがない場合のみファイルをコピーします
    $file = new Filesystem();
    if (!$file->exists($templateDir.'/Block/'.$this->blockFileName.'.twig')) {
        // app/template配下へブロックのtwigファイルを配置
        $file->copy($this->originBlock, $templateDir.'/Block/'.$this->blockFileName.'.twig');
    }
}
```

ソースは[こちら](https://github.com/EC-CUBE/Recommend-plugin/blob/4.0/PluginManager.php)

タグのテンプレートファイルを用意しておく必要がありますので `Resource/template/Block/` 配下にtwigファイルを作成しておきましょう。
おすすめ商品管理プラグインの場合は[こちら](https://github.com/EC-CUBE/Recommend-plugin/blob/4.0/Resource/template/Block/recommend_product_block.twig)

説明は割愛しますが、プラグインでブロックを作成される場合には無効化/アンインストール時にブロックを削除する処理も実装しましょう。

# TemplateEventでタグを追加する

プラグインやカスタマイズディレクトリでTemplateEventを追加してタグを埋められます。

- メリット
  - ページを指定してのタグ追加がシンプルに行える
- デメリット
  - 複数ページへタグ追加する際はその数だけイベントを設定する必要がある
  - 他のプラグイン等で新規追加したページにタグを埋めたいときはイベントの追加が必要

## 手順

`EventSubscriberInterface` を実装したクラスを作成します。
[メーカー管理プラグイン](https://github.com/EC-CUBE/maker-plugin)に実装例があるので確認してみましょう。

```php
// EventSubscriberInterfaceを実装したクラスを作成
class MakerEvent implements EventSubscriberInterface
{
    // getSubscribedEvents関数でフックするテンプレートを指定する
    // 戻り値に配列を指定することで複数のページを指定可能
    // この例では商品詳細画面のtwigを指定している
    public static function getSubscribedEvents()
    {
        // 形式は '{フックしたいテンプレートファイル}' => ['{イベント発火時に実行する関数}', {イベントの優先順位(省略可)}]
        return [
            'Product/detail.twig' => ['onTemplateProductDetail', 10],
        ];
    }

    // TemplateEventを引数にとった関数を作成する
    public function onTemplateProductDetail(TemplateEvent $templateEvent)
    {
        // addSnippet()関数で追加したいテンプレートファイルを指定する。
        $templateEvent->addSnippet('@Maker4/default/maker.twig');
    }
}
```

ソースは[こちら](https://github.com/EC-CUBE/maker-plugin/blob/4.0/MakerEvent.php)

`addSnippet()` 関数の他に `addAsset()` 関数も利用可能。

`addSnippet()` : `<body>`タグ内の下の方にテンプレートを追加できる
`addAsset()` : `<head>`タグ内にテンプレートを追加できる

追加するタグのテンプレートファイルを作成しておく必要があります。
メーカー管理プラグインの場合は[ここ](https://github.com/EC-CUBE/maker-plugin/blob/4.0/Resource/template/default/maker.twig)に定義しています。

テンプレートへパラメータを渡す方法は[こちら](https://qiita.com/okazy/items/56d24c50c341f6edad36#templateevent%E3%81%A7%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%82%92%E3%82%BB%E3%83%83%E3%83%88%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)の記事に記載しているのでよかったらどうぞ。

# まとめ

タグの追加方法がいくつかあり、それぞれメリットデメリットがあります。
タグの要件に合わせて追加方法の選択が必要です。

例えばプラグインで実現したい場合は `default_frame.twig` へのタグの追加は難しいです。
また、特定のページだけにタグを配置したい場合はTemplateEventを利用するのが良いでしょう。


[^1]: デフォルトのテンプレートの場合は `ECCUBE_TEMPLATE_CODE=default`
[^2]: 追加した画面は実装による