2015-12-23 9 views
14

Ich schreibe eine einfache CRUD-App in Phoenix, wo Admins, wenn sie eine neue Organisation erstellen, sie mit einem anfänglichen Mitarbeiterkonto versehen können.Was ist der richtige Umgang mit geschachtelten Formularen/ecto changesets in Phoenix?

Effektiv die Beziehung zwischen Organisationen und Benutzern ist viele zu viele.

ich kam mit den folgenden:

  1. Benutzer Schema:

    defmodule MyApp.User do 
    use MyApp.Web, :model 
    
    schema "users" do 
        field :name, :string 
        field :email, :string 
        field :password, :string, virtual: true 
        field :password_hash, :string 
    end 
    
    def changeset(...) # validate email, password confirmation etc. 
    
  2. Organisationsschema:

    defmodule MyApp.Org do 
        use MyApp.Web, :model 
    
        schema "orgs" do 
        field :official_name, :string 
        field :common_name, :string 
    
        has_many :org_staff_users, MyApp.OrgStaffUser 
        has_many :users, through: [:org_staff_users, :user] 
    end 
    
    def changeset(model, params \\ :empty) do 
        model 
        |> cast(params, ~w(official_name common_name), []) 
    end 
    
    def provisioning_changeset(model, params \\ :empty) do 
        model 
        |> changeset(params) 
        |> cast_assoc(:org_staff_users, required: true) 
    end 
    
  3. Junction Tabelle org_staff_users und die entsprechende Ecto-Schema mit user_id und org_id

  4. -Controller mit der folgenden new Aktion:

    def new(conn, _params) do 
        data = %Org{org_staff_users: [%User{}]} 
        changeset = Org.provisioning_changeset(data) 
        render(conn, "new.html", changeset: changeset) 
    end 
    
  5. Vorlage mit dem folgenden Auszug:

    <%= form_for @changeset, @action, fn f -> %> 
         <%= if @changeset.action do %> 
         <div class="alert alert-danger"> 
          <p>Oops, something went wrong! Please check the errors below:</p> 
          <ul> 
          <%= for {attr, message} <- f.errors do %> 
           <li><%= humanize(attr) %> <%= message %></li> 
          <% end %> 
          </ul> 
         </div> 
         <% end %> 
    
        <%= text_input f, :official_name, class: "form-control" %> 
        <%= text_input f, :common_name, class: "form-control" %> 
    
        <%= inputs_for f, :org_staff_users, fn i -> %> 
         <%= text_input f, :email, class: "form-control" %> 
         <%= text_input f, :password, class: "form-control" %> 
         <%= text_input f, :password_confirmation, class: "form-control" %> 
        <% end %> 
    
        <%= submit "Submit", class: "btn btn-primary" %> 
    <% end %> 
    

So weit, so gut, die Form schön angezeigt.

Das Problem ist, dass ich nicht wirklich verstehen, was der kanonische Weg sein sollte, die changeset zu bauen Ich bin etwa auf create einfügen, während in der Lage zu sein es zu der Ansicht wieder passiert auf Validierungsfehler.

Es ist unklar, ob ich ein changeset (und wie?) Oder explizit drei Differenzmengen pro jede Einheit (User, Org und die Verknüpfungstabelle) verwendet werden soll.

Wie validiere ich die Änderungen für ein solches kombiniertes Formular, vorausgesetzt, dass jedes Modell/Schema seine eigenen spezifischen Validierungen definiert hat?

Die params ich auf Absenden des Formulars receieve sind alle innerhalb von %{"org" => ...} Karte, die, einschließlich die in der Tat zu einem user verwandt. Wie sollte ich das Formular richtig erstellen?

Ich habe die kürzlich aktualisierte http://blog.plataformatec.com.br/2015/08/working-with-ecto-associations-and-embeds/ gelesen, aber ich bleibe verwirrt, egal.

FWIW, ich bin auf Phoenix 1.0.4, Phoenix Ecto 2.0 und Phoenix HTML 2.3.0.

Alle Tipps würden sehr geschätzt werden.

Antwort

13

Im Moment haben Sie keine andere Option außer alles in einer Transaktion zu tun. Sie werden innerhalb der Transaktion eine Organisation erstellen, die einen eigenen Changeset hat, und wenn es funktioniert, erstellen Sie jeden Mitarbeiter.Etwas wie folgt aus:

if organization_changeset.valid? and Enum.all?(staff_changesets, & &1.valid?) do 
    Repo.transaction fn -> 
    Repo.insert!(organization_changeset) 
    Enum.each staff_changesets, &Repo.insert!/1) 
    end 
end 

Hinweis I valid? Kontrollen der Changesets mache, die nicht ideal ist, weil es nicht Zwänge berücksichtigen. Wenn jedoch Einschränkungen in den Changesets vorhanden sind, müssen Sie Repo.insert (ohne Bang !) verwenden.

Denken Sie daran, dies wird auf Ecto 2.0 viel einfacher sein. Auf Ecto Master unterstützen wir bereits belongs_to über Changesets, das heißt, Sie es durch die Erzeugung sowohl Zwischen- und End-Verbände explizit zu tun wäre in der Lage:

<%= inputs_for f, :org_staff_users, fn org_staff -> %> 
    <%= inputs_for org_staff, :user, fn user -> %> 
    # Your user form here 
    <% end %>  
<% end %> 

Wir werden jedoch unterstützen MANY_TO_MANY auch, die es überhaupt machen gerade- nach vorne.

+0

@jose_valim, wäre es toll, diese in der Blog-Post zu klären, oder einen Link zu http://blog.plataformatec.com.br/2015/12/ecto-v1 -1-released-und-ecto-v2-0-Pläne/wo es klarer umrissen wird. Ich hatte die gleiche Verwirrung. Aber oh Junge bin ich aufgeregt für Ecto 2.0. –

+0

@ José Valim, zuerst vielen Dank für die großartige Arbeit in Phoenix, ein wirklich vielversprechendes Projekt. Was das Problem verschachtelter Formulare anbelangt, hoffe ich, dass Sie in Ihrer API-Dokumentation weitere Informationen dazu finden. Der Generator erstellt Entitäten mit Beziehungen zu anderen Entitäten (sagen wir Beiträge zu Benutzern), aber es gibt kein Formularfeld oder eine Methode, die es ermöglicht, einem Benutzer beim Schreiben eines neuen Beitrags einen Beitrag zuzuweisen. Bitte geben Sie weitere Informationen dazu an. –

0

Verwendung eingebettete Schemata wie here