LoginSignup
2
2

More than 5 years have passed since last update.

Phoenixを触ってみる その3

Posted at

前回 の続き

今回はルーティングですが、長いので分割します。

基本的なところ

リクエストURLのパスに対してどのコントローラ・関数を呼び出すのかを定義するのがルーティングですが
他のFW触ってるならrouter.exの中身はイメージつくと思うので、基本的な書き方はスルーします。

mix phx.routesコマンドを使うと定義されたルーティングの一覧が参照可能なので
これを使って実際にどういったルーティングになるのかを確認できるようです。

resources

これ1つ書くだけで、8つのルーティングが登録されるっていうやつ。

router.ex
scope "/", HelloWeb do
  pipe_through :browser
  resources "/users", UserController
end
$ mix phx.routes
(略)
 user_path  GET     /users             HelloWeb.UserController :index
 user_path  GET     /users/:id/edit    HelloWeb.UserController :edit
 user_path  GET     /users/new         HelloWeb.UserController :new
 user_path  GET     /users/:id         HelloWeb.UserController :show
 user_path  POST    /users             HelloWeb.UserController :create
 user_path  PATCH   /users/:id         HelloWeb.UserController :update
            PUT     /users/:id         HelloWeb.UserController :update
 user_path  DELETE  /users/:id         HelloWeb.UserController :delete

一部だけとか、一部を除くっていうのも可能。

router.ex
resources "/users", UserController ,only: [:index, :show]
resources "/comments", CommentController, except: [:delete]
$ mix phx.routes
(略)
   user_path  GET     /users              HelloWeb.UserController :index
   user_path  GET     /users/:id          HelloWeb.UserController :show
comment_path  GET     /comments           HelloWeb.CommentController :index
comment_path  GET     /comments/:id/edit  HelloWeb.CommentController :edit
comment_path  GET     /comments/new       HelloWeb.CommentController :new
comment_path  GET     /comments/:id       HelloWeb.CommentController :show
comment_path  POST    /comments           HelloWeb.CommentController :create
comment_path  PATCH   /comments/:id       HelloWeb.CommentController :update
              PUT     /comments/:id       HelloWeb.CommentController :update

似たようなルーティングを大量に書く必要がなくなるため、router.exがスッキリしそう。

forward

hellowebではない別のPlugへリクエストを渡すことのよう。

といっても、全然元サイトに書いてあることがピンとこない。
複数機能を別々のrouterとして管理するのかな?
まだここではその話は出ないらしいので、あとでちゃんとわかるはずと思っておこう。

Path Helpers

Routerに記載しているのはパス→コントローラの対応だが、
PathHelperを使うことで、パスを逆引きできる。

index.html.eex
<a href="<%= page_path(@conn, :index) %>">To the Welcome Page!</a>

例がpageだから分かりにくいのだが、この page_path/2 はPageController専用。
UserControllerへのパスが欲しければ user_path/2 を使う。

これで画面にURLを埋め込むときにベタ書きしなくて良くなる。地味に便利。
そしてクエリもつけられる。やっぱり便利。
あとパラメータの数が違ったりするとエラーになるのも、地味に良い。

user_path(Endpoint, :show, 17, admin: true, active: false)
"/users/17?admin=true&active=false"

NestedResources

resourceで一通りのCRUD用ルーティングを一括で設定できた。
それがネスト出来るという話。

routing.ex
resources "/users", UserController do [:index, :show]
  resources "/posts", PostController, only: [:index, :show, :create]
end
$ mix phx.routes
(略)
     user_path  GET    /users                     HelloWeb.UserController :index
     user_path  GET    /users/:id                 HelloWeb.UserController :show
user_post_path  GET    /users/:user_id/posts      HelloWeb.PostController :index
user_post_path  GET    /users/:user_id/posts/:id  HelloWeb.PostController :show
user_post_path  POST   /users/:user_id/posts      HelloWeb.PostController :create

ちょっとまて、user_id ってどっから出てきた?
って思っていろいろ試したら、どうやらコントローラ名から拾ってきているっぽい。

router.ex
resources "/users", MemberController do [:index, :show]
  resources "/posts", PostController, only: [:index, :show, :create]
end
$ mix phx.routes
(略)
     member_path  GET    /users                       HelloWeb.MemberController :index
     member_path  GET    /users/:id                   HelloWeb.MemberController :show
member_post_path  GET    /users/:member_id/posts      HelloWeb.PostController :index
member_post_path  GET    /users/:member_id/posts/:id  HelloWeb.PostController :show
member_post_path  POST   /users/:member_id/posts      HelloWeb.PostController :create

コントローラ名に合わせて一番左の列とパス内のパラメータ名が変わっている。
PathHelperの関数名も、ネストしたものを"_"で並べた感じになる。
というか一番左の列がそのままPathHelperの関数名として使えるっぽい。

iex(7)> HelloWeb.Router.Helpers.user_post_path(HelloWeb.Endpoint, :index, 1)
"/users/1/posts"

Scoped Routes

名前空間を分けたいけど、リソースがネストしてるわけではない場合に使うのかな。

router.ex
scope "/admin" do
  resources "/reviews", HelloWeb.Admin.ReviewController
end

リソースのネストと違ってpath_helperのネーミングは階層が深くならない。
そのため、そのままだと他のスコープと被る可能性が出て来る

router.ex
scope "/", HelloWeb do
  pipe_through :browser # Use the default browser stack
  resources "/reviews", ReviewController, only: [:index,:show]
end
scope "/admin" do
  resources "/reviews", HelloWeb.Admin.ReviewController, only: [:index,:show,:delete]
end
$ mix phx.routes
(略)
review_path  GET     /reviews                   HelloWeb.ReviewController :index
review_path  GET     /reviews/:id               HelloWeb.ReviewController :show
review_path  GET     /admin/reviews             HelloWeb.Admin.ReviewController :index
review_path  GET     /admin/reviews/:id         HelloWeb.Admin.ReviewController :show
review_path  DELETE  /admin/reviews/:id         HelloWeb.Admin.ReviewController :delete
# ↑ 一番左の列が全部一緒

特にエラーは出なかったものの、path_helperで困る。
review_path を使うと /reviewが取れてしまうため、/admin/reviewsに対応するpath_helperが無いことになる。

なので、その時はasを使うことで階層化出来る。

router.ex
scope "/", HelloWeb do
  pipe_through :browser # Use the default browser stack
  resources "/reviews", ReviewController, only: [:index,:show]
end
scope "/admin", as: :admin do
  resources "/reviews", HelloWeb.Admin.ReviewController, only: [:index,:show,:delete]
end
$ mix phx.routes
(略)
review_path  GET     /reviews                   HelloWeb.ReviewController :index
review_path  GET     /reviews/:id               HelloWeb.ReviewController :show
admin_review_path  GET     /admin/reviews             HelloWeb.Admin.ReviewController :index
admin_review_path  GET     /admin/reviews/:id         HelloWeb.Admin.ReviewController :show
admin_review_path  DELETE  /admin/reviews/:id         HelloWeb.Admin.ReviewController :delete
iex(1)> HelloWeb.Router.Helpers.review_path(HelloWeb.Endpoint, :index)
"/reviews"
iex(2)> HelloWeb.Router.Helpers.admin_review_path(HelloWeb.Endpoint, :index)
"/admin/reviews"

また、スコープのネストも可能。
ではあるが、複雑になるので推奨はしていないっぽい。

あと面白いのが、同一パスへのスコープを複数定義することも可能という点。
HelloWebじゃないものを横に置く場合とかに使うっぽい。
あとたぶんpipe_throughも変えられるから、 :api とかを横に置きたいときも別スコープにするのかな。

そのpipe_throughについては、次回。

2
2
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
2
2