[Rails5.2] Docker内のMysql 5.7の絵文字対応（文字コードをutf8mb4に変更する）


# はじめに
Ruby on Railsでポートフォリオを作成しているRails初学者です。
今回はタイトルの通り、tableに絵文字情報を保存するためのmysqlをutf8mb4にした際の覚書です。

**Youtube Data APIから動画タイトルを保存しようとしたらエラーが出た。**

```
ActiveRecord::StatementInvalid in VideosController#refresh
Mysql2::Error: Incorrect string value: '\xF0\x9F\x94\xB5\xE8\x87...'
```

絵文字が入っているタイトルがいくつかあり、それが影響でエラーが出たようです。
mysql上のdbの文字コードを見るとuft8になっており、これを変更しなければいけない様子・・・
ひとまず色々なコードを参考にして以下の通りに実施しました。

# 環境
Docker for mac を使ってコンテナ内でRailsを開発しています。

```
ruby 2.4.5
mysql 5.7.31
Ruby on rails 5.0.7.2
```

# 流れ
（準備：dumpする）
1. my.cnfを変更する
2. mysqlをリスタートする
3. ActiveRecordにオプションを追加
4. database.ymlの変更
5. docker-compose.ymlの変更し、コンテナの再起動
6. db:migrate:resetを実行
7. dumpデータのrestore

## 準備：dumpする
そもそもこの作業を行うまでdumpという言葉を知らなかったのですが、**dump**とはDBのテーブル内の情報をSQLの形で出力することだそうです。（ちなみに最後に行うrestoreは、dumpした情報をDBに投入すること）

今回データベース設定を変更し、各テーブルの文字コードをutf8mb4にするので、一旦データをファイルとして落としておいて、設定変更後に投入するという形をとりります。

dump/restoreを簡単に行うために以下のgemを使用します。

```:Gemfile
gem 'yaml_db'
```
今回使用するのはyaml_dbという、ダンプでyaml形式ファイルを出力してくれるgemです。
[GitHub - yamldb/yaml_db](https://github.com/yamldb/yaml_db)

dumpするには以下のコマンドをすればOK

```:Terminal
bundle exec rails db:data:dump
```

コマンド実行後、`db/data.yml`にファイルが出力されています。
こんな感じで、基本的にはテーブル名、カラム情報、レコードの順番で出力されます。

```yaml:data.yml
videos:
  columns:
  - id
  - name
  - url
  - upload_at
  - created_at
  - updated_at
  records:
  - - 1
    - "【衝撃】ボーナス支給額をクイズで決める会社"
    - https://www.youtube.com/watch?v=42ofwfioMFM
    - 2020-10-09 09:00:00.000000000 Z
    - 2020-10-10 08:07:38.000000000 Z
    - 2020-10-10 08:07:38.000000000 Z
 :
```

以下で準備はOKです。

## 1. my.cnfを変更する
my.cnfファイルを以下のように設定します。
記載されている内容をきちんと理解したかったため、1行ずつ見ていきました。

```my.cnf
[mysql]
default-character-set=utf8mb4 #文字コードの設定

[mysqld] #mysqld Mysqlサーバの設定
character-set-server = utf8mb4 #文字コードの設定
skip-character-set-client-handshake #client側で指定した文字コードを無視するためのもの
collation-server = utf8mb4_general_ci #ソート順の指定
init-connect = SET NAMES utf8mb4 #クライアントからserverへの送信に使用される文字セット指定
```

各項目について、細かく見ていきます。

`[mysql]と[mysqld]`
mysqldとは、MySQLサーバとも呼ばれる、mysqlでの各種操作を受け持っているメインプログラムのこと。
mysql側の操作は必ずこのmysqldよりされるため、ここに各種設定が必要。

`skip-character-set-client-handshake`
character-set-client-handshakeとは、クライアント側の文字コードをMySQL側に反映する行為。これをスキップすることで、uft8mb4への設定ができる。

`collation-server = utf8mb4_general_ci`
collationは、照合順序：ソート順のこと。_で区切られた各単語でそれぞれ設定。
詳細は以下を参照
[【MySQL】照合順序とは？](https://qiita.com/kazu56/items/6af85ffcf8d3954455ad)

`init-connect = SET NAMES utf8mb4`
クライアントからサーバへの送信に使用される文字コードの指定
詳細は以下を参照
[10.1.4 接続文字セットおよび照合順序](https://dev.mysql.com/doc/refman/5.6/ja/charset-connection.html)


## 2. mysqlをリスタートする
ひとまず、この設定を反映するためにmysqlをリスタートします。
Docker内のコンテナに入り以下のコマンドを実行します。

もともとの設定を一応確認しておきます

```:terminal
mysql>status;
:(中略)
Server characterset:	utf8
Db     characterset:	utf8
Client characterset:	utf8
Conn.  characterset:	utf8
:
```
utf8になっています。
というわけで、一旦リスタートして、もう一度みてみます。

```:terminal
mysql>service mysql restart
```


```:terminal
mysql>status
:(中略)
Server characterset:	utf8mb4
Db     characterset:	utf8mb4
Client characterset:	utf8mb4
Conn.  characterset:	utf8mb4
:
```

設定できました！

## 3. ActiveRecordにオプションを追加
次に、ActiveRecordのcreate_tableが実行される際に、utf8mb4を使って登録するよう設定していきます。

新しく`config/initializers/utf8mb4.rb`を作成し、以下のコードを記載。
config/initializersにファイルを投入すると、Railsが起動する前に初期設定として読み込まれるようになります。

```ruby:config/initializers/utf8mb4.rb
#optionを設定するmodule
module Utf8mb4
  def create_table(table_name, options = {})
    table_options = options.merge(options: 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')
    super(table_name, table_options) do |td|
      yield td if block_given?
    end
  end
end

ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters
    class AbstractMysqlAdapter
      #最初にmodule utf8mb4を実行し、その後既存のメソッドを実行(super)      
      prepend Utf8mb4
    end
  end
end
```

## 4. database.ymlの変更
以下の内容を追加ないし変更します

```yaml:config/database.yml
  charset: utf8mb4
  encoding: utf8mb4
  collation: utf8mb4_general_ci
```

## 5. docker-compose.ymlの変更

Dockerの設定ファイルも以下のように変更します。

```yaml:docker-compose.yml
:
  db:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci #utf8mb4をセット 
    container_name: コンテナ名
    volumes:
      - ./my.cnf:/etc/mysql/conf.d/my.cnf #my.cnfを読み込むよう設定
 :
```

この状態で、一度コンテナを再起動します。

## 6. db:migrate:resetを実行

ここまでのDBへの設定を反映させるため、DBをリセット→migrateします。

## 7. dumpデータのrestore
最後に、冒頭でdumpしたデータをもう一度投入すれば完了です。
以下のコマンドでrestoreできます。

```:terminal
rails db:data:load
```

以上で設定は完了です！


# 終わりに
色々と設定してきましたが、dumpなどの機能を知ることができたので、いい勉強になりました。

**全面的に参考にさせていただいたサイト様**
大変ありがとうございます・・・！
・[MySQLのencodingをutf8からutf8mb4に変更して寿司ビール問題に対応する](https://techracho.bpsinc.jp/hachi8833/2016_08_25/25044)
・[RailsのDBバックアップ（gem:yaml_db）](https://www.blograils.com/posts/rails-backup)
・[Rails5で絵文字を保存する utf8mb4 (docker)](https://qiita.com/jonnyjonnyj1397/items/471159741b313891e355)