and i have struct like follow
> @primary_key{:id, :binary_id, autogenerate: true}
schema “users” do
field :name, :string
field :email, :string
field :phone, :string
field :password_hash, :string
end’
what is best way to convert Map to User Struct,
note:
i need to do this convertion on each request after authentication , so i need effective way to achieve this?
Wow, I’m pretty surprised that this isn’t built in, or at least available as part of Phoenix since incoming json data is not exactly an unexpected occurence in that context. Sure, it’s simple to manually add, and I understand the point about using strings instead of atoms for external data. Still, from an ergonomic point of view it’s super weird because it forces you to repeat your attributes (and makes refactoring harder).
Or maybe I’m missing something? I’m still very much a newb when it comes to Elixir.
Ah, as it turns out that won’t work for my purposes. I’m not trying to grab the values from a literal and when if I use inspect to get a string dump of the map, it’s obviously not going to come out as proper JSON. Ah well. I’ll write a clever macro at some point, I reckon.
My solution is to not use structs, which I don’t like, I would love to use structs. %Stuff{} = my_stuff is a lot more concise than %{"foo" => foo, "bar" => bar}
In case of many parameters, the function definitions become unreadable and ugly,
which is not an enjoyable kind of pattern matching.
defmodule Stuff do
defstruct foo: nil, bar: nil
def from_map(%{"foo" => foo, "bar" => bar}),
do: %Stuff{foo: foo, bar: bar}
# alternatively - return some error
def from_map(m),
do: m
#...
end
# ...
def handle_in("send_stuff", stuff, socket) do
with %Stuff{} = my_stuff <- Stuff.from_map(stuff) do
something_with(my_stuff)
else
# do something else with "stuff" argument ...
end
end
# ... or
def handle_in("send_stuff", stuff, socket) do
case Stuff.from_map(stuff) do
%Stuff{} = my_stuff ->
something_with(my_stuff)
# other options ...
_ ->
# ...
end
end
Tnx for your suggestions, much appreciated.
I’m trying to avoid boilerplate code in struct definitions and
if/else/with/case statements in functions (I prefer pattern matching).
I am thinking about something like this, using a helper function:
def to_struct(%{} = params, kind) do
params
|> Poison.encode
|> (fn {:ok, json} -> json end).()
|> Poison.decode(%{as: kind, keys: :atoms!})
|> (fn {:ok, decoded} -> decoded end).()
end
def handle_in("send_stuff", %{} = m, socket) do
m |> to_struct(%Stuff{}) |> something_with
end
defp something_with(%Stuff{} = stuff), do: ...
Or maybe I should just skip decoding/encoding and do something like this:
// javascript
let data = { "json": JSON.stringify(actual_stuff) };
// push...
// elixir
def handle_in("send_stuff", %{"json" => json}, socket) do
json |> Poison.decode(%{as: %Stuff{}, keys: :atoms!}) |> something_with
end
Not sure you want to, but you could also use an ecto embedded schema definition, which would allow you to just cast the json into a “structure” (including type conversion)
Surely ecto provides some very nice functionality that could be used in this context.
Also I would never persist anything without using ecto, embedded or otherwise.
I was thinking about defining embedded modules with structs inside a module that uses Phoenix.Channel.
The structs would tell me about the kind of stuff coming in and the function definitions would be short.
So it’s more about clarity than anything else.
I must be missing something - why does Poison have get involved in to_struct? I suspect it’s because you really want to use the Poison.Decode.transform/2 functionality.
Given that the transform process relies on empty structs (possibly nested) I’d be tempted to just code the necessary boilerplate conversions without Poison - that code can go into a separate module from the struct definition. If there is an opportunity to eliminate the boilerplate in the future, great - otherwise and move on …