From c6bb2828470c3c87520c6d8a8d3bb1cbade72afa Mon Sep 17 00:00:00 2001 From: Douglas Lutz Date: Thu, 5 Mar 2020 18:12:12 -0300 Subject: [PATCH] Add slug field to company --- lib/companies/companies.ex | 8 +++---- lib/companies/schema/company.ex | 19 ++++++++++++++- .../controllers/company_controller.ex | 16 ++++++------- lib/companies_web/router.ex | 5 ++-- .../templates/company/card.html.eex | 2 +- .../20200305171000_add_slug_to_companies.exs | 11 +++++++++ ..._add_slug_value_for_existing_companies.exs | 23 +++++++++++++++++++ priv/repo/seeds.exs | 1 + test/companies/companies_test.exs | 21 +++++------------ test/support/factories.ex | 1 + 10 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 priv/repo/migrations/20200305171000_add_slug_to_companies.exs create mode 100644 priv/repo/migrations/20200305174544_add_slug_value_for_existing_companies.exs diff --git a/lib/companies/companies.ex b/lib/companies/companies.ex index 4b8ec85d..407ea9c0 100644 --- a/lib/companies/companies.ex +++ b/lib/companies/companies.ex @@ -107,21 +107,21 @@ defmodule Companies.Companies do ## Examples - iex> get!("Valid name") + iex> get!("Valid slug") %Company{} - iex> get!("Invalid name") + iex> get!("Invalid slug") ** (Ecto.NoResultsError) """ - def get_by_name!(name, opts \\ []) do + def get_by_slug!(slug, 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_by!(name: name) + |> Repo.get_by!(slug: slug) end @doc """ diff --git a/lib/companies/schema/company.ex b/lib/companies/schema/company.ex index 7cceb2c9..d6d2bc61 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] @@ -24,8 +26,23 @@ defmodule Companies.Schema.Company do @doc false def changeset(company, attrs) do + attrs = Map.merge(attrs, slug_map(attrs)) + 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]) end + + defp slug_map(%{"name" => name}) do + slug = + name + |> String.replace(~r/['’]s/u, "s") + |> String.downcase() + |> String.replace(~r/([^a-z0-9가-힣])+/, "-") + |> String.replace(" ", "-") + + %{"slug" => slug} + end + + defp slug_map(_attrs), do: %{} end diff --git a/lib/companies_web/controllers/company_controller.ex b/lib/companies_web/controllers/company_controller.ex index f0e3e870..de4ddc88 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, %{"name" => name}) do - company = Companies.get_by_name!(name, preloads: [:jobs, :industry]) + def show(conn, %{"slug" => slug}) do + company = Companies.get_by_slug!(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_by_slug!(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_by_slug!(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_by_slug!(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 35bf84d5..f83d2081 100644 --- a/lib/companies_web/router.ex +++ b/lib/companies_web/router.ex @@ -53,14 +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] - resources "/companies", CompanyController, only: [:show], param: "name" + 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/lib/companies_web/templates/company/card.html.eex b/lib/companies_web/templates/company/card.html.eex index 17ad00ba..ad8cd070 100644 --- a/lib/companies_web/templates/company/card.html.eex +++ b/lib/companies_web/templates/company/card.html.eex @@ -7,7 +7,7 @@ <% end %>
-

<%= link to: Routes.company_path(@conn, :show, locale(@conn), @company.name) do %><%= @company.name %><% end %>

+

<%= link to: Routes.company_path(@conn, :show, locale(@conn), @company) do %><%= @company.name %><% end %>

<%= if signed_in?(@conn) do %>
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..bbb57349 --- /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 index(:companies, [:slug], unique: true) + end +end diff --git a/priv/repo/migrations/20200305174544_add_slug_value_for_existing_companies.exs b/priv/repo/migrations/20200305174544_add_slug_value_for_existing_companies.exs new file mode 100644 index 00000000..2e4b1c1e --- /dev/null +++ b/priv/repo/migrations/20200305174544_add_slug_value_for_existing_companies.exs @@ -0,0 +1,23 @@ +defmodule Companies.Repo.Migrations.AddSlugValueForExistingCompanies do + use Ecto.Migration + + alias Companies.Repo + alias Companies.Schema.Company + + def change do + Repo.all(Company) + |> Enum.map(fn company -> + slug = + company.name + |> String.replace(~r/['’]s/u, "s") + |> String.downcase() + |> String.replace(~r/([^a-z0-9가-힣])+/, "-") + |> String.replace(" ", "-") + + changeset = Company.changeset(company, %{"slug" => slug}) + changeset = unless changeset.valid?, do: Company.changeset(company, %{"slug" => "#{slug}-2"}) + + Repo.update(changeset) + end) + 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 6c7c9fba..e6d1b422 100644 --- a/test/companies/companies_test.exs +++ b/test/companies/companies_test.exs @@ -85,31 +85,22 @@ defmodule Companies.CompaniesTest do end end - describe "get_by_name!/2" do + describe "get_by_slug!/2" do test "retrieves a company by it's name" do - %{name: name} = insert(:company, name: "ZULU") + %{id: id, slug: slug} = insert(:company, name: "ZULU") - assert %{name: ^name} = Companies.get_by_name!(name) + assert %{id: ^id} = Companies.get_by_slug!(slug) end test "preloads given associations" do company = insert(:company, name: "ZULU") - assert %{jobs: []} = Companies.get_by_name!(company.name, preloads: [:jobs]) + assert %{jobs: []} = Companies.get_by_slug!(company.slug, preloads: [:jobs]) end - test "raises for unknown name" do + test "raises for unknown slug" do assert_raise Ecto.NoResultsError, fn -> - Companies.get_by_name!("NONAME", preloads: [:jobs]) - end - end - - test "raises for multiple companies with same name" do - insert(:company, name: "ZULU") - insert(:company, name: "ZULU") - - assert_raise Ecto.MultipleResultsError, fn -> - Companies.get_by_name!("ZULU", preloads: [:jobs]) + Companies.get_by_slug!("invalid-slug", preloads: [:jobs]) 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