
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