2015-09-03 15 views
13

Tôi có một mô hình Ecto như vậy:Xây dựng một bản đồ JSON cho một tự tham khảo Ecto mô hình

defmodule Project.Category do 
    use Project.Web, :model 

    schema "categories" do 
    field :name, :string 
    field :list_order, :integer 
    field :parent_id, :integer 
    belongs_to :menu, Project.Menu 
    has_many :subcategories, Project.Category, foreign_key: :parent_id 
    timestamps 
    end 

    @required_fields ~w(name list_order) 
    @optional_fields ~w(menu_id parent_id) 

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

Như bạn có thể thấy mô hình loại có thể tham khảo bản thân thông qua các nguyên tử tiểu thể loại.

Dưới đây là quan điểm liên quan đến mô hình này:

defmodule Project.CategoryView do 
    use Project.Web, :view 

    def render("show.json", %{category: category}) do 
    json = %{ 
     id: category.id, 
     name: category.name, 
     list_order: category.list_order 
     parent_id: category.parent_id 
    } 
    if is_list(category.subcategories) do 
     children = render_many(category.subcategories, Project.CategoryView, "show.json") 
     Map.put(json, :subcategories, children) 
    else 
     json 
    end 
    end 
end 

Tôi có một nếu điều kiện trên tiểu thể loại để tôi có thể chơi đẹp với Poison khi họ không cài đặt sẵn.

Cuối cùng, đây là 2 chức năng điều khiển của tôi mà gọi quan điểm này:

defmodule Project.CategoryController do 
    use Project.Web, :controller 

    alias Project.Category 

    def show(conn, %{"id" => id}) do 
    category = Repo.get!(Category, id) 
    render conn, "show.json", category: category 
    end 

    def showWithChildren(conn, %{"id" => id}) do 
    category = Repo.get!(Category, id) 
       |> Repo.preload [:subcategories, subcategories: :subcategories] 
    render conn, "show.json", category: category 
    end 
end 

Chức năng show hoạt động tốt:

{ 
    "parent_id": null, 
    "name": "a", 
    "list_order": 4, 
    "id": 7 
} 

Tuy nhiên, chức năng showWithChildren tôi được giới hạn trong 2 mức độ làm tổ vì về cách tôi sử dụng tải trước:

{ 
    "subcategories": [ 
    { 
     "subcategories": [ 
     { 
      "parent_id": 10, 
      "name": "d", 
      "list_order": 4, 
      "id": 11 
     } 
     ], 
     "parent_id": 7, 
     "name": "c", 
     "list_order": 4, 
     "id": 10 
    }, 
    { 
     "subcategories": [], 
     "parent_id": 7, 
     "name": "b", 
     "list_order": 9, 
     "id": 13 
    } 
    ], 
    "parent_id": null, 
    "name": "a", 
    "list_order": 4, 
    "id": 7 
} 

Ví dụ: mục danh mục 11 ở trên cũng có các danh mục con nhưng tôi không thể tiếp cận chúng. Các danh mục phụ đó cũng có thể có chính các danh mục phụ, do đó độ sâu tiềm năng của hệ thống phân cấp là n.

Tôi biết rằng tôi cần một số phép thuật đệ quy nhưng vì tôi mới dùng cả lập trình hàm và Elixir, tôi không thể quấn đầu quanh nó. Bất kỳ trợ giúp nào cũng được đánh giá rất cao.

Trả lời

9

Bạn có thể xem xét việc làm nạp trước trong giao diện, vì vậy nó hoạt động một cách đệ quy:

def render("show.json", %{category: category}) do 
    %{id: category.id, 
    name: category.name, 
    list_order: category.list_order 
    parent_id: category.parent_id} 
    |> add_subcategories(category) 
end 

defp add_subcategories(json, %{subcategories: subcategories}) when is_list(subcategories) do 
    children = 
    subcategories 
    |> Repo.preload(:subcategories) 
    |> render_many(Project.CategoryView, "show.json") 
    Map.put(json, :subcategories, children) 
end 

defp add_subcategories(json, _category) do 
    json 
end 

Hãy nhớ đây không phải là lý tưởng vì hai lý do:

  1. Lý tưởng nhất là bạn không muốn để thực hiện truy vấn trong chế độ xem (nhưng điều này là đệ quy, do đó, dễ dàng hơn để xem lại hình ảnh trong chế độ xem)

  2. Bạn sẽ phát ra nhiều truy vấn cho cấp thứ hai của các danh mục con

Có một cuốn sách gọi là SQL Antipatterns và nếu tôi không nhầm, nó bao gồm cách viết cấu trúc cây. Ví dụ của bạn được hiển thị dưới dạng một mô hình trong một trong các chương miễn phí. Nó là một cuốn sách tuyệt vời và họ khám phá các giải pháp cho tất cả các antipatterns.

PS: bạn muốn show_with_children chứ không phải showWithChildren.

+2

Hoạt động tuyệt vời. :) Điều đáng nói đến là repo của dự án nên được thêm vào như một bí danh, vì chế độ xem không có chúng theo mặc định. Tôi sẽ xem xét việc chăm sóc điều này ở phía DB chứ không phải là ứng dụng. Cảm ơn! – user1112789

+0

@ jose-valim là ltree thay thế đẹp hơn cho vấn đề này? – user2290820

Các vấn đề liên quan