Elixir Phoenix:我可以从模板调用上下文函数吗?

Elixir Phoenix: Can I call a context function from a template, and should I

我想以文章形式(用于创建和更新)显示 article_categories 的列表作为下拉输入。

我的博客上下文 (blog.ex) 中有一个函数可以通过 Ecto 检索所有类别并将结果格式化为可下拉列表:

defmodule MyApp.Blog do
  import Ecto.Query, warn: false
  alias MyApp.Repo

  alias MyApp.Blog.ArticleCategory

  def get_categories() do
    MyApp.Repo.all from c in ArticleCategory, select: {c.title, c.id}
  end
end

在我的模板 (templates/article/form.html.heex) 中,我这样称呼我的类别:

<%= select f, :category_id, MyApp.Blog.get_categories(), prompt: [key: "Choose your category"] %>

视觉上我得到了我想要的结果,但是...文献似乎暗示将我的类别作为分配传递。

这样做有问题吗? (性能?设计?)

我完全赞成将该逻辑放入控制器中。这里的主要动机是关注点分离:

  • 你的控制器负责解析传入的参数并从data/business层
  • 获取正确的数据
  • 您的视图层负责将数据呈现给用户

其中一个优点是您最终会得到一个更易于维护和测试的系统。

我想很难看出为什么这是最佳实践,但随着应用程序的增长,它会显示出来。

例如:一项新要求是 get_categories 现在仅列出用户有权访问的类别。使用控制器分配,您可以创建一个控制器测试,其中包含类似 assert conn.assigns.categories == ["one", "two"] 的内容。但是,当您在视图中获取类别时,测试将变成类似于 assert html_response(conn, 200) =~ "<li>one</li><li>two</li>" 的东西,它更脆弱(即它不能保证没有项目 'three',或者如果您添加 class 到每个 li 元素,那么你还必须更新测试。