Entity to_h - nested attribute entity is not converted to hash
iJackUA opened this issue · comments
Is it by design? Because I was expecting it to work a bit different.
Suppose I have two Entities
class User < Hanami::Entity
attributes do
attribute :id, Types::Int
attribute :auth_data, Types::Entity(::UserAuthData)
end
end
class UserAuthData < Hanami::Entity
attributes do
attribute :hi, Types::String
end
end
I can initialize it that way
user_params = {id: 3, auth_data: {hi: "hello"}}
user = User.new(user_params)
now when I do user.to_h
{:id=>3,
:auth_data=>#<UserAuthData:0x000055d8e7fcf600 @attributes={:hi=>"hello"}>}
User entity is serialized to hash, but enclosed UserAuthData is still an object, we did not return to the same user_params
The only way I see to do that is
Hanami::Utils::Hash.new(user).to_hash
or
Hanami::Utils::Hash.deep_serialize(user)
these methods return {:id=>3, :auth_data=>{:hi=>"hello"}}
as expected
Maybe Entity object by itself need to have a helper to serialize itself to real nested hash?
Actually I would expect from entity_object.to_h
to do this by default, but maybe I miss some consideration.
Duplicate of #470.
You noticed you can use Hanami::Utils::Hash.deep_serialize
, which is the recommendation from that issue.
But we didn't really answer the question why this doesn't happen by default though. I agree it's surprising that it doesn't work that way. Any input @mereghost? I wonder if a workaround could be adding an optional param, like#to_h(deep: false)
might make sense?
I vaguely remember a past issue of this up ahead in the framework. Also that's how ruby's #to_h
works. It only operates on the instance leaving nested objects intact.
We could provide a way to make sure that the object is deep serialized into a hash for convenience, but that's already provided by hanami-utils
which we depend on.
Thanks @cllns it seems I searched not very good before creating an issue.
There are really valid points (of why current behaviour is "as it is now"):
- that is how ruby's #to_h works
- for JSON API we need to use better serializers (what I actually do) than
to_h
My potential use case to have recursive to_h serialization was in adopting Entity in Trailblazer's Operation. Input of the typical Operation is Hash of params from Action. Later on, an Entity is retrieved based on params and data from Entity is being manipulated during next steps of the Operation. Idea was to use Entity as DTO through these steps, so each step can modify Entity attribute (and nested also).
But at the last step of the Operation I want to persist this Entity to DB via Repository.
And Repository's update
method takes Hash (of attributes) as a data
param.
So I was searching a way to get back a Hash representation of "complex" Entity.
And yes Hanami::Utils::Hash.deep_serialize(user)
can be used, but it took me 30 minutes to find it out.
Maybe a more straightforward way to this can be introduced?
P/S Actually my another and wider question first time I tried Hanami was "Oh great, I can get Entity as a "select" query result from Repo, but wait... why I can not put ready made Entity object as a data
in "insert/update" command in Repo?" :)
@iJackUA I'm sorry for the veeery late response.
We really need to up our game on the documentation side of things (contributions are always welcome ;)).
About the insert/update part: if you use the Repository#create
method you can safely pass an entity. For the Repository#update
method we always take the arguments provided as canon (as we don't do dirty tracking), so if you pass in an Entity
instance as the second parameter (the first being the id) it will set whatever attributes are set there(even stuff like created_at
etc).
Think of it as a poor man's changeset (which is a concept we might introduce in the future). You pass in what you want to update and we do it.