mix phoenix.gen.model
. mix phoenix.gen.model Comment comments author:string body:text approved:boolean post_id:references:posts
mix ecto.migrate
web/models/comment.ex
you can see that comments are already associated with posts, but there is not enough connection in the opposite direction.web/models/post.ex
file: has_many :comments, Pxblog.Comment
mix test
command, everything should still be green! Now let's check the link between posts and comments.test/support/factory.ex
file and add the Comment factory. Write this line to the top of the file, following the rest of the aliases: alias Pxblog.Comment
def comment_factory do %Comment{ author: "Test User", body: "This is a sample comment", approved: false, post: build(:post) } end
test/models/comment_test.exs
and add the following code to it: import Pxblog.Factory # ... test "creates a comment associated with a post" do comment = insert(:comment) assert comment.post_id end
web/router.ex
: resources "/posts", PostController, only: [] do resources "/comments", CommentController, only: [:create, :delete, :update] end
only: []
. Then add comment resources to allow them to create, delete and update them. :create
- to add comments by unauthorized users (created by unconfirmed). :delete
- allows the author of the post and administrators to delete comments, and :update
- approve them for display to the public.web/controllers/comment_controller.ex
file: defmodule Pxblog.CommentController do use Pxblog.Web, :controller end
defmodule Pxblog.CommentView do use Pxblog.Web, :view end
create
, update
and delete
. def create(conn, _), do: conn def update(conn, _), do: conn def delete(conn, _), do: conn
$ mkdir web/templates/comment
web/templates/comment/form.html.eex
: <%= form_for @changeset, @action, fn f -> %> <%= if @changeset.action do %> <div class="alert alert-danger"> <p>Oops, something went wrong! Please check the errors below.</p> </div> <% end %> <div class="form-group"> <%= label f, :author, class: "control-label" %> <%= text_input f, :author, class: "form-control" %> <%= error_tag f, :author %> </div> <div class="form-group"> <%= label f, :body, class: "control-label" %> <%= textarea f, :body, class: "form-control", id: "body-editor" %> <%= error_tag f, :body %> </div> <div class="form-group"> <%= submit "Submit", class: "btn btn-primary" %> </div> <% end %>
web/templates/post/show.html.eex
file, to which we will add a link to this form. Please note that in this template we use two variables @changeset
and @action
. We will come back to this later in the web/controllers/post_controller.ex
. Now let's continue working with the template. After the list of post attributes, add the following line: <%= render Pxblog.CommentView, "form.html", changeset: @comment_changeset, action: post_comment_path(@conn, :create, @post) %>
CommentView
, so we CommentView
pass the name to the first argument in the render
call. We need to transfer there @comment_changeset
(which we have not yet determined, but we will do it soon) and @action
is the way to send comments.web/controllers/post_controller.ex
and make everything work. Change the show function according to the code: def show(conn, %{"id" => id}) do post = Repo.get!(assoc(conn.assigns[:user], :posts), id) comment_changeset = post |> build_assoc(:comments) |> Pxblog.Comment.changeset() render(conn, "show.html", post: post, comment_changeset: comment_changeset) end
web/controllers/comment_controller.ex
file) and fill the contents with the create function. Right in front of the functions, add the following code: alias Pxblog.Comment alias Pxblog.Post plug :scrub_params, "comment" when action in [:create, :update]
def create(conn, %{"comment" => comment_params, "post_id" => post_id}) do post = Repo.get!(Post, post_id) |> Repo.preload([:user, :comments]) changeset = post |> build_assoc(:comments) |> Comment.changeset(comment_params) case Repo.insert(changeset) do {:ok, _comment} -> conn |> put_flash(:info, "Comment created successfully!") |> redirect(to: user_post_path(conn, :show, post.user, post)) {:error, changeset} -> render(conn, Pxblog.PostView, "show.html", post: post, user: post.user, comment_changeset: changeset) end end
build_assoc
function to create a linked scheme, which is determined by the atom. In our case, an associated comment
is created. The result, together with comment_params, is passed to the Comment.changeset function. The rest of the work is standard with one exception.Pxblog.PostView
), the template for the render, and all the variables used in the template: @post
, @user
, and @comment_changeset
. Now you can test: if you send a comment with errors, you will see a list of them directly on the page. If there are no errors when sending a comment, you will receive a blue flash message at the top of the page. We are making progress!web/templates/comment/comment.html.eex
and fill it with the following: <div class="comment"> <div class="row"> <div class="col-xs-4"> <strong><%= @comment.author %></strong> </div> <div class="col-xs-4"> <em><%= @comment.inserted_at %></em> </div> <div class="col-xs-4 text-right"> <%= unless @comment.approved do %> <button class="btn btn-xs btn-primary approve">Approve</button> <% end %> <button class="btn btn-xs btn-danger delete">Delete</button> </div> </div> <div class="row"> <div class="col-xs-12"> <%= @comment.body %> </div> </div> </div>
show
function from the web/controllers/post_controller.ex
immediately after the line with the receipt of posts: post = Repo.get!(assoc(conn.assigns[:user], :posts), id) |> Repo.preload(:comments)
web/templates/post/show.html.eex
file and add a template section that displays comments: <div class="comments"> <h2>Comments</h2> <%= for comment <- @post.comments do %> <%= render Pxblog.CommentView, "comment.html", comment: comment %> <% end %> </div>
test/controllers/comment_controller_test.exs
and proceed: defmodule Pxblog.CommentControllerTest do use Pxblog.ConnCase import Pxblog.Factory @valid_attrs %{author: "Some Person", body: "This is a sample comment"} @invalid_attrs %{} setup do user = insert(:user) post = insert(:post, user: user) {:ok, conn: build_conn(), user: user, post: post} end test "creates resource and redirects when data is valid", %{conn: conn, post: post} do conn = post conn, post_comment_path(conn, :create, post), comment: @valid_attrs assert redirected_to(conn) == user_post_path(conn, :show, post.user, post) assert Repo.get_by(assoc(post, :comments), @valid_attrs) end test "does not create resource and renders errors when data is invalid", %{conn: conn, post: post} do conn = post conn, post_comment_path(conn, :create, post), comment: @invalid_attrs assert html_response(conn, 200) =~ "Oops, something went wrong" end end
@valid_attrs
and @invalid_attrs
in the same way as we did before. Add a setup block, inside which we set up a default user and a post to work with.Source: https://habr.com/ru/post/318790/
All Articles