Drupal8 入門の入門（Viewsモジュール）

> WordPress他のメジャーなCMSを利用した経験のない人間が、Drupalでどうにかしなければならなくなった(ている)時のメモです。（遺言として書き残します。）

# **Viewsモジュールについて**

これを一通り読めばざっくりと概要はつかめると思われる。

- [DRUPAL VIEWSモジュール](http://www.act-brain.co.jp/drupal-views-magic)

公式Document

- [Working with Views in Drupal 8](https://www.drupal.org/node/1912118)

## **設定**
> ホーム ＞ 管理 ＞ サイト構築　＞ Views の「設定」タブ

### 基本
- /admin/structure/views/settings
- ライブプレビューでSQLクエリの表示などの設定(これ便利！)

### 高度
- /admin/structure/views/settings/advanced
- Viewのキャッシュを一括で無効化できるがサイトのパフォーマンスに影響があるので注意
- ビューのキャッシュのみクリアする機能がここにある

## **Viewsモジュールでできること(ざっくり)**
- 基本はノードの抽出条件と表示内容を細かく定義し、その結果をPageまたはBlock、Feedなどに適用する
- ノードでなくてもタクソノミーターム（＝カテゴリリスト）を抽出することも可能

- 一覧のフォーマット、表示するフィールド、抽出条件、ソート条件、ページャー
- 各フィールドの表示フォーマットもカスタマイズ可能（タグを変更するなど）
- リスト自体（```<li>```）にも個々のフィールド自体にもCSSクラスを設定可能
- 画像はオリジナル画像の他、画像スタイルで作成したものを指定可能
- 集約系のクエリも作れるっぽい（未検証）

**(。。。やれることが多すぎて把握しきれない)**

## **Viewのキャッシュについて**

- [DRUPAL VIEWSキャッシュを徹底的に活用する](http://www.act-brain.co.jp/howto/performance-views)
↑で説明されているモジュールは現在はDrupal 8 coreに含まれている？
> https://www.drupal.org/project/views_content_cache

- 設定からすべてのViewをキャッシュしないように設定することも可能だが、個々のViewに対して細かくキャッシュの設定をすることが可能
- キャッシュの種類は「なし、タグベース、時間」

### **時間ベース**
クエリーキャッシュ時間とレンダリング結果のキャッシュ時間をそれぞれ設定可能

![screenshot.1462434468.png](https://qiita-image-store.s3.amazonaws.com/0/35665/c60fbaab-08fc-d3f1-078a-cefdafc8329a.png)


### **タグベース**
> 下記URL(カスタムキャッシュタグのモジュール)の説明によると、『nodeリストのviewはnode_listというタグで管理されており、nodeの追加・更新・削除でキャッシュが無効化される』（と書いているような気がする）
https://www.drupal.org/project/views_custom_cache_tag

> 実際にcachetagsテーブルには「node_list」というタグが登録されており、nodeやuserなどはID付きのタグ(例えば"node:6")が登録されているっぽい

> ということは、統計でアクセス数をカウントして順位の入れ替えが発生しても、nodeが追加・更新・削除されない限り、viewのキャッシュはクリアされない？

```
CREATE TABLE `cachetags` (
  `tag` varchar(255) CHARACTER SET ascii NOT NULL DEFAULT '' COMMENT 'Namespace-prefixed tag string.',
  `invalidations` int(11) NOT NULL DEFAULT '0' COMMENT 'Number incremented when the tag is invalidated.',
  PRIMARY KEY (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Cache table for tracking cache tag invalidations.'
```

**実験 - 1**
1 - サイトを本番モードに切り替え
2 - リストのViewページにアクセス(キャッシュは「タグベース」)
3 - 管理機能からnodeを更新（コンテンツを編集）

> cachetagsテーブルで該当するnode:nのinvalidationsカウンタとnode_listのカウンタがカウントアップされたことを確認

4 - リストのViewページをリロード

> Viewページが更新されたことを確認

**実験 - 2**
1 - サイトを本番モードに切り替え
2 - リストのViewページにアクセス(キャッシュは「時間」)
3 - 管理機能からnodeを更新（コンテンツを編集）

> cachetagsテーブルで該当するnode:nのinvalidationsカウンタとnode_listのカウンタがカウントアップされたことを確認

4 - リストのViewページをリロード

> Viewページが更新されたことを確認

**「時間」については謎だが、基本は「タグベース」でよいのではないかと思われる。ただしnodeへのアクセスをカウントしていても、nodeが更新されない限りはviewのキャッシュはクリアされない**

## **Views TIPS**

### TIPS.複数の値を持つフィールドの1つ目だけ表示する

1. Viewの編集画面で、該当のフィールドの設定画面を開く
2. 「複数フィールドの設定」を表示し以下のように設定する
 -  「すべての値を1行で表示」にチェック
 -  表示形式：「単純なセパレータ」
 - セパレータ：空文字
 - 「1」個の値を表示「0」から開始

![screenshot.1462690719.png](https://qiita-image-store.s3.amazonaws.com/0/35665/9e90aa0c-05c3-fbca-96c1-10fa858c0371.png)

## **色々なViews例**

### タクソノミータームを利用したView

- 表示するフィールドやフォーマット、フィルター条件などを設定する
- パスを設定する（例：articles/category/%category_id）
- 高度の「前後関係からみたフィルター」に「コンテンツ：タクソノミータームIDを含む」を追加
 - 検証の条件に「タクソノミータームID」を指定、その他結果がなかった場合の挙動など細かく設定可能

- デフォルトで用意されているViewではパスは「taxonomy/term/%taxonomy_term」となっている
- 複数のタームが設定されているNodeにも対応している

![screenshot.1463294154.png](https://qiita-image-store.s3.amazonaws.com/0/35665/42e38900-2b83-2d89-5ba2-08e4aa749b6e.png)

コンテクスチュアルフィルターで表示されるクエリー
> パス：articles/category/3

```:sql
SELECT
  node_field_data.created AS node_field_data_created,
  node_field_data.nid AS nid
FROM
  {node_field_data} node_field_data
LEFT JOIN
  {taxonomy_index} taxonomy_index_value_0
  ON node_field_data.nid = taxonomy_index_value_0.nid AND taxonomy_index_value_0.tid = '3'
WHERE
(
  (( (taxonomy_index_value_0.tid = '3') ))
  AND (
    ( (node_field_data.status = '1')
      AND (node_field_data.type IN  ('article'))
    )
  )
)
ORDER BY node_field_data_created DESC
LIMIT 11 OFFSET 0
```

### タクソノミーターム自体を抽出するView(ブロック)

- フィールドに「タクソノミーターム：タームID」を追加し、非表示にする
- フィールドに「タクソノミーターム：名前」を追加し、詳細設定をする

![screenshot.1463295779.png](https://qiita-image-store.s3.amazonaws.com/0/35665/8fc11ac4-e560-da25-c4bc-474a230b1c76.png)

> エンティティへのリンクにチェックを入れた場合、名前がリンクとなって表示されるが、URLはデフォルトの「「taxonomy/term/%taxonomy_term」」となる。
>
> もしパスを変更したい場合は、「結果の書き換え」の「このフィールドを指定のリンクとして出力」にチェックを入れ、リンクパスを設定する。
>
> リンクパスには非表示フィールドとして追加した「タクソノミーターム：名前」を置き換え文字として指定できる
>（例：/articles/category/{{ tid }}）

![screenshot.1463295240.png](https://qiita-image-store.s3.amazonaws.com/0/35665/dcd1b27d-a96c-286c-b68a-83cf32b20fe4.png)

![screenshot.1463295124.png](https://qiita-image-store.s3.amazonaws.com/0/35665/68c44953-58fa-1cd0-02e4-74978d65dbeb.png)


- フィルタの条件に対象のボキャブラリー(vid)を指定する（例では「タクソノミーターム: ボキャブラリー (= タグ)」）
- 必要に応じて、並べ替えの基準を設定する（例ではタクソノミーターム：ウェイト（昇順））
※ボキャブラリー、ウェイトなどは「サイト構築＞タクソノミー」で管理している


コンテクスチュアルフィルターで表示されるクエリー
> WHERE句のtaxonomy_term_field_data.vidはボキャブラリーID
> Viewの編集画面上は『タクソノミーターム: ボキャブラリー (= タグ)』と日本語訳されてわかりにくい。

```:sql
SELECT 
  taxonomy_term_field_data.weight AS taxonomy_term_field_data_weight, 
  taxonomy_term_field_data.tid AS tid
FROM 
  {taxonomy_term_field_data} taxonomy_term_field_data
WHERE 
(
  ( (taxonomy_term_field_data.vid IN  ('tags')) )
)
ORDER BY taxonomy_term_field_data_weight ASC
```

### ノードID以外の情報をパラメータとし利用したView

例：投稿者のIDをパラメータとして渡す

- 表示するフィールドやフォーマット、フィルター条件などを設定する
- パス
- 高度の「前後関係からみたフィルター」に「コンテンツ：投稿者」を追加
 - 検証の条件に「ユーザーID」を指定、その他結果がなかった場合の挙動など細かく設定可能
 - リレーションシップに投稿者を追加（いらないかも？）
 - 検証の条件を設定しておくと、パラメータに存在しないuidや不正な値を与えてもちゃんとバリデーションしてくれているらしい(クエリが実行されない)

![screenshot.1463289198.png](https://qiita-image-store.s3.amazonaws.com/0/35665/9a99ea6e-0893-7ca4-5935-03fd3bea06bc.png)

- URLのパラメータに名前を付けることも可能
- パラメータ名を付けると以下のように取得できる

> $author_id = \Drupal::routeMatch()->getParameter('author_id');

![screenshot.1463289113.png](https://qiita-image-store.s3.amazonaws.com/0/35665/f83fc5b9-e44b-ab5b-40ca-ddc644ff70fd.png)

コンテクスチュアルフィルターで表示されるクエリー
> パス：/articles/author/5

```:sql
SELECT
  node_field_data.created AS node_field_data_created,
  node_field_data.nid AS nid,
  users_field_data_node_field_data.uid AS users_field_data_node_field_data_uid
FROM
  {node_field_data} node_field_data
LEFT JOIN
  {users_field_data} users_field_data_node_field_data ON node_field_data.uid = users_field_data_node_field_data.uid
WHERE
(
  ( (node_field_data.uid = '5' ) )
  AND (
    ( (node_field_data.status = '1')
      AND (node_field_data.type IN  ('article'))
    )
  )
)
ORDER BY node_field_data_created DESC
LIMIT 11 OFFSET 0
```

### 表示しているノードと同じ著者のノードリストを表示するView（Block）
参考：[Exclude the current node from a list view](https://www.drupal.org/node/131547)

- 前後関係からみたフィルターに「コンテンツ：投稿者」追加し、下記のようにデフォルト値を設定する。

![screenshot.1465729979.png](https://qiita-image-store.s3.amazonaws.com/0/35665/b50dbbee-0934-8fbb-80ed-19c69b0386f4.png)


- さらに表示しているノードを除外する場合は前後関係からみたフィルターに「コンテンツ：投稿者」も追加し、下記のように設定する。（「もっとみる」の部分の「除外」がポイント）

![screenshot.1465730067.png](https://qiita-image-store.s3.amazonaws.com/0/35665/c9ba0cd4-e608-c48c-c2cf-879ce6e89241.png)
![screenshot.1465730079.png](https://qiita-image-store.s3.amazonaws.com/0/35665/832a2352-ac80-b0d0-5902-dc17f3ffd374.png)

コンテクスチュアルフィルターで表示されるクエリー例

```:sql
SELECT node_field_data.created AS node_field_data_created, 
  node_field_data.nid AS nid, 
  users_field_data_node_field_data.uid AS users_field_data_node_field_data_uid
FROM 
  {node_field_data} node_field_data
  INNER JOIN {users_field_data} users_field_data_node_field_data 
  ON node_field_data.uid = users_field_data_node_field_data.uid
WHERE 
  (
    (
      (node_field_data.uid = '1' ) AND (node_field_data.nid != '2' OR node_field_data.nid IS NULL) 
    )
    AND
    (
        ( (node_field_data.status = '1') AND (node_field_data.type IN  ('article')) )
    )
  )
ORDER BY node_field_data_created DESC
LIMIT 5 OFFSET 0
```

### 表示しているノードと同じタクソノミータームのノードリストを表示するView（Block）

- 前後関係からみたフィルターに「コンテンツ：カテゴリ」追加し、下記のようにデフォルト値を設定する。（カスタムフィールド「カテゴリ」にタクソノミータームを設定している場合の例）

![screenshot.1465730819.png](https://qiita-image-store.s3.amazonaws.com/0/35665/fdb4c445-3fc4-b271-2853-adaacbf7f6d5.png)

※表示しているノードを除外する場合は前述と同じ

コンテクスチュアルフィルターで表示されるクエリー例

```:sql
SELECT 
  node_field_data.created AS node_field_data_created, 
  node_field_data.nid AS nid, 
  taxonomy_term_field_data_node__field_main_category.tid AS taxonomy_term_field_data_node__field_main_category_tid
FROM 
  {node_field_data} node_field_data
LEFT JOIN 
  {node__field_main_category} node__field_main_category 
  ON node_field_data.nid = node__field_main_category.entity_id 
  AND (node__field_main_category.deleted = '0' 
  AND node__field_main_category.langcode = node_field_data.langcode)
INNER JOIN {taxonomy_term_field_data} taxonomy_term_field_data_node__field_main_category 
  ON node__field_main_category.field_main_category_target_id = taxonomy_term_field_data_node__field_main_category.tid
WHERE (
  ( (node__field_main_category.field_main_category_target_id = '1' ) 
    AND 
    (node_field_data.nid != '2' OR node_field_data.nid IS NULL) 
  )
  AND(
    ( (node_field_data.status = '1') AND (node_field_data.type IN  ('article')) )
  )    
)
ORDER BY node_field_data_created DESC
LIMIT 5 OFFSET 0
```
期待する結果は得られるが、正しいのかは微妙
> node_field_data.nid IS NULL

って正しいのか？