32
loading...
This website collects cookies to deliver better user experience
JsonView
to render json response easier.User
Category
, Post
and Comment
schemas.PostView
which is generated by Phoenix
defmodule MyBlogWeb.PostView do
use MyBlogWeb, :view
alias MyBlogWeb.PostView
def render("index.json", %{posts: posts}) do
%{data: render_many(posts, PostView, "post.json")}
end
def render("show.json", %{post: post}) do
%{data: render_one(post, PostView, "post.json")}
end
def render("post.json", %{post: post}) do
%{id: post.id,
title: post.title,
description: post.description,
content: post.content,
cover: post.cover,
is_published: post.is_published}
end
end
...
def render("post.json", %{post: post}) do
Map.take(post, [:id, :title, :description, :content, :cover, :is_published])
end
...
def render("post.json", %{post: post}) do
post
|> Map.take([:id, :title, :description, :content, :cover, :is_published])
|> Map.merge(%{
comment_count: render_comment_count(post),
author_name: render_author_name(post)
})
end
def render_comment_count(post) do
...
end
def render_author_name(post) do
...
end
def render("post.json", %{post: post}) do
post
|> Map.take([:id, :title, :description, :content, :cover, :is_published])
|> Map.merge(render_custom_fields(post, [:comment_count, :author_name]))
end
defp render_custom_fields(struct, fields) do
Enum.map(fields, fn field ->
{field, render_field(field, struct)}
end)
|> Enum.into(%{})
end
defp render_field(:comment_count, post) do
...
end
defp render_field(:author_name, post) do
...
end
render_field/2
functionUserView
so you can do:def render("post.json", %{post: post}) do
post
...
|> Map.merge(%{
author: render_one(post.author, MyBlogWeb.UserView, "user.json")
})
end
** (KeyError) key :id not found in: #Ecto.Association.NotLoaded<association :author is not loaded>
Ecto.Association.NotLoaded
def render("post.json", %{post: post}) do
post
...
|> Map.merge(%{
author: render_relationship(post.author, MyBlogWeb.UserView, "user.json")
})
end
defp render_relationship(%Ecto.Association.NotLoaded{}, _, _), do: nil
defp render_relationship(relation, view, template) do
render_one(relation, view, template)
end
def render("post.json", %{post: post}) do
post
...
|> Map.merge(
render_relationship(post, [
{:author, MyBlogWeb.UserView, "user.json"},
{:comments, MyBlogWeb.CommentView, "comment.json"}
])
)
end
defp render_relationship(struct, relationships) do
Enum.map(relationships, fn {field, view, template} ->
{field, render_relationship(Map.get(struct, field), view, template)}
end)
|> Enum.into(%{})
end
defp render_relationship(%Ecto.Association.NotLoaded{}, _, _), do: nil
defp render_relationship(relations, view, template) when is_list(relations) do
render_many(relations, view, template)
end
defp render_relationship(relation, view, template) do
render_one(relation, view, template)
end
@fields [:id, :title, :description, :content, :cover, :is_published]
@custom_fiels [:comment_count, :author_name]
@relationships [
{:author, MyBlogWeb.UserView, "user.json"},
{:comments, MyBlogWeb.CommentView, "comment.json"}
]
def render("post.json", %{post: post}) do
render_json(post, @fields, @custom_fiels, @relationships)
end
def render_json(struct, fields, custom_fields \\ [], relationships \\ []) do
struct
|> Map.take(fields)
|> Map.merge(render_custom_fields(struct, custom_fields))
|> Map.merge(render_relationship(struct, relationships))
end
JsonViewHelper
defmodule JsonViewHelper do
import Phoenix.View, only: [render_one: 3, render_many: 3]
def render_json(struct, view, fields, custom_fields \\ [], relationships \\ []) do
struct
|> Map.take(fields)
|> Map.merge(render_custom_fields(struct, view, custom_fields))
|> Map.merge(render_relationship(struct, relationships))
end
defp render_custom_fields(struct, view, fields) do
Enum.map(fields, fn field ->
{field, view.render_field(field, struct)}
end)
|> Enum.into(%{})
end
defp render_relationship(struct, relationships) do
Enum.map(relationships, fn {field, view, template} ->
{field, render_relationship(Map.get(struct, field), view, template)}
end)
|> Enum.into(%{})
end
defp render_relationship(%Ecto.Association.NotLoaded{}, _, _), do: nil
defp render_relationship(relations, view, template) when is_list(relations) do
render_many(relations, view, template)
end
defp render_relationship(relation, view, template) do
render_one(relation, view, template)
end
end
render_custom_fields
a bit, because we call render_field
to render custom field, so we have pass the view module as second parameter, so we can use the module to invoke those render_field
that we define.defmodule BlogeeWeb.PostView do
...
@fields [:id, :title, :description, :content, :cover]
@custom_fields [:status]
@relationships [
{:author, BlogeeWeb.UserView, "basic_info.json"},
{:category, BlogeeWeb.CategoryView, "category.json"}
]
def render("post.json", %{post: post}) do
JsonViewHelper.render_json(post, __MODULE__, @fields, @custom_fields, @relationships)
end
def render_field(:status, post) do
if post.is_published do
"published"
else
"draft"
end
end
end