2017-12-30 28 views
0

Ich habe ein Foo Schema in meiner App, die has_many des Schemas Bar.Dynamische Anzahl der Einsätze mit 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 

Ich möchte eine Funktion, die eine ID für eine Foo nimmt, eine Kopie dieser Foo erstellt und fügt sie, und dann erstellt Kopien aller zugehörigen Bar s mit dem neuen Foo. Bei einem Foo mit vielen untergeordneten Balken kopiert diese Funktion das Foo und alle diese Balken auf einmal.

Ich benutze Ecto.Multi für solche Dinge, aber ich bin mir nicht sicher, wie ich es für eine variable Anzahl von Aktionen einrichten soll. Bisher habe ich dies:

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 

dies ein Fehler wirft:

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

Gibt es eine vernünftige Möglichkeit, dies innerhalb Ecto.Multi zu tun?

Antwort

1

Ich würde Enum.reduce_while/3 hier verwenden. Wir durchlaufen die Liste und sammeln alle eingefügten Balken. Wenn ein Einfügen fehlschlägt, geben wir diesen Fehler zurück, andernfalls geben wir die gesammelten Werte in einer Liste zurück.

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) 
Verwandte Themen