[Drupal] Viewsの外部設置フィルターをスタイリングする方法

1年半ぶりくらいにフロントエンドやったら死にかけたのでメモ。

## Viewsの外部設置フィルターってなんや
これです。
![外部設置フィルター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253501/bbf3250f-05ea-9bf3-6809-56a9be499759.png)

## テンプレートを変えずにクラスやIDを追加する
外部設置フィルターのスタイルを変えるお手軽な方法としては、`hook_form_alter`でCSSのクラスやIDを追加するというものがあります。現状ではフォームの入力部分とラベルのクラスとID、ラッパーを編集することができます。

```php
function MY_MODULE_form_views_exposed_form_alter(&$form, &$form_state, $form_id) {
  $view = $form_state->getStorage('view');
  if ($view['view']->id() === 'MY_VIEW') {

    // フォームの入力部分に属性を追加.
    $form['field_a']['#attributes']['class'][] = 'my-class';
    $form['field_a']['#attributes']['id'][] = 'my-id';

    // ラベルに属性を追加.
    $form['field_a']['#label_attributes']['class'][] = 'my-label-class';
    $form['field_a']['#label_attributes']['id'][] = 'my-label-id';

    // ラッパーを追加.
    $form['field_a']['#prefix'] = '<div class="my-wrapper-class">';
    $form['field_a']['#suffix'] = '</div>';
  }
}
```

## テンプレートを編集する
HTMLの構造を根本的に変える必要がある場合は、テンプレートを編集します。外部設置フィルターの表示には`views-exposed-form.html.twig`という既存のテンプレートが使われているので、これを上書きします。

（ベーステーマがclassyの場合`core/themes/classy/templates/views/views-exposed-form.html.twig`)

こんなファイルです。

```php
{#
/**
 * @file
 * Theme override of a views exposed form.
 *
 * Available variables:
 * - form: A render element representing the form.
 *
 * @see template_preprocess_views_exposed_form()
 */
#}
{% if q is not empty %}
  {#
    This ensures that, if clean URLs are off, the 'q' is added first,
    as a hidden form element, so that it shows up first in the POST URL.
  #}
{{ q }}
{% endif %}
{{ form }}
```

これを`mytheme/template/views/views-exposed-form.html.twig`にコピーして編集します。個々のフォーム要素は`{{ form.FIELD_NAME }}`で取得できます。（これを知らなくてめちゃくちゃハマった）

```php
{#
/**
 * @file
 * Theme override of a views exposed form.
 *
 * Available variables:
 * - form: A render element representing the form.
 *
 * @see template_preprocess_views_exposed_form()
 */
#}
{% if q is not empty %}
  {#
    This ensures that, if clean URLs are off, the 'q' is added first,
    as a hidden form element, so that it shows up first in the POST URL.
  #}
{{ q }}
{% endif %}
<table>
  <tr>{{ form.field_a }}</tr>
  <tr>{{ form.field_b }}</tr>
</table>
```

### 特定のビューやディスプレイにだけ適用したい場合
上記のファイル名だと全ての外部設置フォームに適用されるので、特定の外部設置フォームにのみ適用したい場合は、テンプレート名を変えます。適当に命名はNGで、`form`の`#theme`要素で設定されているものに変える必要があります。

テンプレートの中で`form`をデバッグするには、Develモジュールをインストールして

```php
{{ dpm(form) }}
```
や

```php
{{ kint(form) }}
```
と書くとブラウザで外部設置フォームを閲覧した際に`form`の中身が表示されます。

`form`の直下の`#theme`に

```
 [#theme] => Array
        (
            [0] => views_exposed_form__MY_VIEW__page_1
            [1] => views_exposed_form__page_1
            [2] => views_exposed_form__MY_VIEW__page
            [3] => views_exposed_form__page
            [4] => views_exposed_form__MY_VIEW
            [5] => views_exposed_form
        )
```

のような感じで使えるテンプレート名がリストアップされているので、この中からちょうどいい粒度のものを使用します。（実際のファイル名は「`_`」を「`-`」にする必要があるので注意!）
例えばテンプレートのファイル名を`views-exposed-form--MY_VIEW--page-1`にすると、「`MY_VIEW`というマシン名を持つビューの`page_1`のディスプレイ」を閲覧したときのみ、そのテンプレートが適用されます。

### `#theme`に定義されていないテンプレート名を使いたいとき
権限によってテンプレートを変えるなど(?)`#theme`に定義されているテンプレートの粒度では間に合わない！という場合は`hook_theme_suggestions_HOOK_alter`でテンプレート名を追加することもできます。

```php
/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function MYTHEME_theme_suggestions_views_exposed_form_alter(&$suggestions, &$vars, $hook) {
  //特定の条件下でviews-exposed-form--custom.html.twigを読み込むようにする
  if (条件) {
    $suggestions[] = 'views_exposed_form__custom'; //「-」は全て「_」に変換する点に注意
  }
}
```