4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Phoenixガイドラインを読んでみた(5) - Controllers -

Last updated at Posted at 2016-04-25

はじめに

前回は、Plugを見ていきました。
今回はControllerです。
次回はViewです。

概要

Controllerには、Action (RouterによってHTTPリクエストに対するレスポンスを行うための関数) を定義しています。
Actionは、Templateをレンダリングしたり、JSONレスポンスを返したりします。

PhoenixのControllerはPlugパッケージをベースに作成されていて、Controller自身もPlugです。
Phoenixで新規にアプリケーションを作成したとき、ルート用のControllerとしてPageControllerが定義されます(web/controllers/page_controller.ex)。

defmodule HelloPhoenix.PageController do
  use HelloPhoenix.Web, :controller

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

use HelloPhoenix.Web, :controllerは、HelloPhoenix.Web(web/web.ex)のcontroller関数を呼び出します。
また、HelloPhoenix.Router(web/router.ex)に、デフォルトルート用のController定義が生成されます。

  scope "/", HelloPhoenix do
    ...
    get "/", PageController, :index

Action

Actionは関数であり、Elixirの命名規則に従っていればどんな関数名でもかまいません。
デフォルトの:indexを変更したり、"?"を付けることもできます。

ただし、indexやrouteのresourcesで作成されるAction(show, new, createなど)は変更しない方が分かりやすいです。

Actionは、常にパラメータを二つとる関数で、第一引数は、Plugミドルウェアで生成された接続情報を表すパラメータです。第二引数は、リクエストから引き回されてきたパラメータのmapです。

データアクセス

Phoenix自身はデータアクセスレイヤーを持たずに、EctoによるDBアクセスを提供しています。
もちろん、OTPのETSやDETS、mnesia、他のデータアクセスライブラリを使用することもできます。

フラッシュメッセージ

ユーザアクションに応じて簡単なメッセージを返すようなケースがあります。たとえば、エラーによる更新失敗であったり、アプリケーションへのウェルカムメッセージなどです。こういうケースでは、フラッシュメッセージが有効です。

Phoenix.Controllerでは、put_flash/3get_flash/2というフラッシュメッセージのKey-Valueペア用の関数が提供されています。
以下に、二つのフラッシュメッセージをセットする例を示します。
また、フラッシュメッセージを消去するclear_flash/1という関数もあります。

defmodule HelloPhoenix.PageController do
  . . .
  def index(conn, _params) do
    conn
    |> put_flash(:info, "Welcome to Phoenix, from flash info!")
    |> put_flash(:error, "Let's pretend we have an error.")
    |> render("index.html")
  end
end

フラッシュメッセージのキーに特に決まりはありませんが、:info:errorというのは一般的です。

フラッシュメッセージを表示するためには、Template側でフラッシュメッセージをget_flash/2で取得して表示する必要があります。
なお、デフォルトのレイアウト(web/templates/layout/app.html.eex)にはその仕組みが備わっています。

<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>

レンダリング

Controllerにおいて、コンテンツのレンダリングするには何種類か方法があります。もっとも簡単なのはPhoenixが提供するtext/2関数です。

def show(conn, %{"id" => id}) do
  text conn, "Showing id #{id}"
end

get "/path/:id"をこのshow関数にマッピングされているとします。ブラウザから/path/15をリクエストした場合、想像通り、plain-textで"Showing id 15"が返されます。

同様にjson/2関数を使えば、JSONを返すことができます。なお、Map型などをJSONに変換するにはPoisonライブラリ (Phoenixの依存ライブラリ)を使用しています。
showを以下のように変更すると、/path/15をリクエストに対して{ id: "15" }を返すようになります。

def show(conn, %{"id" => id}) do
  json conn, %{id: id}
end

Phoenixでは、テンプレートを使わずにHTMLを返すこともできます。textjsonと同様にhtml/2関数を使用します。使用方法は想像通りです。
ただ、テンプレートと違って<%= id %>といった書き方はできません。

def show(conn, %{"id" => id}) do
  html conn, """
     <html>
       <head>
          <title>Passing an Id</title>
       </head>
       <body>
         <p>You sent in id #{id}</p>
       </body>
     </html>
    """
end

直接値を出力する関数以外にも、テンプレートを使って表示する方法も一般的です。テンプレートを使って表示する場合は、render/3関数を使用します。

なお、内部的には、render/3関数はPhoenix.Viewモジュールで定義されていますが、利便性の理由からPhoenix.Controllerにも定義しています。

テンプレートを使う例は、以下の通りです。

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

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

render/3関数を使用するために、それぞれの名前の先頭を統一する必要があります。
例えば、HelloControllerからはHelloViewを参照しますし、HelloViewweb/templates/helloディレクトリを参照し、そのディレクトリ内にはrender/3で指定したテンプレート(上記の例だと"show.html.eex")が無ければなりません。

render/3には、第三引数としてパラメータを渡しますが、関数内でこれを制御することもできます。

def index(conn, _params) do
  conn
  |> assign(:message, "Welcome Back!")
  |> render("index.html")
end

上記の例をplugを使って書くこともできます。

plug :assign_welcome_message, "Welcome Back"

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

defp assign_welcome_message(conn, msg) do
  assign(conn, :message, :msg)
end

なお、plugには指定されたアクションにだけ適用されるような書き方もあります。

  plug :assign_welcome_message, "Hi!" when action in[:index]

レスポンス

200以外のレスポンスを返す場合には、send_resp/3を使います。また、レスポンスのcontent-typeを指定する場合には、put_resp_content_type/2を使用します。

def index(conn, _params) do
  conn
  |> put_resp_content_type("text/plain")
  |> send_resp(201, "")
end

レイアウト

Layoutは、テンプレートのサブセットで、/web/templates/layoutに配置されます。Phoenixでは、デフォルトでapp.html.eexというレイアウトを生成し、全てのテンプレートがこのレイアウト上に表示されます。
LayoutはあくまでTemplateに過ぎませんので、表示する際にはViewが必要になります。LayoutViewモジュールが/web/views/layout_view.exに定義されます。Layoutを/web/templates/layoutに配置する限りは、Layout用の新しいViewを生成する必要はありません。

Phoenix.Controllerモジュールのput_layout/2関数を使って、使用するLayoutを変更することができます。この関数にfalseを指定することでレイアウト無しで表示させることもできます。

def index(conn, params) do
  conn
  |> put_layout(false)                # ← ()を忘れないように
  |> render "index.html"
end

レンダリングフォーマット

同じ関数でHTMLやplain-textなどのフォーマットを切り替えたい場合は、以下のようにします。

defmodule HelloPhoenix.Router do
  use HelloPhoenix.Web, :router

  pipeline :browser do
    plug :accepts, ["html", "text"]
    ...
  end

  def index(conn, _params) do
    render conn, :index
  end
  ...  

http://localhost:4000/?_format=textとすると、"index.text.eex"を参照し、htmlだと"index.html.eex"を参照するようになります。

Content-Typeのセット

xmlを出力したい場合は、以下のようにします。

def index(conn, _params) do
  conn
  |> put_resp_content_type("text/xml")
  |> render "index.xml", content: some_xml_content
end

HTTPステータス

put_status/2を使って、200以外のステータスに変更することができます。

def index(conn, _params) do
  conn
  |> put_status(202)
  |> render("index.html")
end

リダイレクト

リダイレクトするには、redirect/2関数を使います。

defmodule HelloPhoenix.Router do
  use HelloPhoenix.Web, :router
  . . .

  scope "/", HelloPhoenix do
    . . .
    get "/", PageController, :index
  end

  # New route for redirects
  scope "/", HelloPhoenix do
    get "/redirect_test", PageController, :redirect_test, as: :redirect_test
  end
end

defmodule HelloPhoenix.PageController do
  . . .
  def index(conn, _params) do
    redirect conn, to: "/redirect_test"
  end

  def redirect_test(conn, _params) do
    text conn, "Redirect!"
  end
  . . .
end

外部へのリダイレクトの場合、to:ではなくexternal:を使います

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?