2015-07-26 21 views
12

Demek, birçok Etiketler aittir Mesaj modeli var yönetme Soru, Phoenix'teki Save etiketini Post'a nasıl bağlayacağınız?birçok çoğa dernek

cevap

9

İç içe geçmiş değişiklikleri henüz ecto içinde desteklenmiyor: https://github.com/elixir-lang/ecto/issues/618 Etiketleri kendiniz kaydetmelisiniz.

Aşağıdaki kod parçacıklarında tag_ids seçip Post.changeset/2 bana geçerli bir sonuç verirse bunları birleştirme tablosuna eklerim. Form I'de seçilen etiketleri tutmak için formda okuyabileceğimiz ve bir varsayılan ayarlayabileceğimiz bir sanal alan ekledik. En iyi çözüm değil ama benim için çalışıyor.

(çok ilişkisi için birçok oluşturmak için JoinTable) PostController

def create(conn, %{"post" => post_params}) do 
    post_changeset = Post.changeset(%Post{}, post_params) 

    if post_changeset.valid? do 
    post = Repo.insert!(post_changeset) 

    case Dict.fetch(post_params, "tag_ids") do 
     {:ok, tag_ids} -> 

     for tag_id <- tag_ids do 
      post_tag_changeset = PostTag.changeset(%PostTag{}, %{"tag_id" => tag_id, "post_id" => post.id}) 
      Repo.insert(post_tag_changeset) 
     end 
     :error -> 
     # No tags selected 
    end 

    conn 
    |> put_flash(:info, "Success!") 
    |> redirect(to: post_path(conn, :new)) 
    else 
    render(conn, "new.html", changeset: post_changeset) 
    end 
end 

PostModel

schema "posts" do 
    has_many :post_tags, Stackoverflow.PostTag 
    field :title, :string 
    field :tag_ids, {:array, :integer}, virtual: true 

    timestamps 
end 

@required_fields ["title"] 
@optional_fields ["tag_ids"] 

def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
end 

PostTagModel

schema "post_tags" do 
    belongs_to :post, Stackoverflow.Post 
    belongs_to :tag, Stackoverflow.Tag 

    timestamps 
end 

@required_fields ["post_id", "tag_id"] 
@optional_fields [] 

def changeset(model, params \\ :empty) do 
    model 
    |> cast(params, @required_fields, @optional_fields) 
end 
PostForm

<%= form_for @changeset, @action, fn f -> %> 

    <%= if f.errors != [] 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 %> 

    <div class="form-group"> 
    <%= label f, :title, "Title" %> 
    <%= text_input f, :title, class: "form-control" %> 
    </div> 

    <div class="form-group"> 
    <%= label f, :tag_ids, "Tags" %> 
    <!-- Tags in this case are static, load available tags from controller in your case --> 
    <%= multiple_select f, :tag_ids, ["Tag 1": 1, "Tag 2": 2], value: (if @changeset.params, do: @changeset.params["tag_ids"], else: @changeset.model.tag_ids) %> 
    </div> 

    <div class="form-group"> 
    <%= submit "Save", class: "btn btn-primary" %> 
    </div> 

<% end %> 

etiketleri güncellemek istiyorsanız

, iki seçeneğiniz vardır.

  1. Tümünü sil ve yeni girişlerini değişiklikler için
  2. bak eklemek ve mevcut girdileri

Ben yardımcı olur tutun.

8

Yapmak istediğiniz ilk şey, modellerin düzeltilmesidir. Ecto, çoktan çoğa ilişkiler için has_many through: sözdizimini sağlar. Here are the docs.

Bir çoktan çoğa ilişkisi, hiçbir etiketin veya yayınların doğrudan birbirine işaret eden yabancı anahtarlara sahip olabileceğinden (bire-çok ilişki oluşturacak) birleşim tablosu gerektirir.

Ecto, has_many kullanarak bire çok çoğaltma tablosu ilişkisini, has_many through: kullanan çoktan çoğa ilişkiyi kullanarak tanımlamanızı gerektirir.

defmodule MyApp.TagPost do 

    use MyApp.Web, :model 

    schema "tag_posts" do 
    belongs_to :tag, MyApp.Tag 
    belongs_to :post, MyApp.Post 

    # Any other fields to attach, like timestamps... 
    end 

    # … 
end 

yapabileceksiniz istiyorsanız emin olun:

defmodule MyApp.Post do 

    use MyApp.Web, :model 

    schema "posts" do 
    has_many :tag_posts, MyApp.TagPost 
    has_many :tags, through: [:tag_posts, :tags] 

    field :title, :string 
    field :description, :string 
    end 

    # … 
end 

Bu, bir şöyle görünür tablo tag_posts katılmak olduğunu varsayar: sevdiği örnekle

, görüneceğini Belirli bir etiketle ilişkili tüm yayınları görmek için, İlişki modelinde başka bir şekilde ilişkiyi tanımladığınızdan emin olun:

defmodule MyApp.Tag do 

    use MyApp.Web, :model 

    schema "posts" do 
    has_many :tag_posts, MyApp.TagPost 
    has_many :posts, through: [:tag_posts, :posts] 

    # other post fields 
    end 

    # … 
end 

Ardından, denetleyicinizde, hem kaydettiğiniz gönderinin kimliğine hem de listenizdeki etiketlerin kimliğine sahip yeni tag_post'ler oluşturmak istiyorsunuz.

+0

Ben senin Örneğin burada verilen dayalı Modellerimi genişlettik ama bazı sorularım var: Eğer "#other sonrası alanları" yazma MyApp.Tag yılında , sana "#other etiketi alanları" Gelecekte demek kabul Örneğin belirli bir etikete sahip tüm Yayınları veya belirli bir gönderi için tüm etiketleri nasıl sorgularım? Bu hazır sorguların TagPost modeline konması gerektiğini düşünüyorum. Sanırım ne soruyorum, herhangi bir "kısayol" ya da bunu yapmanın basitleştirilmiş bir yolu var mı? Yoksa elle uzun bir sorgu yazmam gerekecek mi? – Wobbley

+0

@The Brofessor PostController'ı önerdiğiniz değişiklikleri yansıtacak şekilde nasıl değiştirirsiniz? – helcim