4
votes

What I would like to do is to dynamically route top level paths depending on slugs that are available for a specific model in the DB, like how GitHub does for user/org names like https://github.com/elixir-lang and https://github.com/phoenixframework, but I can't seem to get my head around it in the PhoenixFramework.

What I've tried so far, in my routes.ex is:

Enum.each(MyApp.Repo.all(MyApp.User), fn section ->
  get "/#{user.username}", UserController, :show, assigns: %{"username" => user.username}
end)

but I end up with the following error when it tries to compile the app:

== Compilation error on file web/router.ex ==
** (ArgumentError) repo MyApp.Repo is not started, please ensure it is part of your supervision tree
    lib/ecto/query/planner.ex:91: Ecto.Query.Planner.cache_lookup/3
    lib/ecto/query/planner.ex:72: Ecto.Query.Planner.query/4
    lib/ecto/repo/queryable.ex:91: Ecto.Repo.Queryable.execute/5
    lib/ecto/repo/queryable.ex:15: Ecto.Repo.Queryable.all/4
    web/router.ex:25: (module)
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8
2

2 Answers

4
votes

routes.ex is DSL, that is parsed during compilation time, not in runtime. First two words in error message are Compilation error. This means, that you can't use database in your routes.

Instead, try defining top level route like this:

get /:user_slug, UserController, :show

In your controller, check if this user exist in db and return 404, if not.

2
votes

I wrote a blog post about how to do this:

http://www.adamcz.com/blog/pretty-urls-with-phoenix

If you already have slugs in the database, you just need to specify the param in your router file:

resources "/users", UserController, only: [:index, ...], param: "slug"

and then look for that param in your controller:

def show(conn, %{"slug" => slug}) do
  user = Repo.get_by(User, slug: slug)
  # ...
end