Active Model Serializer をざっくり使ってみた

#はじめに
Railsのプロジェクトで一部json形式でデータをレスポンスを返す必要があった時、インスタンス変数をメソッドで無理やりjsonにして関連付けレコードはmergeで無理やり連結、といった力技でコードを書いていました。
しかし、Active Model Serializerというgemを使えば簡単にjsonデータを整形できるとのことだったので試してみました。

#環境

ruby '2.5.1'
rails '5.2.4.1'
active_model_serializers '0.10.10'

#使用するモデル、アソシエーション

```ruby:project.rb
class Project < ApplicationRecord
  has_many :jobs, dependent: :destroy
  has_many :project_categories, dependent: :destroy
  has_many :categories, through: :project_categories
end
```

```ruby:job.rb
class Job < ApplicationRecord
  belongs_to :project
end
```

```ruby:project_category.rb
class ProjectCategory < ApplicationRecord
  belongs_to :project
  belongs_to :category
  
end
```

#Active Model Serializerの設定

以下のコマンドで`Model`に紐づく`Serializer`のファイルを作成できる

```ruby:
rails g serializer "モデル名"
```

まずはJobの`serializer`ファイルを設定してみる

```ruby:jobs_serializer.rb
class JobSerializer < ActiveModel::Serializer
  # 出力したいカラムを指定
  attributes :id, :name
end
```


#Jsonを出力

コントローラに以下のコードを記述

```ruby:jobs_controllers.rb
  def index
    @job = Job.first
    render json: @job, serializer: JobSerializer
  end
```

出力結果

```ruby:
{
  "id": 1,
  "name": "ジョブ1"
}
```

期待した値が帰ってきました！

#複数のデータを出力する

1つのデータだけではなく複数のデータを出力したい場合は以下のようにコード記述

```ruby:jobs_controllers.rb
  def index
    @jobs = Job.all
    render json: @jobs, each_serializer: JobSerializer
  end
```

出力結果

```ruby:
[
  {
    "id": 1,
    "name": "ジョブ1"
  },
  {
    "id": 2,
    "name": "ジョブ2"
  },
  {
    "id": 3,
    "name": "ジョブ3"
  }
]
```

#ネストしたデータを出力する

Jobの親であるProjectも一緒に表示してみる

Projectの`serializer`ファイルを下記のように設定

```ruby:projects_serializer.rb
class ProjectSerializer < ActiveModel::Serializer
  # 出力したいカラムを指定
  attributes :id, :name
end
```

Jobの`serializer`ファイルを下記のコードに修正

```ruby:jobs_serializer.rb
class JobSerializer < ActiveModel::Serializer
  # 出力したいカラムを指定
  attributes :id, :name

  # アソシエーションの指定
  belongs_to :project,  serializer: ProjectSerializer
end
```

ネストされたデータを出力するとN+1問題が発生するので、controller側で下記のようにコードを修正

```ruby:jobs_controllers.rb
  def index
    # N+1問題の対応
    @jobs = Job.preload(:project)

    render json: @jobs, each_serializer: JobSerializer
  end
```

出力結果

```ruby:
[
  {
    "id": 1,
    "name": "ジョブ1",
    "project": {
      "id": 1,
      "name": "テスト1"
    }
  },
  {
    "id": 2,
    "name": "ジョブ2",
    "project": {
      "id": 2,
      "name": "テスト2"
    }
  },
  {
    "id": 3,
    "name": "ジョブ3",
    "project": {
      "id": 3,
      "name": "テスト2"
    }
  }
]
```

#多重ネストしたデータを出力する

次はProjectと多対多の関係であるCategoryを表示してみる

Cagegoryの`serializer`ファイルを下記のように設定

```ruby:categories_serializer.rb
class CategorySerializer < ActiveModel::Serializer
  # 出力したいカラムを指定
  attributes :id, :name
end
```

Projectの`serializer`ファイルを下記のように修正

```ruby:projects_serializer.rb
class ProjectSerializer < ActiveModel::Serializer
  # 出力したいカラムを指定
  attributes :id, :name

  # アソシエーションの指定
  has_many :categories,  serializer: CategorySerializer
end
```

出力結果

```ruby:
[
  {
    "id": 1,
    "name": "ジョブ1",
    "project": {
      "id": 1,
      "name": "テスト1"
    }
  },
  {
    "id": 2,
    "name": "ジョブ2",
    "project": {
      "id": 2,
      "name": "テスト2"
    }
  },
  {
    "id": 3,
    "name": "ジョブ3",
    "project": {
      "id": 3,
      "name": "テスト2"
    }
  }
]
```

Categoryがネストされていない。。。
どうやら多重ネストを出力したい場合は、render時に`include`オプションを設定してあげる必要があるようです。

Jobの`controller`を下記のように修正

```ruby:jobs_controllers.rb
  def index
    # N+1問題の対応
    Job.preload(project: [:categories])

    render json: @jobs, each_serializer: JobSerializer, include: { project: [ :categories] }
  end
```

出力結果

```ruby
[
  {
    "id": 1,
    "name": "ジョブ1",
    "project": {
      "id": 1,
      "name": "テスト1",
      "categories": [
        {
          "id": 1,
          "name": "北海道"
        },
        {
          "id": 2,
          "name": "東北"
        },
        {
          "id": 5,
          "name": "甲信越"
        }
      ]
    }
  },
  {
    "id": 2,
    "name": "ジョブ2",
    "project": {
      "id": 2,
      "name": "テスト2",
      "categories": [
        {
          "id": 7,
          "name": "東海"
        },
        {
          "id": 8,
          "name": "関西"
        },
        {
          "id": 9,
          "name": "中国３"
        },
        {
          "id": 10,
          "name": "四国"
        },
        {
          "id": 11,
          "name": "九州・沖縄"
        }
      ]
    }
  },
  {
    "id": 3,
    "name": "ジョブ3",
    "project": {
      "id": 3,
      "name": "テスト３",
      "categories": [
        {
          "id": 4,
          "name": "首都圏"
        },
        {
          "id": 6,
          "name": "北陸"
        },
        {
          "id": 8,
          "name": "関西"
        },
        {
          "id": 9,
          "name": "中国"
        }
      ]
    }
  },
]
```

無事多重ネストしたデータを出力できました！









#参考URL
https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/configuration_options.md
https://tech.medpeer.co.jp/entry/2018/04/25/080000
https://techracho.bpsinc.jp/hachi8833/2017_09_28/45536
https://qiita.com/unasaka/items/08659477c72656df02e8
https://qiita.com/romukey/items/b78906e91a0355e1e77a
https://qiita.com/kakkunpakkun/items/1c23b936d13f08a42752
https://qiita.com/rh_taro/items/b09b3ebb68c2566c1ee5