2015-12-23 2 views
14

Я пишу простое приложение CRUD в Фениксе, где администраторы при создании новой организации могут предоставить ему исходную учетную запись сотрудника.Каков правильный способ обработки вложенных форм/ecto изменений в Phoenix?

Фактически взаимоотношения между организациями и пользователями много для многих.

я придумал следующее:

  1. схема пользователя:

    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. Организация схемы:

    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 стол org_staff_users и соответствующий Ecto схемы с user_id и org_id

  4. Контроллер со следующими new действия:

    def new(conn, _params) do 
        data = %Org{org_staff_users: [%User{}]} 
        changeset = Org.provisioning_changeset(data) 
        render(conn, "new.html", changeset: changeset) 
    end 
    
  5. шаблона со следующей выдержке:

    <%= 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 %> 
    

До сих пор так хорошо, форма проявления красиво.

Проблема заключается в том, что я не понимаю, каков должен быть канонический способ построения набора изменений, который я собираюсь вставить на create, имея возможность передать его снова на просмотр при ошибках проверки.

Неясно, должен ли я использовать одну ревизию (и как?) Или явно три ревизии за каждый объект (User, Org и таблицу перехода).

Как проверить изменения для такой комбинированной формы, учитывая, что каждая модель/схема имеет свои собственные конкретные проверки?

Параметры, которые я получаю при отправке формы, находятся в пределах %{"org" => ...} карта, включая те, которые на самом деле связаны с user. Как я должен правильно создать форму?

Я прочитал недавно обновленный http://blog.plataformatec.com.br/2015/08/working-with-ecto-associations-and-embeds/ , но я все равно смущен.

FWIW, я нахожусь на Phoenix 1.0.4, Phoenix Ecto 2.0 и Phoenix HTML 2.3.0.

Любые советы были бы весьма признательны.

ответ

13

Прямо сейчас у вас нет другого выбора, кроме всего, что нужно сделать в транзакции. Вы собираетесь создать организацию внутри транзакции, которая имеет свой собственный набор изменений, и если он работает, вы создаете каждого сотрудника.Что-то вроде этого:

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 

Обратите внимание, что я делаю valid? проверки на ревизиях, который не является идеальным, поскольку он не учитывает ограничения. Если существуют ограничения в наборах изменений, вам необходимо использовать Repo.insert (без помех !).

Имейте в виду, что на Ecto 2.0 это будет намного проще. На мастер экто, мы уже поддерживаем belongs_to через ревизии, что означает, что вы могли бы сделать это в явном виде, генерируя как промежуточные и конечные ассоциации:

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

Однако, мы также будем поддерживать many_to_many, которая сделает его полностью неразветвленный вперед.

+0

@jose_valim, было бы здорово, чтобы разъяснить это в блоге, или ссылку на http://blog.plataformatec.com.br/2015/12/ecto-v1 -1-released-and-ecto-v2-0-plans/где он более четко обозначен. У меня была такая же путаница. Но, ох, я в восторге от Ecto 2.0. –

+0

@ José Valim, первое спасибо за всю большую работу, поставленную в Phoenix, действительно перспективный проект. Что касается вопроса о вложенных формах, я надеюсь, вы представите больше информации об этом в документации API. Генератор создает объекты с отношениями к другим объектам (например, сообщения для пользователей), но не существует поля формы или метода, позволяющих назначать сообщение пользователю при написании нового сообщения. Пожалуйста, предоставьте дополнительную информацию об этом. –

0

Используйте встроенные схемы, как описано here

 Смежные вопросы

  • Нет связанных вопросов^_^