add many_to_many relation for channel<->note
This commit is contained in:
parent
370bdfe99d
commit
127757b12a
|
@ -1,19 +0,0 @@
|
|||
defmodule Chiya.Channels.ChannelNote do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "channels_notes" do
|
||||
|
||||
field :channel, :id
|
||||
field :note, :id
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(channel_note, attrs) do
|
||||
channel_note
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
|
@ -8,6 +8,8 @@ defmodule Chiya.Notes do
|
|||
|
||||
alias Chiya.Notes.Note
|
||||
|
||||
@preloads [:channels]
|
||||
|
||||
@doc """
|
||||
Returns the list of notes.
|
||||
|
||||
|
@ -18,9 +20,19 @@ defmodule Chiya.Notes do
|
|||
|
||||
"""
|
||||
def list_notes do
|
||||
Repo.all(Note)
|
||||
Repo.all(Note) |> Repo.preload(@preloads)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Preloads a note
|
||||
|
||||
## Examples
|
||||
|
||||
iex> preload_note(note)
|
||||
%Note{}
|
||||
"""
|
||||
def preload_note(note), do: Repo.preload(note, @preloads)
|
||||
|
||||
@doc """
|
||||
Gets a single note.
|
||||
|
||||
|
@ -37,6 +49,22 @@ defmodule Chiya.Notes do
|
|||
"""
|
||||
def get_note!(id), do: Repo.get!(Note, id)
|
||||
|
||||
@doc """
|
||||
Gets a single note and preloads it.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Note does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_note_preloaded!(123)
|
||||
%Note{}
|
||||
|
||||
iex> get_note_preloaded!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_note_preloaded!(id), do: Repo.get!(Note, id) |> preload_note()
|
||||
|
||||
@doc """
|
||||
Creates a note.
|
||||
|
||||
|
|
|
@ -10,7 +10,11 @@ defmodule Chiya.Notes.Note do
|
|||
field :slug, :string
|
||||
field :url, :string
|
||||
|
||||
many_to_many :channels, Chiya.Channels.Channel, join_through: "channels_notes"
|
||||
many_to_many :channels, Chiya.Channels.Channel,
|
||||
# join_through: Chiya.Channels.ChannelNote,
|
||||
join_through: "channels_notes",
|
||||
join_keys: [note: :id, channel: :id],
|
||||
on_replace: :delete
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
@ -18,7 +22,9 @@ defmodule Chiya.Notes.Note do
|
|||
@doc false
|
||||
def changeset(note, attrs) do
|
||||
note
|
||||
|> Chiya.Notes.preload_note()
|
||||
|> cast(attrs, [:name, :content, :slug, :published_at, :kind, :url])
|
||||
|> put_assoc(:channels, attrs["channels"] || [])
|
||||
|> validate_required([:name, :content, :slug, :published_at, :kind, :url])
|
||||
|> unique_constraint(:slug)
|
||||
end
|
||||
|
|
|
@ -11,10 +11,12 @@ defmodule ChiyaWeb.NoteController do
|
|||
|
||||
def new(conn, _params) do
|
||||
changeset = Notes.change_note(%Note{})
|
||||
render(conn, :new, changeset: changeset)
|
||||
render(conn, :new, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
|
||||
def create(conn, %{"note" => note_params}) do
|
||||
note_params = from_channel_ids(note_params)
|
||||
|
||||
case Notes.create_note(note_params) do
|
||||
{:ok, note} ->
|
||||
conn
|
||||
|
@ -22,7 +24,7 @@ defmodule ChiyaWeb.NoteController do
|
|||
|> redirect(to: ~p"/notes/#{note}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :new, changeset: changeset)
|
||||
render(conn, :new, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,13 +34,14 @@ defmodule ChiyaWeb.NoteController do
|
|||
end
|
||||
|
||||
def edit(conn, %{"id" => id}) do
|
||||
note = Notes.get_note!(id)
|
||||
note = Notes.get_note_preloaded!(id)
|
||||
changeset = Notes.change_note(note)
|
||||
render(conn, :edit, note: note, changeset: changeset)
|
||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
|
||||
def update(conn, %{"id" => id, "note" => note_params}) do
|
||||
note = Notes.get_note!(id)
|
||||
note_params = from_channel_ids(note_params)
|
||||
note = Notes.get_note_preloaded!(id)
|
||||
|
||||
case Notes.update_note(note, note_params) do
|
||||
{:ok, note} ->
|
||||
|
@ -47,7 +50,7 @@ defmodule ChiyaWeb.NoteController do
|
|||
|> redirect(to: ~p"/notes/#{note}")
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, :edit, note: note, changeset: changeset)
|
||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -59,4 +62,17 @@ defmodule ChiyaWeb.NoteController do
|
|||
|> put_flash(:info, "Note deleted successfully.")
|
||||
|> redirect(to: ~p"/notes")
|
||||
end
|
||||
|
||||
defp from_channel_ids(note_params) do
|
||||
selected_ids = Enum.map(note_params["channels"] || [], &String.to_integer/1)
|
||||
|
||||
selected_channels =
|
||||
Chiya.Channels.list_channels()
|
||||
|> Enum.filter(fn c -> Enum.member?(selected_ids, c.id) end)
|
||||
|
||||
Map.put(note_params, "channels", selected_channels)
|
||||
end
|
||||
|
||||
defp to_channel_options(items \\ nil),
|
||||
do: Enum.map(items || Chiya.Channels.list_channels(), fn c -> {c.name, c.id} end)
|
||||
end
|
||||
|
|
|
@ -8,6 +8,9 @@ defmodule ChiyaWeb.NoteHTML do
|
|||
"""
|
||||
attr :changeset, Ecto.Changeset, required: true
|
||||
attr :action, :string, required: true
|
||||
attr :channels, :list, required: true
|
||||
|
||||
def note_form(assigns)
|
||||
|
||||
def selected_channels(changeset), do: Enum.map(changeset.data.channels, fn c -> c.id end)
|
||||
end
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.note_form changeset={@changeset} action={~p"/notes/#{@note}"} />
|
||||
<.note_form changeset={@changeset} action={~p"/notes/#{@note}"} channels={@channels} />
|
||||
|
||||
<.back navigate={~p"/notes"}>Back to notes</.back>
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.note_form changeset={@changeset} action={~p"/notes"} />
|
||||
<.note_form changeset={@changeset} action={~p"/notes"} channels={@channels} />
|
||||
|
||||
<.back navigate={~p"/notes"}>Back to notes</.back>
|
||||
|
|
|
@ -8,6 +8,14 @@
|
|||
<.input field={f[:published_at]} type="datetime-local" label="Published at" />
|
||||
<.input field={f[:kind]} type="text" label="Kind" />
|
||||
<.input field={f[:url]} type="text" label="Url" />
|
||||
<.input
|
||||
field={f[:channels]}
|
||||
type="select"
|
||||
label="Channel"
|
||||
multiple={true}
|
||||
options={@channels}
|
||||
value={selected_channels(@changeset)}
|
||||
/>
|
||||
<:actions>
|
||||
<.button>Save Note</.button>
|
||||
</:actions>
|
||||
|
|
|
@ -10,4 +10,4 @@ defmodule Chiya.Repo.Migrations.AddObanJobsTable do
|
|||
def down do
|
||||
Oban.Migration.down(version: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,8 +3,8 @@ defmodule Chiya.Repo.Migrations.CreateChannelsNotes do
|
|||
|
||||
def change do
|
||||
create table(:channels_notes, primary_key: false) do
|
||||
add :channel, references(:channels, on_delete: :nothing)
|
||||
add :note, references(:notes, on_delete: :nothing)
|
||||
add :channel, references(:channels, on_delete: :delete_all)
|
||||
add :note, references(:notes, on_delete: :delete_all)
|
||||
end
|
||||
|
||||
create index(:channels_notes, [:channel])
|
||||
|
|
|
@ -2,7 +2,7 @@ defmodule Chiya.AccountsTest do
|
|||
use Chiya.DataCase
|
||||
|
||||
import Chiya.AccountsFixtures
|
||||
|
||||
|
||||
alias Chiya.Accounts
|
||||
alias Chiya.Accounts.{User, UserToken}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
defmodule Chiya.ChannelsTest do
|
||||
use Chiya.DataCase
|
||||
|
||||
|
||||
import Chiya.ChannelsFixtures
|
||||
|
||||
alias Chiya.Channels
|
||||
alias Chiya.Channels.Channel
|
||||
|
||||
describe "channels" do
|
||||
|
||||
|
||||
@invalid_attrs %{content: nil, name: nil, slug: nil, visibility: nil}
|
||||
|
||||
test "list_channels/0 returns all channels" do
|
||||
|
@ -22,7 +20,12 @@ defmodule Chiya.ChannelsTest do
|
|||
end
|
||||
|
||||
test "create_channel/1 with valid data creates a channel" do
|
||||
valid_attrs = %{content: "some content", name: "some name", slug: "some slug", visibility: :public}
|
||||
valid_attrs = %{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: "some slug",
|
||||
visibility: :public
|
||||
}
|
||||
|
||||
assert {:ok, %Channel{} = channel} = Channels.create_channel(valid_attrs)
|
||||
assert channel.content == "some content"
|
||||
|
@ -37,7 +40,13 @@ defmodule Chiya.ChannelsTest do
|
|||
|
||||
test "update_channel/2 with valid data updates the channel" do
|
||||
channel = channel_fixture()
|
||||
update_attrs = %{content: "some updated content", name: "some updated name", slug: "some updated slug", visibility: :private}
|
||||
|
||||
update_attrs = %{
|
||||
content: "some updated content",
|
||||
name: "some updated name",
|
||||
slug: "some updated slug",
|
||||
visibility: :private
|
||||
}
|
||||
|
||||
assert {:ok, %Channel{} = channel} = Channels.update_channel(channel, update_attrs)
|
||||
assert channel.content == "some updated content"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Chiya.NotesTest do
|
||||
use Chiya.DataCase
|
||||
|
||||
|
||||
import Chiya.NotesFixtures
|
||||
|
||||
alias Chiya.Notes
|
||||
|
@ -16,11 +16,18 @@ defmodule Chiya.NotesTest do
|
|||
|
||||
test "get_note!/1 returns the note with given id" do
|
||||
note = note_fixture()
|
||||
assert Notes.get_note!(note.id) == note
|
||||
assert Notes.get_note_preloaded!(note.id) == note
|
||||
end
|
||||
|
||||
test "create_note/1 with valid data creates a note" do
|
||||
valid_attrs = %{content: "some content", kind: "some kind", name: "some name", published_at: ~N[2023-03-04 16:22:00], slug: "some slug", url: "some url"}
|
||||
valid_attrs = %{
|
||||
content: "some content",
|
||||
kind: "some kind",
|
||||
name: "some name",
|
||||
published_at: ~N[2023-03-04 16:22:00],
|
||||
slug: "some slug",
|
||||
url: "some url"
|
||||
}
|
||||
|
||||
assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs)
|
||||
assert note.content == "some content"
|
||||
|
@ -37,7 +44,15 @@ defmodule Chiya.NotesTest do
|
|||
|
||||
test "update_note/2 with valid data updates the note" do
|
||||
note = note_fixture()
|
||||
update_attrs = %{content: "some updated content", kind: "some updated kind", name: "some updated name", published_at: ~N[2023-03-05 16:22:00], slug: "some updated slug", url: "some updated url"}
|
||||
|
||||
update_attrs = %{
|
||||
content: "some updated content",
|
||||
kind: "some updated kind",
|
||||
name: "some updated name",
|
||||
published_at: ~N[2023-03-05 16:22:00],
|
||||
slug: "some updated slug",
|
||||
url: "some updated url"
|
||||
}
|
||||
|
||||
assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs)
|
||||
assert note.content == "some updated content"
|
||||
|
@ -51,7 +66,7 @@ defmodule Chiya.NotesTest do
|
|||
test "update_note/2 with invalid data returns error changeset" do
|
||||
note = note_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Notes.update_note(note, @invalid_attrs)
|
||||
assert note == Notes.get_note!(note.id)
|
||||
assert note == Notes.get_note_preloaded!(note.id)
|
||||
end
|
||||
|
||||
test "delete_note/1 deletes the note" do
|
||||
|
|
|
@ -3,8 +3,18 @@ defmodule ChiyaWeb.ChannelControllerTest do
|
|||
|
||||
import Chiya.ChannelsFixtures
|
||||
|
||||
@create_attrs %{content: "some content", name: "some name", slug: "some slug", visibility: :public}
|
||||
@update_attrs %{content: "some updated content", name: "some updated name", slug: "some updated slug", visibility: :private}
|
||||
@create_attrs %{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: "some slug",
|
||||
visibility: :public
|
||||
}
|
||||
@update_attrs %{
|
||||
content: "some updated content",
|
||||
name: "some updated name",
|
||||
slug: "some updated slug",
|
||||
visibility: :private
|
||||
}
|
||||
@invalid_attrs %{content: nil, name: nil, slug: nil, visibility: nil}
|
||||
|
||||
describe "index" do
|
||||
|
|
|
@ -3,8 +3,22 @@ defmodule ChiyaWeb.NoteControllerTest do
|
|||
|
||||
import Chiya.NotesFixtures
|
||||
|
||||
@create_attrs %{content: "some content", kind: "some kind", name: "some name", published_at: ~N[2023-03-04 16:22:00], slug: "some slug", url: "some url"}
|
||||
@update_attrs %{content: "some updated content", kind: "some updated kind", name: "some updated name", published_at: ~N[2023-03-05 16:22:00], slug: "some updated slug", url: "some updated url"}
|
||||
@create_attrs %{
|
||||
content: "some content",
|
||||
kind: "some kind",
|
||||
name: "some name",
|
||||
published_at: ~N[2023-03-04 16:22:00],
|
||||
slug: "some slug",
|
||||
url: "some url"
|
||||
}
|
||||
@update_attrs %{
|
||||
content: "some updated content",
|
||||
kind: "some updated kind",
|
||||
name: "some updated name",
|
||||
published_at: ~N[2023-03-05 16:22:00],
|
||||
slug: "some updated slug",
|
||||
url: "some updated url"
|
||||
}
|
||||
@invalid_attrs %{content: nil, kind: nil, name: nil, published_at: nil, slug: nil, url: nil}
|
||||
|
||||
describe "index" do
|
||||
|
@ -38,6 +52,21 @@ defmodule ChiyaWeb.NoteControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "create note with channel" do
|
||||
setup [:create_channels]
|
||||
|
||||
test "redirects to show when selecting a channel", %{conn: conn, channel: channel} do
|
||||
attrs = Map.put_new(@create_attrs, :channels, [to_string(channel.id)])
|
||||
conn = post(conn, ~p"/notes", note: attrs)
|
||||
|
||||
assert %{id: id} = redirected_params(conn)
|
||||
assert redirected_to(conn) == ~p"/notes/#{id}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{id}")
|
||||
assert html_response(conn, 200) =~ "Note #{id}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit note" do
|
||||
setup [:create_note]
|
||||
|
||||
|
@ -64,6 +93,28 @@ defmodule ChiyaWeb.NoteControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "update note with channel" do
|
||||
setup [:create_note, :create_channels]
|
||||
|
||||
test "adds and removes the correct channels from the note", %{
|
||||
conn: conn,
|
||||
note: note,
|
||||
channel: channel,
|
||||
channel2: channel2
|
||||
} do
|
||||
attrs = Map.put_new(@update_attrs, :channels, [to_string(channel.id)])
|
||||
conn = put(conn, ~p"/notes/#{note}", note: attrs)
|
||||
assert redirected_to(conn) == ~p"/notes/#{note}"
|
||||
|
||||
attrs = Map.put_new(@update_attrs, :channels, [to_string(channel2.id)])
|
||||
conn = put(conn, ~p"/notes/#{note}", note: attrs)
|
||||
assert redirected_to(conn) == ~p"/notes/#{note}"
|
||||
|
||||
conn = get(conn, ~p"/notes/#{note}")
|
||||
assert html_response(conn, 200) =~ "some updated content"
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete note" do
|
||||
setup [:create_note]
|
||||
|
||||
|
@ -81,4 +132,10 @@ defmodule ChiyaWeb.NoteControllerTest do
|
|||
note = note_fixture()
|
||||
%{note: note}
|
||||
end
|
||||
|
||||
defp create_channels(_) do
|
||||
channel = Chiya.ChannelsFixtures.channel_fixture()
|
||||
channel2 = Chiya.ChannelsFixtures.channel_fixture()
|
||||
%{channel: channel, channel2: channel2}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,11 @@ defmodule Chiya.ChannelsFixtures do
|
|||
entities via the `Chiya.Channels` context.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Generate a unique note slug.
|
||||
"""
|
||||
def unique_channel_slug, do: "some slug#{System.unique_integer([:positive])}"
|
||||
|
||||
@doc """
|
||||
Generate a channel.
|
||||
"""
|
||||
|
@ -13,7 +18,7 @@ defmodule Chiya.ChannelsFixtures do
|
|||
|> Enum.into(%{
|
||||
content: "some content",
|
||||
name: "some name",
|
||||
slug: "some slug",
|
||||
slug: unique_channel_slug(),
|
||||
visibility: :public
|
||||
})
|
||||
|> Chiya.Channels.create_channel()
|
||||
|
|
Loading…
Reference in New Issue