use*_*789 14 json elixir ecto phoenix-framework
我有一个Ecto模型:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,Category模型可以通过子类别atom引用自身.
以下是与此模型关联的视图:
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
Run Code Online (Sandbox Code Playgroud)
我在子类别上有if条件,这样我就可以在没有预装的情况下使用Poison.
最后,这是我调用此视图的2个控制器函数:
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
Run Code Online (Sandbox Code Playgroud)
该show
功能正常工作:
{
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}
Run Code Online (Sandbox Code Playgroud)
但是,showWithChildren
由于我使用预加载的方式,我的功能仅限于2级嵌套:
{
"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
}
Run Code Online (Sandbox Code Playgroud)
例如,上面的类别项目11也有子类别,但我无法联系到它们.这些子类别本身也可以有子类别,因此层次结构的潜在深度为n.
我知道我需要一些递归魔法,但由于我是功能编程和Elixir的新手,我无法绕过它.任何帮助是极大的赞赏.
您可以考虑在视图中执行预加载,因此它以递归方式工作:
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
Run Code Online (Sandbox Code Playgroud)
请记住,这不是理想的两个原因:
理想情况下,您不希望在视图中进行查询(但这是递归的,因此在视图渲染中更容易搭载)
您将为第二级子类别发出多个查询
有一本名为SQL Antipatterns的书,如果我没有弄错,它将介绍如何编写树结构.您的示例在其中一个免费章节中作为反模式公开.这是一本很好的书,他们探索所有反模式的解决方案.
PS:你想要show_with_children
而不是showWithChildren
.
归档时间: |
|
查看次数: |
1472 次 |
最近记录: |