diff --git a/lib/companies/companies.ex b/lib/companies/companies.ex index 51acda13..f7984649 100644 --- a/lib/companies/companies.ex +++ b/lib/companies/companies.ex @@ -90,14 +90,18 @@ defmodule Companies.Companies do ** (Ecto.NoResultsError) """ - def get!(id, opts \\ []) do + def get!(key, opts \\ []) do preloads = Keyword.get(opts, :preloads, []) - from(c in Company) - |> preload(^preloads) - |> from() - |> where([c], is_nil(c.removed_pending_change_id)) - |> Repo.get!(id) + query = from(c in Company) |> preload(^preloads) |> from() |> where([c], is_nil(c.removed_pending_change_id)) + + final_query = + case Integer.parse(key) do + :error -> where(query, [c], c.slug == ^key) + {int_id, _remainder} -> where(query, [c], c.id == ^int_id or c.slug == ^key) + end + + Repo.one!(final_query) end @doc """ diff --git a/lib/companies/schema/company.ex b/lib/companies/schema/company.ex index 7cceb2c9..324a4643 100644 --- a/lib/companies/schema/company.ex +++ b/lib/companies/schema/company.ex @@ -6,6 +6,7 @@ defmodule Companies.Schema.Company do alias Companies.Schema.{Industry, Job, PendingChange} + @derive {Phoenix.Param, key: :slug} schema "companies" do field :blog, :string field :description, :string @@ -13,6 +14,7 @@ defmodule Companies.Schema.Company do field :location, :string field :name, :string field :url, :string + field :slug, :string belongs_to :industry, Industry has_many :jobs, Job, defaults: [removed_pending_change_id: nil] @@ -25,7 +27,26 @@ defmodule Companies.Schema.Company do @doc false def changeset(company, attrs) do company - |> cast(attrs, [:name, :description, :url, :github, :location, :blog, :industry_id]) + |> cast(attrs, [:name, :description, :url, :github, :location, :blog, :industry_id, :slug]) |> validate_required([:name, :description, :url, :industry_id]) + |> generate_slug() + |> unique_constraint(:slug, message: "Company slug already exists") end + + defp generate_slug(%Ecto.Changeset{data: %{id: id}} = changeset) when not is_nil(id) do + changeset + end + + defp generate_slug(%Ecto.Changeset{changes: %{name: name}} = changeset) do + slug = + name + |> String.replace(~r/['’]s/u, "s") + |> String.downcase() + |> String.replace(~r/([^a-z0-9가-힣])+/, "-") + |> String.replace(" ", "-") + + put_change(changeset, :slug, slug) + end + + defp generate_slug(changeset), do: changeset end diff --git a/lib/companies_web/controllers/company_controller.ex b/lib/companies_web/controllers/company_controller.ex index 62718f5b..bc5bb539 100644 --- a/lib/companies_web/controllers/company_controller.ex +++ b/lib/companies_web/controllers/company_controller.ex @@ -37,20 +37,20 @@ defmodule CompaniesWeb.CompanyController do end end - def show(conn, %{"id" => id}) do - company = Companies.get!(id, preloads: [:jobs, :industry]) + def show(conn, %{"slug" => slug}) do + company = Companies.get!(slug, preloads: [:jobs, :industry]) render(conn, "show.html", company: company) end - def edit(conn, %{"id" => id}) do - company = Companies.get!(id) + def edit(conn, %{"slug" => slug}) do + company = Companies.get!(slug) changeset = Companies.change(company) industries = Industries.all() render(conn, "edit.html", company: company, changeset: changeset, industries: industries) end - def update(conn, %{"id" => id, "company" => company_params}) do - company = Companies.get!(id) + def update(conn, %{"slug" => slug, "company" => company_params}) do + company = Companies.get!(slug) case Companies.update(company, company_params, current_user(conn)) do {:ok, _company} -> @@ -64,8 +64,8 @@ defmodule CompaniesWeb.CompanyController do end end - def delete(conn, %{"id" => id}) do - company = Companies.get!(id) + def delete(conn, %{"slug" => slug}) do + company = Companies.get!(slug) {:ok, _company} = Companies.delete(company, current_user(conn)) conn diff --git a/lib/companies_web/router.ex b/lib/companies_web/router.ex index a5ba2919..e2205d82 100644 --- a/lib/companies_web/router.ex +++ b/lib/companies_web/router.ex @@ -53,13 +53,13 @@ defmodule CompaniesWeb.Router do scope "/" do pipe_through [:auth] - resources "/companies", CompanyController, except: [:index, :show] + resources "/companies", CompanyController, except: [:index, :show], param: "slug" resources "/jobs", JobController, except: [:index, :show] resources "/users", UserController, only: [:edit, :update] end get "/", CompanyController, :recent - resources "/companies", CompanyController, only: [:index, :show] + resources "/companies", CompanyController, only: [:index, :show], param: "slug" get "/jobs", JobController, :index get "/profile", UserController, :profile get "/for_hire", UserController, :for_hire diff --git a/priv/repo/migrations/20200305171000_add_slug_to_companies.exs b/priv/repo/migrations/20200305171000_add_slug_to_companies.exs new file mode 100644 index 00000000..f88363e8 --- /dev/null +++ b/priv/repo/migrations/20200305171000_add_slug_to_companies.exs @@ -0,0 +1,11 @@ +defmodule Companies.Repo.Migrations.AddSlugToCompanies do + use Ecto.Migration + + def change do + alter table(:companies) do + add :slug, :string + end + + create unique_index(:companies, [:slug]) + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index ab45d596..4374a531 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -174,6 +174,7 @@ technology_consulting = Repo.insert!(%Industry{name: "Technology Consulting"}) plataformatec = Repo.insert!(%Company{ name: "Plataformatec", + slug: "plataformatec", description: """ Project inception, coaching, tailored projects, general consulting. Sponsor of Elixir, employer to Elixir's BDFL. """, diff --git a/test/companies/companies_test.exs b/test/companies/companies_test.exs index 44180704..99218f72 100644 --- a/test/companies/companies_test.exs +++ b/test/companies/companies_test.exs @@ -66,21 +66,22 @@ defmodule Companies.CompaniesTest do end describe "get!/2" do - test "retrieves a company by id" do - %{id: id} = insert(:company, name: "ZULU") + test "retrieves a company by given key" do + %{id: id, slug: slug} = insert(:company, name: "ZULU") - assert %{id: ^id} = Companies.get!(id) + assert %{id: ^id} = Companies.get!("#{id}") + assert %{id: ^id} = Companies.get!(slug) end test "preloads given associations" do company = insert(:company, name: "ZULU") - assert %{jobs: []} = Companies.get!(company.id, preloads: [:jobs]) + assert %{jobs: []} = Companies.get!("#{company.id}", preloads: [:jobs]) end test "raises for unknown id" do assert_raise Ecto.NoResultsError, fn -> - Companies.get!(1000, preloads: [:jobs]) + Companies.get!("#{1000}", preloads: [:jobs]) end end end @@ -89,13 +90,13 @@ defmodule Companies.CompaniesTest do test "retrieves by id" do %{id: company_id} = insert(:company) - assert %{id: ^company_id} = Companies.get!(company_id) + assert %{id: ^company_id} = Companies.get!("#{company_id}") end test "does not retrieve deleted record" do company = insert(:company, %{removed_pending_change: build(:pending_change)}) - assert_raise Ecto.NoResultsError, fn -> Companies.get!(company.id) end + assert_raise Ecto.NoResultsError, fn -> Companies.get!("#{company.id}") end end end diff --git a/test/support/factories.ex b/test/support/factories.ex index 317052db..cf158369 100644 --- a/test/support/factories.ex +++ b/test/support/factories.ex @@ -10,6 +10,7 @@ defmodule Companies.Factory do description: "A test company", industry: insert(:industry), name: sequence(:name, &"Test Company #{&1}"), + slug: sequence(:slug, &"test-company-#{&1}"), url: "www.example.com" } end