First here is a gist with all the relevant code.
If I was to use mix phx.gen.live Blog Post posts title:string body:text
I would see that the handle_params is what does the lookup for the record via the context.
In my show example in my gist https://gist.github.com/joshchernoff/8f49a1ff56623d9dca3e021bdc2e8f3b#file-show-ex
I first try and get a current_user if there is one on my mount, then I use that current_user as a way to authorize the user who is looking up the post. Its possible the post is not published yet. (IE: draft) and anyone not an admin will (I hope, given Ecto.NoResultsError is handled) get a 404
My observation though at this point is a redundancy of callbacks.
Reviewing my logs I see not only am I making two calls to mount which is expect but I also make two calls to handle_params.
Here is the whole of my logs when making a single request to a post’s show view.
[info] GET /posts/foobar
[debug] Processing with Phoenix.LiveView.Plug.show/2
Parameters: %{"slug" => "foobar"}
Pipelines: [:browser]
[
"mount",
%{"slug" => "foobar"},
%{
"_csrf_token" => "...",
"user_token" => ...
},
#Phoenix.LiveView.Socket<
assigns: %{
flash: %{},
live_action: :show,
live_module: MorphicProWeb.PostLive.Show
},
changed: %{},
endpoint: MorphicProWeb.Endpoint,
id: "phx-Fgvs1dGJIPBNOgEC",
parent_pid: nil,
root_pid: nil,
router: MorphicProWeb.Router,
view: MorphicProWeb.PostLive.Show,
...
>
]
[debug] QUERY OK source="users_tokens" db=23.2ms idle=3075.7ms
SELECT u1."id", u1."email", u1."hashed_password", u1."confirmed_at", u1."admin", u1."inserted_at", u1."updated_at" FROM "users_tokens" AS u0 INNER JOIN "users" AS u1 ON u1."id" = u0."user_id" WHERE ((u0."token" = $1) AND (u0."context" = $2)) AND (u0."inserted_at" > $3::timestamp + (-60::decimal::numeric * interval '1 day')) [..., "session", ~U[2020-05-04 20:21:36.666142Z]]
[
"handle_params",
%{"slug" => "foobar"},
"https://localhost:4001/posts/foobar",
#Phoenix.LiveView.Socket<
assigns: %{
current_user: #MorphicPro.Accounts.User<
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
admin: true,
captcha: nil,
captcha_return: nil,
confirmed_at: ~N[2020-04-22 20:51:33],
email: "jchernoff@morphic.pro",
...
>,
flash: %{},
live_action: :show,
live_module: MorphicProWeb.PostLive.Show
},
changed: %{current_user: true},
endpoint: MorphicProWeb.Endpoint,
id: "phx-Fgvs1dGJIPBNOgEC",
parent_pid: nil,
root_pid: nil,
router: MorphicProWeb.Router,
view: MorphicProWeb.PostLive.Show,
...
>
]
[debug] QUERY OK source="posts" db=5.9ms idle=2959.3ms
SELECT p0."id", p0."body", p0."draft", p0."excerpt", p0."published_at", p0."published_at_local", p0."slug", p0."title", p0."large_img", p0."thumb_img", p0."likes_count", p0."tags_string", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."slug" = $1) ["foobar"]
[debug] QUERY OK source="tags" db=12.0ms idle=2928.6ms
SELECT t0."id", t0."name", p1."id" FROM "tags" AS t0 INNER JOIN "posts" AS p1 ON p1."id" = ANY($1) INNER JOIN "post_tags" AS p2 ON p2."post_id" = p1."id" WHERE (p2."tag_id" = t0."id") ORDER BY p1."id" [[12]]
[info] Sent 200 in 46ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 227µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Parameters: %{"_csrf_token" => "...", "vsn" => "2.0.0"}
[
"mount",
%{"slug" => "foobar"},
%{
"_csrf_token" => "...",
"user_token" => ...
},
#Phoenix.LiveView.Socket<
assigns: %{
flash: %{},
live_action: :show,
live_module: MorphicProWeb.PostLive.Show
},
changed: %{},
endpoint: MorphicProWeb.Endpoint,
id: "phx-Fgvs1dGJIPBNOgEC",
parent_pid: nil,
root_pid: #PID<0.683.0>,
router: MorphicProWeb.Router,
view: MorphicProWeb.PostLive.Show,
...
>
]
[debug] QUERY OK source="users_tokens" db=7.2ms idle=3347.1ms
SELECT u1."id", u1."email", u1."hashed_password", u1."confirmed_at", u1."admin", u1."inserted_at", u1."updated_at" FROM "users_tokens" AS u0 INNER JOIN "users" AS u1 ON u1."id" = u0."user_id" WHERE ((u0."token" = $1) AND (u0."context" = $2)) AND (u0."inserted_at" > $3::timestamp + (-60::decimal::numeric * interval '1 day')) [..., "session", ~U[2020-05-04 20:21:37.128194Z]]
[
"handle_params",
%{"slug" => "foobar"},
"https://localhost:4001/posts/foobar",
#Phoenix.LiveView.Socket<
assigns: %{
current_user: #MorphicPro.Accounts.User<
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
admin: true,
captcha: nil,
captcha_return: nil,
confirmed_at: ~N[2020-04-22 20:51:33],
email: "jchernoff@morphic.pro",
...
>,
flash: %{},
live_action: :show,
live_module: MorphicProWeb.PostLive.Show
},
changed: %{current_user: true},
endpoint: MorphicProWeb.Endpoint,
id: "phx-Fgvs1dGJIPBNOgEC",
parent_pid: nil,
root_pid: #PID<0.683.0>,
router: MorphicProWeb.Router,
view: MorphicProWeb.PostLive.Show,
...
>
]
[debug] QUERY OK source="posts" db=11.5ms idle=2211.0ms
SELECT p0."id", p0."body", p0."draft", p0."excerpt", p0."published_at", p0."published_at_local", p0."slug", p0."title", p0."large_img", p0."thumb_img", p0."likes_count", p0."tags_string", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."slug" = $1) ["foobar"]
[debug] QUERY OK source="tags" db=30.8ms queue=0.1ms idle=2214.7ms
SELECT t0."id", t0."name", p1."id" FROM "tags" AS t0 INNER JOIN "posts" AS p1 ON p1."id" = ANY($1) INNER JOIN "post_tags" AS p2 ON p2."post_id" = p1."id" WHERE (p2."tag_id" = t0."id") ORDER BY p1."id" [[12]]
This shows me that I’m not only looking up the user twice but also the blog post in question.
Is there a better way to do this? I feel like I’m missing something here. I understand that some of the callbacks get called twice as a result of the http request and the follow up websocket calls, so if thats the case whats the best approach for this? If this is it, then I guess I’m just looking for validation.