[Elixir+Phoenix]Ectoで多対多の関係を作成する

#Goal
Ectoを使って多対多の関係を構築する。
フォロー機能を実装する。

#Dev-Environment
OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.15
PostgreSQL: postgres (PostgreSQL) 9.4.4

#Wait a minute
フォロー機能を例に、Ectoによる多対多の関係を実装する。

Ectoの参考記事は以下のリンク先。
[yoavlt](http://qiita.com/yoavlt)氏、素晴らしい記事をありがとうございます！参考にさせて頂きます！！
参考: [Qiita - Elixir Phoenixのデータベース操作モジュールEcto入門 多対多](http://qiita.com/yoavlt/items/ffbda1f0397839c5db99#%E5%A4%9A%E5%AF%BE%E5%A4%9A)

#Index
Many to many
|> Preparation
|> Get an intermediate table

##Preparation
まず準備。
新しく検証用のプロジェクトを作成。

```cmd
>cd プロジェクト作成ディレクトリ
>mix phoenix.new followed_users
... (選択肢はyes)
>cd followed_users
>mix ecto.create
>mix phoenix.server
>ctrl+c
```

以降、プロジェクトと言えば、
followed_usersを指し示す。

データモデルは以下。

- ユーザのデータモデル
  * モデル: User
  * テーブル: users
  * 生成カラム名(カラム名:型): name:string, email:string
  * 自動生成カラム名(カラム名:型): id:integer, inserted_at:timestamp, updated_at:timestamp

- 中間テーブルのデータモデル
  * モデル: Relationship
  * テーブル: relationships
  * 生成カラム(カラム名:型): follower_id:integer, followed_id:integer
  * 自動カラム(カラム名:型): id:integer, inserted_at:timestamp, updated_at:timestamp
  * インデックス: follower_id, followed_id, follower_idとfollowed_idでの複合インデックス(ユニーク)

Description: 
インデックスを詳しく知りたい方は、[hexdocs - v0.14.3 Ecto.Migration.index/3](http://hexdocs.pm/ecto/0.14.3/Ecto.Migration.html#index/3)を参照して下さい。

Caution:
インデックスが必要ない方はやらなくて問題ないです。

自動生成でソースコードを生成

- User

```cmd
>mix phoenix.gen.html User users name:string email:string
```

- Relationship

```cmd
>mix phoenix.gen.html Relationship relationships follower_id:integer followed_id:integer
```

ファイル: web/router.ex
示されたルーティングを追加する。

```elixir
scope "/", FollowedUsers do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/users", UserController
  resources "/relationships", RelationshipController
end
```

ファイル: priv/repo/timestamp_create_relationship.exs
インデックスを追加。

```elixir
defmodule FollowedUsers.Repo.Migrations.CreateRelationship do
  use Ecto.Migration
  @disable_ddl_transaction true

  def change do
    create table(:relationships) do
      add :follower_id, :integer
      add :followed_id, :integer

      timestamps
    end

    create index(:relationships, [:follower_id], concurrently: true)
    create index(:relationships, [:followed_id], concurrently: true)
    create index(:relationships, [:follower_id, :followed_id], unique: true, concurrently: true)
  end
end
```

マイグレーションの実行。

```cmd
>mix ecto.migrate
...
```

これで準備完了。

##Get an intermediate table
中間テーブルとの関係を構築します。

ファイル: web/models/user.ex
Userのschemaを以下のように編集する。

```elixir
schema "users" do
  field :name, :string
  field :email, :string

  # User who follow
  has_many :followed_users, FollowedUsers.Relationship, foreign_key: :follower_id
  has_many :relationships, through: [:followed_users, :followed_user]

  # Followers the user
  has_many :followers, FollowedUsers.Relationship, foreign_key: :followed_id
  has_many :reverse_relationships, through: [:followers, :follower]

  timestamps
end
```

Description:
逆転していて分かり辛くなっていると思うので、説明を書いておきます。

followed_users: フォローしているユーザ
follower_idをキーにして、フォローしているユーザを取得している。

```elixir
has_many :followed_users, FollowedUsers.Relationship, foreign_key: :follower_id
has_many :relationships, through: [:followed_users, :followed_user]
```

followers: フォロワーのユーザ
followed_idをキーにして、フォローされているユーザを取得している。

```elixir
has_many :followers, FollowedUsers.Relationship, foreign_key: :followed_id
has_many :reverse_relationships, through: [:followers, :follower]
```

ファイル: web/models/relationship.ex
Relationshipのschemaを以下のように編集する。

```elixir
schema "relationships" do
  belongs_to :followed_user, FollowedUsers.User, foreign_key: :follower_id
  belongs_to :follower, FollowedUsers.User, foreign_key: :followed_id

  timestamps
end
```

テスト用データを適当に画面から用意して、
iex上でデータ取得ができるか試してみましょう。
(二件のユーザと一件のリレーションシップが最低)

iex起動。
何回も書くのが面倒くさいので、aliasを定義。

```elixir
>iex -S mix
iex(1)> alias FollowedUsers.User
nil
iex(2)> alias FollowedUsers.Relationship
nil
iex(3)> alias FollowedUsers.Repo
nil
iex(4)> import Ecto.Query
nil
```

データを取得してみる。

```elixir
iex(5)> [user] = Repo.all(from(u in User, where: u.id == 1, preload: :relationships, preload: :reverse_relationships))
```

Description:
以下のやり方でも取得できる。

```elixir
user = Repo.get(User, 1) |> Repo.preload(:relationships) |> Repo.preload(:reverse_relationships)
```

フォローしているユーザのidを取得。

```elixir
iex(7)> for followed_user <- user.followed_users do
...(7)> IO.puts followed_user.followed_id
...(7)> end
```

フォローされているユーザのidを取得。

```elixir
iex(6)> for follower <- user.followers do
...(6)> IO.puts follower.follower_id
...(6)> end
```

#Speaking to oneself
多対多の関係性に対して認識が間違えてないか不安になってきた。

#Bibliography
[Ruby on Rails Tutorial - 第十一章](http://railstutorial.jp/chapters/following-users?version=4.0#top)
[hexdocs - v0.14.3 Ecto.Schema.has_many/3 :through](http://hexdocs.pm/ecto/Ecto.Schema.html#has_many/3)
[Qiita - Elixir Phoenixのデータベース操作モジュールEcto入門 多対多](http://qiita.com/yoavlt/items/ffbda1f0397839c5db99#%E5%A4%9A%E5%AF%BE%E5%A4%9A)
[PartyIX - has_many throughで，class_nameとかforeign_keyをちゃんと復習してみる](http://h3poteto.hatenablog.com/entry/2014/06/15/231742)
[hexdocs - v0.14.3 Ecto.Migration.index/3](http://hexdocs.pm/ecto/0.14.3/Ecto.Migration.html#index/3)