Aguxez
My concern with working with associations in this case is having to update the record, I came up with a solution and would like feedback on it to see if it is correct or even viable.
I’m working on a project where users have inventories, obviously in their inventories they have items, I wanted to keep track of how much of an item an user has, so my schema goes as follows:
User:
schema "users" do
...
has_one(:inventory, Inventory)
timestamps()
end
Inventory:
schema "inventories" do
field(:item_quantities, {:array, :map}, default: [])
belongs_to(:user, User)
many_to_many(:items, Item, join_through: "inventory_items")
timestamps()
end
Item:
schema "items" do
...
many_to_many(:inventories, Inventory, join_through: "inventory_items")
timestamps()
end
The solution I came up with to keep track of the amount of a item an user has is with the field(:item_quantities, {:array, :map} part. With a common module I update this value so it is less error prone when updating the values (adding, removing, etc…) These are the relevant functions on the module:
@doc """
Updates 'item_id' on 'inventory' adding 'amount' to it.
"""
def add_inventory_item_amount(%Inventory{} = inventory, item_id, amount) do
updated_items =
Enum.map(inventory.item_quantities, fn item ->
case item["id"] == item_id do
true ->
%{item | "amount" => item["amount"] + amount}
false ->
item
end
end)
inventory
|> Ecto.Changeset.change(%{item_quantities: updated_items})
|> Repo.update!()
end
@doc """
Updates 'item_id' on 'inventory' removing 'amount' from it.
"""
def remove_inventory_item_amount(%Inventory{} = inventory, item_id, amount) do
updated_items =
Enum.map(inventory.item_quantities, fn item ->
case item["id"] == item_id do
true ->
%{item | "amount" => item["amount"] - amount}
false ->
item
end
end)
inventory
|> Ecto.Changeset.change(%{item_quantities: updated_items})
|> Repo.update!()
end
@doc """
Adds 'item_amount' to 'inventory' of 'item'. Mostly used to add new items to the inventory.
"""
def add_item_to_inventory(%Item{} = item, %Inventory{} = inventory, item_amount) do
params = %{id: item.id, amount: item_amount}
inv = Repo.preload(inventory, :items)
inv
|> Ecto.Changeset.change(%{item_quantities: [params | inv.item_quantities]})
|> Ecto.Changeset.put_assoc(:items, [item | inv.items])
|> Repo.update!()
end
My question is, is this a valid way to do what I want to do or can I work directly with the associations without keeping track on a separate field on the schema?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ai
- #ecto-query
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #elixirconf-eu
- #api
- #forms
- #metaprogramming
- #hex











Showing Posts 1 to 2- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
jeremyjh
I would make
inventory_itemsa schema so it can have anamountattribute. Then you’d join through the schema rather than a raw table name e.g.many_to_many(:inventories, Inventory, join_through: InventoryItems). If you do this, I don’t think you actually need aninventoriestable, unless that will have some other attribute you don’t show (such as an inventory type or name).Aguxez
Yes, this is what I was looking to do. I’m not discarding having an
inventorytable as we may have another use for it later on, but you’re right, as for now I don’t think it’d be 100% necessary. Thanks!