Writing a Blog Engine in Phoenix and Elixir: Part 8, finishing comments

Brandon Richey
Mar 9, 2016 · 10 min read

Latest Update: 08/02/2016

Image for post
Image for post
Timmy better be trapped in a well! All of your comments on my blog are just “bark bark woof woof”!

Previous Post in this series

Current Versions

  • Elixir: v1.3.1
  • Phoenix: v1.2.0
  • Ecto: v2.0.2

Where We Left Off

When the last tutorial completed, we had a nice little starting UI going; we could add new comments to a post and we could get a listing of every comment on our post, but we still have some features we need to develop.

  1. We need to remove the “Approve/Delete” buttons if the user is not logged in or the creator of the post/an admin
  2. We need the “Approve/Delete” buttons to actually do something
  3. We should not show unapproved post if we’re not the authorized person for that post

We’ll start off by making sure our authorization model is all set, since that will dictate features 1 and 3.

Adding An Authorization Flag to Show

Open up web/controllers/post_controller.ex and down at the bottom you’ll see a lot of logic regarding authorization that we already have. Instead of adding even more logic, let’s refactor our authorization code a little bit and reuse it.

We’ll start by moving our user authorization check into a private function:

And then we’ll modify our existing authorize_user function to use this new function instead:

And we’ll quickly run our tests to make sure we didn’t break anything.

Wonderful! Next, we’ll need to add a new plug to set the authorization flag that we’ll be using on our template, reusing that handy helper function we just created:

We could verify this works in two ways:

  1. Start hooking all the code up in our templates and see what happens
  2. Start writing tests

Let’s write tests instead of testing things the manual way (hey, we need to write the tests anyways)!

Writing Tests For Our Authorization Check

We’ll get rid of our initial “Shows chosen resource” test and instead replace it with four other tests. We have four tests we want to write: when is_authorized_user? returns true for being an author (or for an admin), when it returns false for the wrong logged in user, and when it returns false for no logged in user. In test/controllers/post_controller_test.exs, we need to modify the setup block, add a logout function, and add some new tests. First, the setup block and helper functions:

And then add some tests for the new logic expectations:

And now we'll run our tests again to make sure everything is still good:

Wonderful! Now to start hooking up our UI since we know our new flag works as expected!

Hooking Up The Delete Button

First, let’s open up our Post Show template (web/templates/post/show.html.eex) and modify our listing of comments to pass our new author_or_admin flag:

Notice we had to do a lot in this render function. First, we don’t want to use @author_or_admin since we don’t necessarily have a guarantee those will be set. We’ll instead use @conn.assigns, which is safer. Next, since we’ll need to post to specific routes using the link helper, we’ll need to pass in both @conn and @post.

Now, each comment template will be able to read the new flag we created, so we’ll jump to that template next (web/templates/comment/comment.html.eex). We need to do two things here:

  1. If we’re the author or admin, display the comment. If we’re not, then don’t display anything. To do this, we’ll wrap our comment inside of an if check.
  2. If we’re the author or admin, then display the “Approve/Delete” buttons.

We’ll start with step 1. At the top of our template, add this line:

(Again, note the use of conn.assigns instead of just @value). At the bottom of our template, add:

Next, find the approve/delete buttons, and inside of the div they’re located in, modify it to the following:

So the final template should look like this:

Now try opening up a blog post display in two separate browsers; one logged in as a user, the other not logged in at all. You should see the unapproved posts and the buttons as an authorized user, but no unapproved posts nor buttons as an unauthorized user!

We’re very close to having complete functionality with our comments, so let’s hammer out the last piece of functionality: hooking up the approve/delete buttons!

Hooking Up Our Delete Button

To make our delete button work, we’ll need to make sure the Comments controller supports delete. Open up web/controllers/comment_controller.ex and scroll down to the delete function. Right now, it only returns conn, so we’ll need to change it to actually do something:

And let’s write a test to make sure we can delete comments. Open up test/controllers/comment_controller_test.exs. First, we’ll alias Pxblog.Comment if we haven’t already. Next, we’ll add a comment insert to our setup block:

And finally, we'll write the test itself:

And running our specs, everything should be green! All that’s left to do is hook up the button itself! Return to web/templates/comment/comment.html.eex and the code for the delete button should be replaced with:

There’s a lot here, so let’s step through it. First, we set up the text for our button (“Delete” in our case). Next, we tell Phoenix that we want this to use DELETE instead of GET for our link. We then tell it where to send the delete command (you can use mix phoenix.routes to get a list of all available route helpers, remember). We then specify our CSS classes, and set up a confirmation box when the button is clicked through the data dictionary list.

That’s it! Just go through and test the functionality yourself, and make sure your tests are still green, but we should be all done with our Delete button functionality!

Hooking Up The Approve Button

The approve button will be a bit more complicated to write. We need to provide a means of updating the “approved” flag. We’ll start off by writing the code for our update function in the Comment controller (web/controllers/comment_controller.ex):

And we’ll write a new test to cover updating the “approved” status to true in test/controllers/comment_controller_test.exs:

Rerun our tests and everything should be green! Now, we need to modify our original “Approve” button to send to the update function with approved set to true. Replace the button in web/templates/comment/comment.html.eex with the following lines:

This creates a new little form just for purposes of our update button. It sends via PUT, displays the form inline (instead of as a block), and sets the “approved” value to true via a hidden input. Mess around with it a bit and everything should be working just fine! Everything is near perfect, except technically ANYONE would be able to modify/delete/approve comments! We need to add authorizations into the comments controller to keep everything safe!

Adding Authorization To Comments Controller

We’ll want to copy the authorize_user and is_authorized_user? functions from the PostController into the CommentController (web/controllers/comment_controller.ex), but with some modifications. First, we want to make sure we’re actually working with the expected post, so we’ll change authorize_user to set_post_and_authorize_user:

And we’ll need to add a plug for statement for these at the top of our controller:

And run our tests. We’ll get some failures depending on whether we’re logging in before updating/deleting or not, so let’s modify our tests (test/controllers/comment_controller_test.exs):

And now we'll run our tests:

And that’s it! All of our buttons are hooked up and working, and we’re properly showing/hiding unapproved posts and actions when authorized or not!


We now have a fully-working comments section complete with mini admin panel! It’s not amazing and there are lots of other features we could start adding here, but this is a great baseline to iterate on. We’ll want to add our live commenting system (which we’ll tackle in the next post in this series), update the design of our blog engine (which will go into adding other dependencies and working with Brunch.io), and generally run around and clean up our code/refactor as appropriate (there is a ton of duplication with user authentication, for example)! We’ll also want to add a way for users to authenticate with outside systems and add functionality for RSS feeds, importing from other platforms, etc!

Join us for the next section of this tutorial where we’ll turn the commenting system into a live commenting system via Phoenix’s channels!

Next Post In This Series

Check out my new book!

Hey everyone! If you liked what you read here and want to learn more with me, check out my new book on Elixir and Phoenix web development:

I’m really excited to finally be bringing this project to the world! It’s written in the same style as my other tutorials where we will be building the scaffold of a full project from start to finish, even covering some of the trickier topics like file uploads, Twitter/Google OAuth logins, and APIs!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store