如何使用 Phoenix 表单在控制器的 Absinthe @graphql 突变属性中填充自定义类型?
How do I use Phoenix forms to populate a custom type in an Absinthe @graphql mutation attribute in a controller?
我已经读完 Craft GraphQL APIs in Elixir with Absinthe (Pragprog),我正在尝试扩展 item_controller.ex 以允许编辑“菜单项”。
我在控制器中做了这些功能:
@graphql """
query ($id: ID!) {
menu_item(id: $id) @put {
name
description
}
}
"""
def edit(conn, %{data: %{menu_item: item}}) do
render(conn, "edit.html", item: item)
end
@graphql """
mutation UpdateMenuItem($id: ID!, $input: MenuItemInput!) {
updatedMenuItem: updateMenuItem(id: $id, input: $input) {
errors { key message }
menuItem {
name
description
price
}
}
}
"""
def update(conn, %{data: %{menu_item: _}}) do
conn
|> redirect(to: "/admin/items")
end
def update(conn, %{errors: errors}) do
conn
|> put_flash(:info, Enum.reduce(errors, "", fn e, a -> a <> e.message end))
|> redirect(to: "/admin/items")
end
这是我的 edit.html.eex:
<%= render "form.html",
Map.put(assigns, :action,
Routes.item_path(@conn, :update, @item)) %>
这是我的 form.html.eex:
<%= form_for @conn, @action, [method: :put, as: :input], fn f -> %>
<div class="form-group">
<label for="description">Description</label>
<input type="string" id="description" as="description" name="description" value="<%= @item.description %>"/>
<label for="price">price</label>
<input type="string" id="price" name="price" value="<%= @item.price %>"/>
<label for="name">name</label>
<input type="string" id="name" name="name" value="<%= @item.name %>"/>
<label for="category">category</label>
<input type="string" id="category" name="categoryId" value="<%= @item.category_id %>"/>
</div>
<%= submit "Update", class: "btn btn-primary" %>
<% end %>
但是当我提交表单时出现错误。这是错误:
In argument "input": Expected type "MenuItemInput!", found null.
Variable "input": Expected non-null, found null.
这对我来说很有意义。例如,我可以将 form.html.eex 中任何输入元素的名称属性更改为“输入”,然后我会得到一个不同的错误:
Argument "input" has invalid value $input.
同样,这对我来说很有意义。 $input 参数不是 MenuItemInput 变量。
所以我希望通过两种可能的方式之一来解决这个问题:
在 form.html.eex 中,我创建了一个“表单中的表单”,因此它有一个“输入”字段,而该字段又包含多个字段。也许是“输入组”?
在 item_controller 中,我将变量传递到 @graphql 模块属性中。我根本不知道该怎么做。我尝试了很多奇怪的语法,因为我什至找不到具有内联变量的 SDL 示例。
任何 advice/criticism/ideas 都非常欢迎。我敢肯定其他人已经这样做了,因为在读完这本书后尝试这似乎是很自然的事情。
我回答了我自己的问题!我只需要将我的 @graphql
查询更改为:
mutation UpdateMenuItem($id: ID!, $description: String!, $name: String!, $price: Decimal!, $categoryId: ID!) {
update_menu_item(id: $id, input: {description: $description, name: $name, price: $price, categoryId: $categoryId}) {
errors { key message }
menu_item {
name
description
price
}
}
}
因此,在 Phoenix 控制器中使用此 @graphql 模块属性时,您似乎无法真正使用自定义输入类型,如 MenuItemInput
。我还必须使用 snake-case 作为字段名称。
P.S。我将我的控制器 update
函数签名更改为:
def update(conn, %{data: %{update_menu_item: %{errors: nil, menu_item: menu_item}}}) do
警告任何其他这样做的人 - 来自 Absinthe.Phoenix 的突变不会触发订阅!这是一个针对此问题的 github 问题:https://github.com/absinthe-graphql/absinthe_phoenix/issues/74 因此,如果您希望 Phoenix 控制器操作与订阅一起使用,则必须手动发布。
我已经读完 Craft GraphQL APIs in Elixir with Absinthe (Pragprog),我正在尝试扩展 item_controller.ex 以允许编辑“菜单项”。
我在控制器中做了这些功能:
@graphql """
query ($id: ID!) {
menu_item(id: $id) @put {
name
description
}
}
"""
def edit(conn, %{data: %{menu_item: item}}) do
render(conn, "edit.html", item: item)
end
@graphql """
mutation UpdateMenuItem($id: ID!, $input: MenuItemInput!) {
updatedMenuItem: updateMenuItem(id: $id, input: $input) {
errors { key message }
menuItem {
name
description
price
}
}
}
"""
def update(conn, %{data: %{menu_item: _}}) do
conn
|> redirect(to: "/admin/items")
end
def update(conn, %{errors: errors}) do
conn
|> put_flash(:info, Enum.reduce(errors, "", fn e, a -> a <> e.message end))
|> redirect(to: "/admin/items")
end
这是我的 edit.html.eex:
<%= render "form.html",
Map.put(assigns, :action,
Routes.item_path(@conn, :update, @item)) %>
这是我的 form.html.eex:
<%= form_for @conn, @action, [method: :put, as: :input], fn f -> %>
<div class="form-group">
<label for="description">Description</label>
<input type="string" id="description" as="description" name="description" value="<%= @item.description %>"/>
<label for="price">price</label>
<input type="string" id="price" name="price" value="<%= @item.price %>"/>
<label for="name">name</label>
<input type="string" id="name" name="name" value="<%= @item.name %>"/>
<label for="category">category</label>
<input type="string" id="category" name="categoryId" value="<%= @item.category_id %>"/>
</div>
<%= submit "Update", class: "btn btn-primary" %>
<% end %>
但是当我提交表单时出现错误。这是错误:
In argument "input": Expected type "MenuItemInput!", found null.
Variable "input": Expected non-null, found null.
这对我来说很有意义。例如,我可以将 form.html.eex 中任何输入元素的名称属性更改为“输入”,然后我会得到一个不同的错误:
Argument "input" has invalid value $input.
同样,这对我来说很有意义。 $input 参数不是 MenuItemInput 变量。
所以我希望通过两种可能的方式之一来解决这个问题:
在 form.html.eex 中,我创建了一个“表单中的表单”,因此它有一个“输入”字段,而该字段又包含多个字段。也许是“输入组”?
在 item_controller 中,我将变量传递到 @graphql 模块属性中。我根本不知道该怎么做。我尝试了很多奇怪的语法,因为我什至找不到具有内联变量的 SDL 示例。
任何 advice/criticism/ideas 都非常欢迎。我敢肯定其他人已经这样做了,因为在读完这本书后尝试这似乎是很自然的事情。
我回答了我自己的问题!我只需要将我的 @graphql
查询更改为:
mutation UpdateMenuItem($id: ID!, $description: String!, $name: String!, $price: Decimal!, $categoryId: ID!) {
update_menu_item(id: $id, input: {description: $description, name: $name, price: $price, categoryId: $categoryId}) {
errors { key message }
menu_item {
name
description
price
}
}
}
因此,在 Phoenix 控制器中使用此 @graphql 模块属性时,您似乎无法真正使用自定义输入类型,如 MenuItemInput
。我还必须使用 snake-case 作为字段名称。
P.S。我将我的控制器 update
函数签名更改为:
def update(conn, %{data: %{update_menu_item: %{errors: nil, menu_item: menu_item}}}) do
警告任何其他这样做的人 - 来自 Absinthe.Phoenix 的突变不会触发订阅!这是一个针对此问题的 github 问题:https://github.com/absinthe-graphql/absinthe_phoenix/issues/74 因此,如果您希望 Phoenix 控制器操作与订阅一起使用,则必须手动发布。