13
4

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でrender()を書く場所によって書き方が異なる?

Last updated at Posted at 2019-05-12

fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます :bow:

Phoenixで、render()を使っていると、書く場所によって、書き方が違うのを、少し解説しておこうと思います

なお、「Phoenix」は、ElixirのWebフレームワークです

内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします :wink:

コントローラと.eexでrender()の書き方が異なる?

コントローラ(通常ページ、LiveView共に)では、下記のようにrender()を使います

lib/sample_web/controllers/page_controller.ex
defmodule SampleWeb.PageController do
	use SampleWeb, :controller

	def index(conn, params ) do
		render( conn, page, params: params )
	end
end

一方、.eexファイル内(layout.html.eexも含む)で、別の.html.eexファイルを読み込む場合に使うrender()では、以下のように、第一引数がconnではありません

lib/sample_web/templates/page/index.html.eex
<article>
	<section>
		<%= render( @view_module, "header.html", params: @params ) %>
	</section>
</article>

コントローラで使うrender()の第一引数を調べてみる

まず、コントローラで使用しているrender()の第一引数を見てみます

以下のようなIO.inspect()によるデバッグコードを追加して、ブラウザ表示を行い、コンソールを確認します

なお、ブラウザ表示時、http://localhost:4000/?param_a=123&param_b=xyzとパラメータを付けておきます

lib/sample_web/controllers/page_controller.ex
defmodule SampleWeb.PageController do
	use SampleWeb, :controller

	def index(conn, params ) do

IO.puts "=============================="
IO.inspect conn
IO.puts "=============================="

		render( conn, page, params: params )
	end
end

このデバッグで、第一引数が「%Plug.Conn」であることが確認できます

iex> [info] GET /
iex> [debug] Processing with SampleWeb.PageController.index/2
  Parameters: %{"param_a" => "123", "param_b" => "xyz"}
  Pipelines: [:browser]
iex> ==============================
iex> %Plug.Conn{
  adapter: {Plug.Cowboy.Conn, :...},
  assigns: %{},
  before_send: [#Function<2.67953897/1 in Phoenix.Controller.fetch_flash/2>,
   #Function<0.58261320/1 in Plug.Session.before_send/2>,
   #Function<1.1812729/1 in Plug.Logger.call/2>,
   #Function<0.102641852/1 in Phoenix.LiveReloader.before_send_inject_reloader/2>],
  body_params: %{},
  cookies: %{},
  halted: false,
  host: "localhost",
  method: "GET",
  owner: #PID<0.32698.1>,
   params: %{"param_a" => "123", "param_b" => "xyz"},
  path_info: [],
  path_params: %{},
  port: 4000,
  private: %{
    SampleWeb.Router => {[], %{}},
    :phoenix_action => :index,
    :phoenix_controller => SampleWeb.PageController,
    :phoenix_endpoint => SampleWeb.Endpoint,
    :phoenix_flash => %{},
    :phoenix_format => "html",
    :phoenix_layout => {SampleWeb.LayoutView, :app},
    :phoenix_pipelines => [:browser],
    :phoenix_router => SampleWeb.Router,
    :phoenix_view => SampleWeb.PageView,
    :plug_session => %{},
    :plug_session_fetch => :done
  },
  query_params: %{"param_a" => "123", "param_b" => "xyz"},
  query_string: "param_a=123&param_b=xyz",
  remote_ip: {127, 0, 0, 1},
  req_cookies: %{},
  req_headers: [
    {"accept",
     "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
    {"accept-encoding", "gzip, deflate"},
    …
  ],
  request_path: "/",
  resp_body: nil,
  resp_cookies: %{},
  resp_headers: [
    {"cache-control", "max-age=0, private, must-revalidate"},
    {"x-request-id", "FZvM6ESlOHrlYv4AAv2j"},
    {"x-frame-options", "SAMEORIGIN"},
    …
  ],
  scheme: :http,
  script_name: [],
  secret_key_base: :...,
  state: :unset,
  status: nil
}
iex> ==============================

.eexで使うrender()の第一引数は「ページ表示に使っているViewモジュール」

次に、.eexで指定している第一引数をIO.inspect()で見てみます

lib/sample_web/templates/page/index.html.eex
<%
IO.puts "------------------------------"
IO.inspect @view_module
IO.puts "------------------------------"
%><article>
	<section>
		<%= render( @view_module, "header.html", params: @params ) %>
	</section>
</article>

このデバッグで、第一引数が「SampleWeb.PageView」であることが確認できます

iex> [info] GET /
iex> [debug] Processing with SampleWeb.PageController.index/2
  Parameters: %{"param_a" => "123", "param_b" => "xyz"}
  Pipelines: [:browser]
iex> ------------------------------
iex> SampleWeb.PageView
iex> ------------------------------
```

# ネタばらし

実は、引数の数が同じ、異なる2つのrender()が、定義されているのです(紛らわしい…)

両方のrender()のリファレンスを確認してみましょう

## コントローラで使用されるrender()は「Phoenix.Controller」の関数

https://hexdocs.pm/phoenix/Phoenix.Controller.html#render/3
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/19277458-3971-76d8-9bfc-7be30fe95f30.png)

## .eexで使用されるrender()は「Phoenix.View」の関数

https://hexdocs.pm/phoenix/Phoenix.View.html#render/3
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155423/d8f69d4e-2561-cdf2-c657-183129bba3b7.png)

# 何故、こんな面倒な仕様になっているの?

render()は、本来、ビュー側での定義が、MVC的な責務です(PhoenixのソレがMVCなのかはさておき)

しかし、render()は、コントローラ内から頻繁に呼び出されるため、利便性を上げることを目的に、コントローラ側の関数としても実装されている … というのがPhoenixでの実態と思われます

ちなみに、[コントローラおよびビューのモジュール名のコラム](https://qiita.com/piacere_ex/items/688b3e2542bc67ef52c7#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%A8%E3%83%93%E3%83%A5%E3%83%BC%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%90%8D%E3%81%8C%E4%B8%8D%E4%B8%80%E8%87%B4%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%A8)でも少し解説していますが、コントローラのモジュール名を基準に、自動的にビューのモジュール名を特定することが可能なため、このような実装が実現しているのでしょう

# p.s.「いいね」よろしくお願いします

ページ左上の ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/4d515047-cc48-382e-c2b1-3ad0cc50dbbf.png) や ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/a4e3da58-70a3-4197-95a2-6a6906650d01.png) のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!:tada:
13
4
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
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?