ho un modello di Ecto in quanto tale:Costruire una mappa JSON per un modello di Ecto autoreferenziale
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
Come si può vedere il modello Categoria sé può fare riferimento tramite l'atomo sottocategorie.
Ecco la vista associata a questo modello:
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
Ho un se condizione in sottocategorie in modo che posso giocare bello con il veleno quando non sono precaricati.
Infine, qui miei 2 funzioni del controller che richiamano questa vista:
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
La funzione show
funziona bene:
{
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}
Tuttavia, la funzione showWithChildren
è limitato a 2 livelli di nidificazione perché di come utilizzo il preloading:
{
"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
}
Ad esempio, anche l'articolo di categoria 11 sopra ha sottocategorie, ma non riesco a raggiungerle. Queste sottocategorie possono anche avere sottocategorie stesse, quindi la profondità potenziale della gerarchia è n.
Sono consapevole del fatto che ho bisogno di qualche magia ricorsiva, ma poiché sono nuovo sia per la programmazione funzionale che per l'elisir, non riesco a capirlo. Qualsiasi aiuto è molto apprezzato.
Funziona meravigliosamente. :) Vale la pena ricordare che il repository del progetto deve essere aggiunto come alias, in quanto le view non le hanno per impostazione predefinita. Cercherò di occuparmene sul lato DB piuttosto che sull'applicazione. Grazie! – user1112789
@ jose-valim è l'alternativa più carina a questo problema? – user2290820