2017-12-30 12 views
0

私のアプリにFooというスキーマがあります。has_manyのスキーマBarです。Ecto.multiのダイナミックインサート数

schema "foos" do 
    field :content, :string 
    has_many :bars, MyApp.Content.Bar, foreign_key: :foo_id 
    end 

    schema "bars" do 
    field :content, :string 
    belongs_to :foo, MyApp.Content.Foo, foreign_key: :foo_id 
    end 

私はFooのIDをとる関数をしたいが、そのFooのコピーを作成し、それを挿入し、新しいFooに関連付けられたすべてのBar秒のコピーを作成します。多くの子バーを持つFooを与えられると、この関数はそのFooとすべてのバーを一度にコピーします。

私はEcto.Multiをこのようなものに使用していますが、可変数のアクションに対して設定する方法がわかりません。これまでのところ私はこれを持っている:

resolve fn (%{foo_id: foo_id}, _info) -> 
    oldfoo = Repo.get!(Foo, foo_id) 

    multi = 
    Multi.new 
     |> Multi.run(:foo, fn %{} -> 
     MyApp.Content.create_foo(%{ 
      content: oldfoo.content 
     }) end) 
     |> Multi.run(:bars, fn %{foo: foo} -> 

     query = 
      from b in Bar, 
      where: b.foo_id == ^foo.id, 
      select: b.id 

     bars = Repo.all(query) # returns a list of id/ints like [2,3,6,7,11...] 

     Enum.map(bars, fn barid -> 
      bar = Repo.get(Bar, barid) 
      Bar.changeset(%Bar{}, %{ 
      content: bar.content, 
      foo_id: foo.id 
      }) 
      |> Repo.insert() 
     end) 
     end) 

    case Repo.transaction(multi) do 
    {:ok, %{foo: foo}} -> 
     {:ok, foo} 
    {:error, _} -> 
     {:error, "Error"} 
    end 

これは、エラーがスローされます。

** (exit) an exception was raised: 
    ** (CaseClauseError) no case clause matching: [ok: %MyApp.Content.Foo... 

はEcto.Multi以内にこれを行うために合理的な方法はありますか?

答えて

1

ここではEnum.reduce_while/3を使用します。リストの上をループし、挿入されたすべての棒を集めます。挿入が失敗した場合はそのエラーを返し、それ以外の場合は収集した値をリストに返します。

Enum.reduce_while(bars, {:ok, []}, fn barid, {:ok, acc} -> 
    bar = Repo.get(Bar, barid) 
    Bar.changeset(%Bar{}, %{ 
    content: bar.content, 
    foo_id: foo.id 
    }) 
    |> Repo.insert() 
    |> case do 
    {:ok, bar} -> {:cont, {:ok, [bar | acc]}} 
    {:error, error} -> {:halt, {:error, error}} 
    end 
end)