Phoenix.Param error when I'm trying to create delete link in phoenix 1.4

I’m trying to update my old codes in Phoenix 1.3 to 1.4, but I have this error:

structs expect an :id key when converting to_param or a custom implementation of the Phoenix.Param protocol (read Phoenix.Param docs for more information), got: ~N[2018-12-01 19:23:39]

my code for delete query:

def delete_cards(id) do
		card = get_card_by_id(id)
		|> CarditemSchema.changeset
		case Repo.delete(card) do
			{:ok, _} -> {:ok}
			{:error, changeset} -> {:error, changeset}
		end
	end

my code in controller:

def cards_delete(conn, %{"id" => id}) do
    case CardItemQuery.delete_cards(id) do
      {:ok} ->
        conn
        |> put_flash(:info, "کارت مورد نظر با موفقیت حذف شد.")
        |> redirect(to: "/card-admin/card/category ")
			{:error, _changeset} ->
        conn
        |> put_flash(:error, "کارت مورد نظر وجود ندارد")
        |> redirect(to: "/card-admin/card/category ")
    end
  end

and my html:

<%= link "Delete", to: Routes.admin_dashbord_path(@conn, :cards_delete, card), method: :delete, data: [confirm: "Are you sure?"] %>

I have read this Phoenix.Param link document and I added @derive {Phoenix.Param, key: :id} in my schema, like this:

defmodule WeddingCard.DB.CarditemSchema do
  use Ecto.Schema
  @derive {Phoenix.Param, key: :id}
  import Ecto.Changeset
  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id


  schema "wedding_card_item" do

    field :status, :boolean, null: false
    field :code, :string, null: false
    field :title, :string, size: 100, null: false
    field :price, :string, null: false
    field :pic_one, :string, null: false
    field :pic_two, :string, null: false
    field :video, :string, null: false

    belongs_to :wedding_card_category, WeddingCard.DB.CardCategorySchema, foreign_key: :wedding_card_category_id, type: :binary_id
    timestamps()
  end

but it wasn’t fixed, how can I fix this?

my router:

    delete "/card/cards/delete", AdminDashbordController, :cards_delete

Thanks.

1 Like

I updated my code and it works for me but why does it have this behaviour ?

<%= link "Delete", to: Routes.admin_dashbord_path(@conn, :cards_delete, card.id), method: :delete, data: [confirm: "Are you sure?"] %>

I have put id card.id instead of card map and I edited my router

    delete "/card/cards/:id/delete", AdminDashbordController, :cards_delete

instead of

    delete "/card/cards/delete", AdminDashbordController, :cards_delete

I’m confused, delete method needs :id like get method, doesn’t it?

1 Like

You were originally putting the card object into the parameters, hence why you didn’t need the :id on your path, now you are putting it on your path. If you changed the Delete link to something like this then it should have worked originally without :id in the path:

<%= link "Delete", to: Routes.admin_dashbord_path(@conn, :cards_delete, id: card.id), method: :delete, data: [confirm: "Are you sure?"] %>

URL params should always be a keyword list.

2 Likes

Hello, when I use this , I have an error:

<%= link "حذف", to: Routes.admin_dashbord_path(@conn, :cards_delete, id: card.id), method: :delete, data: [confirm: "آیا مطمئن هستید؟"] %>

Error:

protocol Phoenix.Param not implemented for [id: "6bfd44ad-0584-4de3-9d33-21c01fa4943a"]. This protocol is implemented for: Any, Atom, BitString, Integer, Map

Oh my mistake, it’s supposed to be a map, not a keyword list! ^.^

<%= link "حذف", to: Routes.admin_dashbord_path(@conn, :cards_delete, %{id: card.id}), method: :delete, data: [confirm: "آیا مطمئن هستید؟"] %>
1 Like

I’m afraid that it didn’t work again

maps cannot be converted to_param. A struct was expected, got: %{id: "6bfd44ad-0584-4de3-9d33-21c01fa4943a"}

Can you show the code and your full Router (or at least the full output of the mix phx.routes command)?

my code works if I write like this:

<%= link "حذف", to: Routes.admin_dashbord_path(@conn, :cards_delete, card.id), method: :delete, data: [confirm: "آیا مطمئن هستید؟"] %>

my delete functions:

def cards_delete(conn, %{"id" => id}) do
    case CardItemQuery.delete_cards(id) do
      {:ok} ->
        conn
        |> put_flash(:info, "کارت مورد نظر با موفقیت حذف شد.")
        |> redirect(to: "/card-admin/card/category ")
			{:error, _changeset} ->
        conn
        |> put_flash(:error, "کارت مورد نظر وجود ندارد")
        |> redirect(to: "/card-admin/card/category ")
    end
  end

and

  def delete_cards(id) do
		card = get_card_by_id(id)
		|> CarditemSchema.changeset
		case Repo.delete(card) do
			{:ok, _} -> {:ok}
			{:error, changeset} -> {:error, changeset}
		end
	end

Yeah adding the :id to the path is fine, just if you want it as a parameter then the prior should work (if the :id in the path is removed of course). That struct error thing implies to me that the :id wasn’t removed or there was a conflicting route, which can’t really tell while :id is still in the route. ^.^

But yeah, having :id in the route is fine, it’s usually what I do too.

1 Like

Thank you for helping!! now I understand how it works

1 Like