前回 の続き
今回はルーティングですが、長いので分割します。
基本的なところ
リクエストURLのパスに対してどのコントローラ・関数を呼び出すのかを定義するのがルーティングですが
他のFW触ってるならrouter.exの中身はイメージつくと思うので、基本的な書き方はスルーします。
mix phx.routes
コマンドを使うと定義されたルーティングの一覧が参照可能なので
これを使って実際にどういったルーティングになるのかを確認できるようです。
resources
これ1つ書くだけで、8つのルーティングが登録されるっていうやつ。
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
一部だけとか、一部を除くっていうのも可能。
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を使うことで、パスを逆引きできる。
<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用ルーティングを一括で設定できた。
それがネスト出来るという話。
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
ってどっから出てきた?
って思っていろいろ試したら、どうやらコントローラ名から拾ってきているっぽい。
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
名前空間を分けたいけど、リソースがネストしてるわけではない場合に使うのかな。
scope "/admin" do
resources "/reviews", HelloWeb.Admin.ReviewController
end
リソースのネストと違ってpath_helperのネーミングは階層が深くならない。
そのため、そのままだと他のスコープと被る可能性が出て来る
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を使うことで階層化出来る。
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については、次回。