Configuration Management in Drupal8

#Configuration Managementとは

Drupal8のサイト構成を管理するための機能でCoreモジュールとして提供されています。

configテーブルの内容から、有効化されているモジュール一覧、コンテンツタイプ・タクソノミー、フィールドなどあらゆるサイト構成が対象です。

サイト構成はYAMLファイルで管理されており、Git管理下に置けば開発サイト→本番サイトへのデプロイも容易に行うことができます。

#Configuration Management 環境設定

##YAMLファイルの同期ディレクトリを設定する

settings.php の $config_directories['sync'] にて保存パスを指定すれば良いです。

デフォルトでは sites/default/files 以下にランダムなフォルダ名となっています。

```php
$config_directories['sync'] = 'sites/default/files/config_rsOV0YGdqQaPzrUtJC44K8SL628s1lwq1PdYm883mIUKuT8jcUvp-j6q8QLgu16CdP3e8iGlpA/sync';
```

sites/default/files フォルダは、管理画面などからアップロードしたたくさんのファイルが保存される場所なのでGit管理から外すことが多いですね。
そういうときは別のパスを指定しましょう。

```php
$config_directories['sync'] = 'sites/default/config/sync';
```

デフォルトだと 'sync' だが、それぞれ'dev'、'staging'、'prod'と分けて環境ごとにYAMLファイルを分けることで、安全に本番サイトを運用するという考え方もあるようです。

##サイトのUUIDを一致させる

設定内容をインポートするとき、クローンしたDBではないサイトの場合はUUIDが一致しませんという旨のメッセージが表示されてしまい、インポートができません。

```
The import failed due for the following reasons:                                                                                                          [error]
Site UUID in source storage does not match the target storage.
（ソースのストレージにあるサイトのUUIDが、対象のストレージに一致しません。）
```

その場合は、ターミナル上でdrushコマンドを使うことになりますが、次の手順でUUIDを一致させることができます。

まず、同期元サイトのUUIDを確認します。

```bash
$ drush config-get system.site
uuid: 86d5e75a-f927-4a9a-9739-9093888f4050
```

同期対象のサイトに同期元サイトのUUIDをセットします。

```bash
$ drush config-set system.site uuid 86d5e75a-f927-4a9a-9739-9093888f4050
```

これにてサイト構成が無事にインポートできるようになります。

#Configuration Management 管理画面

Configuration Management管理画面を開くにはこちらのパスへアクセスします。
/admin/config/development/configuration

##同期

![スクリーンショット 2017-01-21 18.44.19.png](https://qiita-image-store.s3.amazonaws.com/0/39468/721c6e67-2cfe-d7ba-30e7-eced4dcce61b.png)


同期タブではYAMLファイルとDBに保存されているサイト構成の差分を確認することができ、［すべてインポート］ボタンをクリックするとYAMLファイルの内容が取り込まれます。
差分がなければ画面にはなにも表示されます。

##インポート

![スクリーンショット 2017-01-21 19.07.50.png](https://qiita-image-store.s3.amazonaws.com/0/39468/73a544d1-03ee-8e5c-be14-bf48572aa497.png)


全体のサイト構成のインポートは、tar.gz形式で圧縮されたYAMLファイルを指定します。

![スクリーンショット 2017-01-21 20.40.20.png](https://qiita-image-store.s3.amazonaws.com/0/39468/dfdd30c2-27d0-0cf0-7493-296f18817496.png)


個別にサイト構成をインポートすることもできます。

##エクスポート

![スクリーンショット 2017-01-21 20.29.29.png](https://qiita-image-store.s3.amazonaws.com/0/39468/6d700621-1b39-ed1f-1e6a-55b541d6b792.png)


全体のサイト構成のエクスポートは、tar.gz形式で圧縮されたYAMLファイルを指定します。

![スクリーンショット 2017-01-21 20.37.46.png](https://qiita-image-store.s3.amazonaws.com/0/39468/ae07b537-5b18-921b-5b53-b3d4f6b4a1bc.png)


個別にサイト構成を取得することもできます。

#Drushコマンドを使ってサイト構成をインポート／エクスポートする

上で紹介した管理画面でもサイト構成の同期はできますが、開発者ならDrushコマンドを使う方が動作が軽快な上にGitコマンドを使ってのファイル差分が見やすかったりするのでこちらの操作がメインになると思います。

##エクスポート

サイト構成をエクスポートするには config-exportオプション（エイリアスはcex）を指定します。

config-exportオプションの後ろにはディレクトリラベルを指定します。
デフォルトでは「sync」になっていて settings.php の $config_directories でパス指定がされています。

```bash
$ drush cex sync
```

（Drushはデフォルトでsyncのパスを読み込んでくれるので、syncは省略することができます）

##インポート

サイト構成をインポートするには config-importオプション（エイリアスはcim）を指定します。

```bash
$ drush cim sync
```

#特定モジュールを同期対象から外す

開発時のデバッグ用モジュールの設定など本番環境に反映したくないものがあります。
特定モジュールの設定を除外するには --skip-modules パラメーターを利用します。

```bash
$ drush cim -y --skip-modules=devel,kint
$ drush cex -y --skip-modules=devel,kint
```

これにより、devel,kintモジュールが無効化された他の環境の影響を回避することができます。

ただ、モジュール周りの設定値は除外できても、関連するエンティティまでは除外されません。
develなどの開発モジュールはいいですが、エンティティが絡むモジュールの場合はインポート時に依存エラーになる可能性があるので注意する必要があります。

都度、--skip-modules パラメーターを指定するのが面倒なときは、drushrc.phpにあらかじめ追加しておくと良いでしょう。

```php
/**
   * @file
   * Drush configuration file drush/drushrc.php
   */
  $command_specific['config-export']['skip-modules'] = array('devel', 'kint');
  $command_specific['config-import']['skip-modules'] = array('devel', 'kint');
```

#サイト設定を編集する

config-editオプション（エイリアスはcedit）を指定するとサイト構成を編集することができます。

```bash
$ drush config-edit
Choose a configuration.
[0]    :  Cancel
[1]    :  action.settings
[2]    :  aes.settings
[3]    :  block.block.breadcrumbs
[4]    :  block.block.testsite_admin
[5]    :  block.block.testsite_branding
[6]    :  block.block.testsite_local_actions
[7]    :  block.block.testsite_local_tasks
[8]    :  block.block.testsite_login
[9]    :  block.block.testsite_messages
[10]   :  block.block.testsite_page_title
･･･
```

番号を入力→Enterでviエディタによる編集画面が開きます。
編集後、保存すれば即時にサイト構成へ反映されます。

config-edit の後ろに設定名を直接指定することもできます。

```bash
$ drush config-edit image.style.large
```

#CMIのサブモジュール

Gitワークフローをサポートしてくれるサブモジュール群です。

Configuration Tools
https://github.com/previousnext/drush_cmi_tools

Configuration Update Manager
https://www.drupal.org/project/config_update

Configuration Development
https://www.drupal.org/project/config_devel

Configuration Synchronizer
https://www.drupal.org/project/config_sync

Configuration Extra
https://github.com/drush-ops/config-extra

#小ネタ

##ローカル環境のサイト構成の値を固定する

D7の `$conf` と同じように settings.php には `$config` という設定変数があり、これを使ってローカル設定を固定することができます。

```php
$config['name-of.config']['nested']['key']
```

設定例）SMTPモジュール

```php
$config['smtp.settings']['smtp_on'] = true;
$config['smtp.settings']['smtp_host'] = 'mail.example.com';
$config['smtp.settings']['smtp_port'] = '587';
$config['smtp.settings']['smtp_protocol'] = 'standard';
$config['smtp.settings']['smtp_username'] = 'sato@example.com';
$config['smtp.settings']['smtp_password'] = '*****';
$config['system.mail']['interface']['default'] = 'SMTPMailSystem';
```

このように記述しておけばYAMLファイル、DBのどちらのサイト構成の値も無視されます。

`$config`で値を上書きしているときに気にする点として、drush config-get を実行したときに依然として管理画面上で入力しておいた値（configテーブルの値）が表示されてしまいます。
それを回避するには**`--include-overridden`**パラメータを追加します。その名の通り、上書きしている値を取得しますよという意味ですね。
このパラメータを付けておけば`$config`側の正しい値が取得できます。

```
drush config-get smtp.settings smtp_username --include-overridden
```

<img width="935" alt="スクリーンショット 2017-02-08 19.10.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/39468/a0586c04-0378-0c7e-2577-f9feab3fe465.png">


D7のときは`$conf`で値を上書きすると管理画面上でフィールドがグレイアウトされた上で値が上書き表示されていましたが、D8（現在バージョン Drupal8.2.5）ではバグなのかうまく機能していないようです…。おそらくDrupal8.3以降で修正されると思うのででそれまでは辛抱ですね･･･。
参考） https://www.drupal.org/node/2408549


##GitとCMIを連携する

開発環境からステージングへ直接ソース展開することが出来てしまいます。

```bash
$ drush @stage ssh git fetch
$ drush @stage ssh git checkout develop
$ drush @stage config-import
```

＠stage の部分はあらかじめ drushrc.php にて定義しておく必要があります。
参考）http://handbook.deeson.co.uk/development/drupal8/

ステージング環境からローカル環境へサイト構成をマージします。

```bash
$ drush @dev config-merge @stage --git --message="#28941 bug fix."
```

##dev環境と本番環境の設定の違いをどうやって吸収するか？

インポート／エクスポート時に--skip-modules を利用すれば特定モジュールを除外できるのでそこでサイトごとの違いを吸収することができるはずです。

もしくは --skip-modules を使わずに 単純に .gitignore で特定のYAMLファイルを除外する方法もあります。
https://github.com/drush-ops/drush/issues/1820

##Featureモジュールとどちらがいいの？

D7のワークフローに馴染みがある場合はFeatureモジュールを選ぶのは悪くないと思います。
CMIはすでに決められた単位の構成を受け入れるしかないですが、Featureなら構成単位は自由に決められるところは利点と思われます。
Featureモジュールも管理画面での操作だけでなくDrushコマンドにも対応しており、構成ごとにインポートしたり、すべての構成を一括インポートすることもできます。

一方のCMIはGitとの親和性が高く、Gitと連携したワークフローを組みやすいのがメリットです。
CMIまたはFeatureのどちらを選ぶかはプロジェクトメンバー間で決めれば良いと思います。

##ブロックやコンテンツのデブロイはどうするか？

D7ではfeatureモジュールとuuidモジュールを連携させてブロックやコンテンツもファイル化できていました。
D8ではuuid機能がCoreに取り込まれたのでuuidモジュールは開発が停止したために、D8版featureモジュールとuuidモジュールは連携できなくなっています。今後、featureモジュールがコンテンツデブロイに対応するかどうかもわかりません。

2018年リリース予定のDrupal8.5ではCoreにコンテンツデプロイ機能が搭載されるという噂もありますが、2017年時点のD8にはそのような機能はありません。
D8でコンテンツをデブロイを可能にするモジュール「Deploy」または「Default Content for D8」を利用すれば解決するかも知れないが、どちらも現在はalpha版の状態でまだ安定動作していないようです。

Deploy
https://www.drupal.org/project/deploy
Default Content for D8
https://www.drupal.org/project/default_content

とりあえずの対応策として今のところは、データベース上のコンテンツに該当するテーブルを持って行くという方法が考えられますが、ほかの方々はどのようにデブロイしているんでしょうね。（いい方法があれば是非おしえてください）