Skip to content

Commit

Permalink
Support custom primary key field
Browse files Browse the repository at this point in the history
  • Loading branch information
tfwright committed Jan 21, 2024
1 parent 6a91f05 commit 737f2af
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 39 deletions.
2 changes: 2 additions & 0 deletions dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ defmodule Demo.Posts.Post do

import Ecto.Changeset

@primary_key {:post_id, :id, autogenerate: true}
@derive {Phoenix.Param, key: :post_id}
schema "posts" do
field :title, :string
field :body, :string
Expand Down
4 changes: 2 additions & 2 deletions dev/initdb/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ create table user_profiles (
);

create table posts (
id serial,
post_id serial,
user_id uuid,
disabled_user_id uuid,
title text NOT NULL,
Expand Down Expand Up @@ -63,7 +63,7 @@ create table alt.user_profiles (
);

create table alt.posts (
id serial,
post_id serial,
user_id uuid,
disabled_user_id uuid,
title text NOT NULL,
Expand Down
7 changes: 7 additions & 0 deletions lib/live_admin.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ defmodule LiveAdmin do
def fetch_config(resource, key, config),
do: Keyword.get(resource.__live_admin_config__, key) || Keyword.fetch!(config, key)

def primary_key!(resource) do
[key] = Keyword.fetch!(resource.__live_admin_config__(), :schema).__schema__(:primary_key)

key
end

def route_with_params(assigns, parts \\ []) do
resource_path = parts[:resource_path] || assigns.key

Expand Down Expand Up @@ -173,6 +179,7 @@ defmodule LiveAdmin do
resource
|> fetch_config(:label_with, config)
|> case do
nil -> Map.fetch!(record, LiveAdmin.primary_key!(resource))
{m, f, a} -> apply(m, f, [record | a])
label when is_atom(label) -> Map.fetch!(record, label)
end
Expand Down
32 changes: 29 additions & 3 deletions lib/live_admin/components/container.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,9 @@ defmodule LiveAdmin.Components.Container do
def handle_params(params, uri, socket = %{assigns: %{live_action: :list, loading: false}}) do
socket =
socket
|> assign(page: String.to_integer(params["page"] || "1"))
|> assign(sort_attr: String.to_existing_atom(params["sort-attr"] || "id"))
|> assign(sort_dir: String.to_existing_atom(params["sort-dir"] || "asc"))
|> assign(search: params["s"])
|> assign_resource_info(uri)
|> assign_pagination_params(params)
|> assign_mod()
|> assign_repo()
|> assign_prefix(params)
Expand Down Expand Up @@ -329,4 +327,32 @@ defmodule LiveAdmin.Components.Container do

assign(socket, repo: repo, prefix_options: prefix_options)
end

defp assign_pagination_params(socket, params) do
params = Map.new(params, fn
{"sort-attr", val} -> {"sort_attr", val}
{"sort-dir", val} -> {"sort_dir", val}
pair -> pair
end)

types =
%{
page: :integer,
sort_attr: Ecto.ParameterizedType.init(Ecto.Enum, values: socket.assigns.resource |> LiveAdmin.Resource.fields(socket.assigns.config) |> Enum.map(fn {field, _, _} -> field end)),
sort_dir: Ecto.ParameterizedType.init(Ecto.Enum, values: [:asc, :desc])
}

defaults = %{
page: 1,
sort_attr: LiveAdmin.primary_key!(socket.assigns.resource),
sort_dir: :asc
}

params =
{defaults, types}
|> Ecto.Changeset.cast(params, Map.keys(types))
|> Ecto.Changeset.apply_action!(:update)

assign(socket, params)
end
end
32 changes: 15 additions & 17 deletions lib/live_admin/components/resource/form/search_select.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ defmodule LiveAdmin.Components.Container.Form.SearchSelect do
>
<%= hidden_input(@form, @field,
disabled: @disabled,
value: if(@selected_option, do: @selected_option.id),
value:
if(@selected_option,
do: Map.fetch!(@selected_option, LiveAdmin.primary_key!(@resource))
),
id: input_id(@form, @field) <> "_hidden"
) %>
<%= if @selected_option do %>
<a
href="#"
phx-click={JS.push("select", value: %{id: nil}, target: @myself, page_loading: true)}
phx-click={JS.push("select", value: %{key: nil}, target: @myself, page_loading: true)}
class="button__remove"
/>
<%= record_label(@selected_option, @resource, @config) %>
Expand All @@ -60,7 +63,7 @@ defmodule LiveAdmin.Components.Container.Form.SearchSelect do
items={
Enum.filter(
@options,
&(&1.id != input_value(@form, @field))
&(Map.fetch!(&1, LiveAdmin.primary_key!(@resource)) != input_value(@form, @field))
)
}
>
Expand All @@ -84,7 +87,7 @@ defmodule LiveAdmin.Components.Container.Form.SearchSelect do
href="#"
phx-click={
JS.push("select",
value: %{id: option.id},
value: %{key: Map.fetch!(option, LiveAdmin.primary_key!(@resource))},
target: @myself,
page_loading: true
)
Expand Down Expand Up @@ -117,35 +120,30 @@ defmodule LiveAdmin.Components.Container.Form.SearchSelect do
{:noreply, assign(socket, :options, options)}
end

def handle_event("select", %{"id" => id}, socket) do
def handle_event("select", %{"key" => key}, socket) do
socket =
socket
|> assign_selected_option(id)
|> assign_selected_option(key)
|> push_event("change", %{})

{:noreply, socket}
end

defp assign_selected_option(socket, id) when id in [nil, ""],
defp assign_selected_option(socket, key) when key in [nil, ""],
do: assign(socket, :selected_option, nil)

defp assign_selected_option(
socket = %{assigns: %{selected_option: %{id: selected_option_id}}},
id
socket = %{assigns: %{selected_option: %{key: selected_option_key}}},
key
)
when selected_option_id == id,
when selected_option_key == key,
do: socket

defp assign_selected_option(socket, id),
defp assign_selected_option(socket, key),
do:
assign(
socket,
:selected_option,
Resource.find!(
id,
socket.assigns.resource,
socket.assigns.prefix,
socket.assigns.repo
)
Resource.find!(key, socket.assigns.resource, socket.assigns.prefix, socket.assigns.repo)
)
end
2 changes: 1 addition & 1 deletion lib/live_admin/components/resource/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ defmodule LiveAdmin.Components.Container.Index do
<input
type="checkbox"
class="resource__select"
data-record-id={record.id}
data-record-key={Map.fetch!(record, LiveAdmin.primary_key!(@resource))}
phx-click={JS.dispatch("live_admin:toggle_select")}
/>
</div>
Expand Down
10 changes: 7 additions & 3 deletions lib/live_admin/components/resource/view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ defmodule LiveAdmin.Components.Container.View do
class="resource__action--danger"
data-confirm="Are you sure?"
phx-click={
JS.push("delete", value: %{id: @record.id}, page_loading: true, target: @myself)
JS.push("delete",
value: %{key: Map.fetch!(@record, LiveAdmin.primary_key!(@resource))},
page_loading: true,
target: @myself
)
}
>
<%= trans("Delete") %>
Expand Down Expand Up @@ -98,7 +102,7 @@ defmodule LiveAdmin.Components.Container.View do
@impl true
def handle_event(
"delete",
%{"id" => id},
%{"key" => key},
%{
assigns: %{
resource: resource,
Expand All @@ -108,7 +112,7 @@ defmodule LiveAdmin.Components.Container.View do
} = socket
) do
socket =
id
key
|> Resource.find!(resource, socket.assigns.prefix, socket.assigns.repo)
|> Resource.delete(resource, session, socket.assigns.repo, config)
|> case do
Expand Down
16 changes: 9 additions & 7 deletions lib/live_admin/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,27 @@ defmodule LiveAdmin.Resource do
end
end

def all(ids, resource, prefix, repo) do
def all(keys, resource, prefix, repo) do
key = LiveAdmin.primary_key!(resource)

resource.__live_admin_config__()
|> Keyword.fetch!(:schema)
|> where([s], s.id in ^ids)
|> where([s], field(s, ^key) in ^keys)
|> repo.all(prefix: prefix)
end

def find!(id, resource, prefix, repo) do
find(id, resource, prefix, repo) ||
def find!(key, resource, prefix, repo) do
find(key, resource, prefix, repo) ||
raise(Ecto.NoResultsError,
queryable: Keyword.fetch!(resource.__live_admin_config__(), :schema)
)
end

def find(id, resource, prefix, repo) do
def find(key, resource, prefix, repo) do
resource.__live_admin_config__()
|> Keyword.fetch!(:schema)
|> preload(^preloads(resource))
|> repo.get(id, prefix: prefix)
|> repo.get(key, prefix: prefix)
end

def delete(record, resource, session, repo, config) do
Expand Down Expand Up @@ -209,7 +211,7 @@ defmodule LiveAdmin.Resource do
|> Enum.into(%{})
|> Map.put_new(:page, 1)
|> Map.put_new(:sort_dir, :asc)
|> Map.put_new(:sort_attr, :id)
|> Map.put_new(:sort_attr, LiveAdmin.primary_key!(resource))

query =
resource.__live_admin_config__()
Expand Down
2 changes: 1 addition & 1 deletion lib/live_admin/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ defmodule LiveAdmin.Router do
|> Keyword.put_new(:create_with, Application.get_env(:live_admin, :create_with))
|> Keyword.put_new(:list_with, Application.get_env(:live_admin, :list_with))
|> Keyword.put_new(:update_with, Application.get_env(:live_admin, :update_with))
|> Keyword.put_new(:label_with, Application.get_env(:live_admin, :label_with, :id))
|> Keyword.put_new(:label_with, Application.get_env(:live_admin, :label_with))
|> Keyword.put_new(:title_with, Application.get_env(:live_admin, :title_with))
|> Keyword.put_new(:validate_with, Application.get_env(:live_admin, :validate_with))
|> Keyword.put_new(:hidden_fields, Application.get_env(:live_admin, :hidden_fields, []))
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ defmodule LiveAdmin.MixProject do
extras: ["README.md"],
source_ref: "v#{@version}"
],
compilers: Mix.compilers() ++ compilers(Mix.env())
compilers: Mix.compilers() ++ compilers(Mix.env()),
consolidate_protocols: !System.get_env("LIVE_ADMIN_DEV")
]
end

Expand Down
4 changes: 2 additions & 2 deletions test/live_admin/components/container_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ defmodule LiveAdmin.Components.ContainerTest do
describe "edit resource with plural embed with multiple entries" do
setup %{conn: conn} do
post = Repo.insert!(%Post{title: "test", previous_versions: [%Version{}, %Version{}]})
{:ok, view, html} = live(conn, "/live_admin_test_post/edit/#{post.id}")
{:ok, view, html} = live(conn, "/live_admin_test_post/edit/#{post.post_id}")
%{response: html, view: view}
end

Expand Down Expand Up @@ -292,7 +292,7 @@ defmodule LiveAdmin.Components.ContainerTest do
setup %{conn: conn} do
user = Repo.insert!(%User{})
post = Repo.insert!(%Post{title: "test", user: user})
{:ok, _, html} = live(conn, "/live_admin_test_post/#{post.id}")
{:ok, _, html} = live(conn, "/live_admin_test_post/#{post.post_id}")
%{response: html, user: user}
end

Expand Down
2 changes: 1 addition & 1 deletion test/live_admin_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule LiveAdminTest do

describe "record_label/2 when config uses mfa" do
assert 1 =
LiveAdmin.record_label(%LiveAdminTest.Post{id: 1}, LiveAdminTest.PostResource,
LiveAdmin.record_label(%LiveAdminTest.Post{post_id: 1}, LiveAdminTest.PostResource,
label_with: {LiveAdminTest.PostResource, :label, []}
)
end
Expand Down
4 changes: 3 additions & 1 deletion test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ defmodule LiveAdminTest.PostResource do

def run_action(_, _), do: {:ok, "worked"}

def label(post), do: post.id
def label(post), do: post.post_id
end

defmodule LiveAdminTest.Post do
use Ecto.Schema

@primary_key {:post_id, :id, autogenerate: true}
@derive {Phoenix.Param, key: :post_id}
schema "posts" do
field(:title, :string)
belongs_to(:user, LiveAdminTest.User, type: :binary_id)
Expand Down

0 comments on commit 737f2af

Please sign in to comment.