add many_to_many relation for channel<->note

This commit is contained in:
Inhji 2023-03-05 23:25:49 +01:00
parent 370bdfe99d
commit 127757b12a
16 changed files with 186 additions and 48 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -10,4 +10,4 @@ defmodule Chiya.Repo.Migrations.AddObanJobsTable do
def down do
Oban.Migration.down(version: 1)
end
end
end

View File

@ -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])

View File

@ -2,7 +2,7 @@ defmodule Chiya.AccountsTest do
use Chiya.DataCase
import Chiya.AccountsFixtures
alias Chiya.Accounts
alias Chiya.Accounts.{User, UserToken}

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()