Phoenix 1.2友好URL Slug问题:旧记录未更新且链接丢失ID参数
搞定Phoenix 1.2旧记录Slug缺失和索引页链接失效问题
你已经把新记录的Slug功能跑通了,但旧数据没触发Slug生成逻辑,加上空Slug导致索引页链接丢参数,这俩问题其实很好解决,分四步来:
1. 先给所有旧记录批量生成Slug
你的Slug是在创建/更新记录时通过changeset生成的,旧数据没走过这个流程,所以得手动补一遍。打开项目目录的IEx控制台(输入iex -S mix),跑这段代码:
Radios.Repo.all(Radios.Radio) |> Enum.each(fn radio -> # 只处理没有Slug的记录 if is_nil(radio.slug) or radio.slug == "" do slug = String.downcase(radio.name) |> String.replace(" ", "-") # 用Ecto.Changeset.change直接更新Slug Radios.Repo.update!(Ecto.Changeset.change(radio, slug: slug)) end end)
跑完所有旧记录就都有Slug了。
2. 优化Changeset,确保更新时Slug也能同步
现在你的slug_map只从传入的params里拿name,如果更新记录时没传name参数,Slug不会跟着更新(比如只改价格的情况)。改一下逻辑,优先用params的name,没有的话就用记录本身的name:
# 重写slug_map,把struct传进来 defp slug_map(params, struct) do # 优先取params里的name,没有就用原记录的name name = Map.get(params, "name", struct.name) if name do slug = String.downcase(name) |> String.replace(" ", "-") %{"slug" => slug} else %{} end end # 然后修改changeset函数,把struct传给slug_map def changeset(struct, params \\ %{}) do params = Map.merge(params, slug_map(params, struct)) struct |> cast(params, [:name, :desc, :price, :text1, :text2, :text3, :text4, :mainimg, :mainimg2, :slug]) |> validate_required([:name, :desc, :price, :text1, :text2, :mainimg]) end
这样以后不管是创建还是更新,只要name有值,Slug都会自动生成或更新。
3. 给Phoenix.Param加个兜底,避免空链接
万一还有记录没生成Slug(比如name为空的极端情况),索引页会生成空链接,导致show方法拿不到id。修改Phoenix.Param的实现,当Slug为空时自动用记录的id代替:
defimpl Phoenix.Param, for: Radios.Radio do def to_param(%{slug: slug, id: id}) do # 有Slug用Slug,没有就用id if slug && slug != "", do: slug, else: to_string(id) end end
同时,show方法也要兼容两种查询方式(Slug或id),不然用id访问会报错:
def show(conn, %{"id" => id}) do radio = case Integer.parse(id) do # 如果是数字,按id查 {int_id, _} -> Repo.get!(Radio, int_id) # 不是数字,按Slug查 :error -> Repo.get_by!(Radio, slug: id) end render(conn, "show.html", radio: radio) end
这样不管链接是带Slug还是id,都能正常找到记录。
4. 确认模板里的路由助手用对了
最后检查一下索引页的模板(比如web/templates/radio/index.html.eex),确保链接是用Phoenix的路由助手生成的,比如:
<%= link radio.name, to: radio_path(@conn, :show, radio) %>
不要硬写URL,路由助手会自动调用Phoenix.Param的to_param方法生成正确的链接。
内容的提问来源于stack exchange,提问作者Simon Cooper
相关产品推荐
相关产品推荐

