Phoenixガイドラインを読んでみた(2) - Adding Pages -

## はじめに

[前回](http://qiita.com/dynamitecoolguy/items/c4cb74143bf0beccc6a9)、`mix phoenix.new`で作ったアプリケーションのパスを覗いてみました。
今回は、Phoenix公式ページの[Adding Pages](http://www.phoenixframework.org/docs/adding-pages)を見ながら、ページを追加していきます。
なお、[次回](http://qiita.com/dynamitecoolguy/items/aeded848cde25a871da1)はガイドラインのRoutingです。

## 追加している書物

### New Route

初期状態で、ルーティングを設定するファイルは`web/router.ex`にあります。
このファイルでは、HTTPメソッドとパスを、ControllerとActionにマッピングすることでルーティングを表します。
前回のデフォルトページの例だと以下のようになります。

```
get "/", PageController, :index
```

これは、GETメソッドでルート(/)パスへアクセスを、HelloPhoenix.PageController(`web/controllers/page_controller.ex`)のindexにマッピングしていることを表します。

では、/helloへのGETアクセスを、HelloPhoenix.HelloControllerのindexにマッピングしてみます。
`web/router.ex`のscopeに、`get "/hello", HelloController, :index`を追加します。
scopeやpipelineについては、ひとまずおいておきます。

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

    get "/", PageController, :index
    get "/hello", HelloController, :index
  end
```

### New Controller

Phoenixで言うControllerとはElixirのモジュールであり、ActionはElixirの関数です。
したがって、前項で追加したルーティングのControllerとActionは次のようになります(`web/controllers/hello_controller.ex`)。

```
defmodule HelloPhoenix.HelloController do
  use HelloPhoenix.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end
```

`index/2`関数の最初の引数`conn`は、リクエストに関するさまざまな情報が含まれています。
二つ目の引数`_params`は、リクエストパラメータが格納されます。今回は参照しないので、先頭に"_"を付けておきます。

このアクションの中身`render conn, "index.html"`は、テンプレート`index.html.eex`を探してレンダリングすることを表しています。
このファイルは、テンプレートディレクトリの後にコントローラの名前を追加したディレクトリ(`web/templates/hello/`)から探されます。

なお、`render conn, :index`とアトムを使うこともできますが、この場合は、acceptヘッダ応じてindex.htmlやindex.jsonなどのテンプレートが選択されます。

### New View

PhoenixではレンダリングはViewの仕事です。
Viewでは、Controllerからレンダリングに必要なデータを受け取り、表示用に変換し、テンプレートに適用して表示するといった作業を行います。

前述作成したHelloControllerに対してはHelloViewを用意する必要があります。なお、Viewの前の名前(Hello)は、Controllerと一致しておく必要があります。
HelloViewは、`web/views/hello_view.ex`に以下のように作成することにします。

```
defmodule HelloPhoenix.HelloView do
  use HelloPhoenix.Web, :view
end
```

### New Template

Phoenixでは、標準のテンプレートとして[EEx](http://elixir-lang.org/docs/stable/eex/EEx.html)を使用します。
Templateは、テンプレートディレクトリ(`web/templates/`)に後にControllerの名前(hello)を追加したディレクトリ(`web/templates/hello`)に配置します。テンプレート名はControllerで定義(index.html)したファイルに拡張し(.eex)を追加したファイルになります(`web/templates/hello/index.html.eex`)。

```
<div class="jumbotron">
  <h2>Hello World, from Phoenix!</h2>
</div>
```

Phoenixサーバを起動(`mix phoenix.server`)して、ブラウザで`localhost:4000/hello`にアクセスしてみます。なお、Phoenixサーバを起動しっぱなしだった場合でも、再起動する必要はありません。アクセスされたときに必要であれば再ビルドされます。

index.html.eexにはdivしかありません。html本体は`web/templates/layout/app.html.eex`にあり、このファイル内の以下の部分にindex.html.eexの内容が差し込まれます。

```
      <main role="main">
        <%= render @view_module, @view_template, assigns %>
      </main>
```

### もう一つアクション

もう一つ、URLの一部に"messenger"という名前を付けて、パラメータとしてControllerに渡して、そのパラメータを含むページを表示する例を作成します。

### New Route2

Routeにパラメータを表すアトム":messenger"を追加した行を追加します。
URLでアトムに対応する部分がパラメータとしてControllerに渡されます。
例えば、以下のRouteに対して`localhost:4000/hello/Hogehoge`を与えると、パラメータ"messenger"に"hogehoge"が割り当てられます。

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

    get "/", PageController, :index
    get "/hello", HelloController, :index
    get "/hello/:messenger", HelloController, :show
  end
```

### New Action2

`web/controllers/hello_controller.ex`はすでに作成済みですので、これにshow Actionを追加します(:showは前項でRouteに追加したAction)。
show関数の第二引数はパターンマッチングで実装しています。

```
def show(conn, %{"messenger" => messenger}) do
  render conn, "show.html", messenger: messenger
end
```

Controllerのrender/3は、第三引数にパラメータを渡すことができ、そのパラメータはViewに渡されます。

### New Template2

show Actionに対応するTemplate(`web/templates/hello/show.html.eex`)を作成します。
EExには、`<%= %>`内にElixirの式を書くことができます。
なお、`@messenger`は、テンプレートで使用できるメタプログラミングな書き方で、`Dict.get(assigns, :messenger)`と同じ意味です。これによりシンプルにパラメータを表現することができます。

```
<div class="jumbotron">
  <h2>Hello World, from <%= @messenger %>!</h2>
</div>
```

これで`localhost:4000/hello/hogehoge`などとアクセスすると、"Hello World, from hogehoge!"とブラウザに表示されることになります。