Created
June 10, 2020 17:57
-
-
Save Doerge/f9278eec405f05e71443380878ea5e5f to your computer and use it in GitHub Desktop.
Example of copying a hierachy of an Ecto schema.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule MyApp.Item do | |
use Ecto.Schema | |
import Ecto.Changeset | |
alias MyApp.Item | |
schema "items" do | |
field :name, :string | |
belongs_to :parent, Item | |
has_many :children, Item, foreign_key: :parent_id | |
timestamps() | |
end | |
def copy_changeset(item, attrs \\ %{}) do | |
%Item{ | |
name: item.name, | |
parent_id: item.parent_id, | |
children: [] | |
} | |
|> cast(attrs, [ | |
:name | |
]) | |
end | |
end | |
defmodule MyApp.MyContext do | |
alias Ecto.Multi | |
alias MyApp.Repo | |
alias MyApp.Item | |
@doc """ | |
Inserts an item copy in the Multi-arg. | |
## Examples | |
iex> copy_item(item, 42, Multi.new()) | |
%{:ok, %{{:item, 42} => new_item} | |
""" | |
def copy_item(item, item_key, multi, attrs \\ %{}) do | |
new_item_changeset = Item.copy_changeset(item, attrs) | |
multi | |
|> Multi.insert({:item, item_key}, new_item_changeset) | |
end | |
@doc """ | |
Deep copies the item hierachy. | |
## Examples | |
iex> copy_item_hierachy(item, Multi.new()) |> Repo.transaction() | |
%{:ok, %{{:item, :root} => new_item, {:item, 0} => new_item_child0, ...} | |
Changes are cast in the item's copy_changeset functions, so it's possible to | |
inject changes on the fly: | |
iex> copy_item_hierachy(item, Multi.new(), %{name: "parent"}, %{name: "child"}) |> Repo.transaction() | |
%{:ok, %{{:item, :root} => %Item{name: "parent"}, {:item, 0} => %Item{name: "child"}, ...} | |
""" | |
def copy_item_hierachy(item, multi, root_attrs \\ %{}, child_attrs \\ %{}) do | |
# Copy the item | |
root_item_multi = copy_item(item, :root, multi, root_attrs) | |
# Handle children | |
root_item_multi | |
|> Multi.merge(fn %{{:item, :root} => new_root_item} -> | |
item.children | |
|> Enum.with_index() | |
|> Enum.reduce(Multi.new(), fn {child_item, item_index}, acc_multi -> | |
attrs = Map.put(child_attrs, :parent_id, new_root_item.id) | |
copy_item(child_item, item_index, acc_multi, attrs) | |
end) | |
end) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment