Friday

November 22nd , 2024

FOLLOW US

HOW TO CREATE YOUR OWN PHOENIX NEWS TO EARN MONEY ?

featured img


How to Build Elixir Blog App With Phoenix in Less Than 15min

 

 

Okay, let’s get real for a second. No-one can actually build a production-ready blog application from scratch in less than 15 minutes. That’s not how the world works. However, creating a working prototype — that’s totally possible.

So here is the plan — we are gonna spend the next 15min or so building a prototype of a blog application that meets the following criteria:

User can create, update, display and delete posts

User can add comments to a post

User can see all the comments for a particular post

User can see how many comments does each post have

A small side note — this blog post is inspired by an article I read a couple of days back — “Elixir Blog in 15 Minutes Using Phoenix Framework — Step By Step Tutorial” written by Jakub Cie?lar. Since the article was published way back in 2015, I felt it will be nice to get an updated version that properly reflects the current state of Elixir and Phoenix.

STEP 0: Prerequisites

The Phoenix Framework has a fantastic set of installation docs. I suggest you follow these and you’ll be set up and ready to go in no time.

Another small side note — if you are planning to explore and work with different versions of Elixir in the future, you might want to install Elixir (and Erlang) via asdf.

STEP 1: Create the blog application

First of all, meet your new best friend — Mix. This is an Elixir build tool that provides a basic set of tasks to help create and manage Elixir apps.

To create a new application from scratch, all you need to do is open a new terminal window and enter the following command:

mix phx.new blog

Halfway through the installation process you will be asked if you want to fetch and install the project dependancies — I suggest you go ahead and do so.

* creating blog/config/config.exs

* creating blog/config/dev.exs

* creating blog/config/prod.exs

...

* creating blog/assets/css/phoenix.css

* creating blog/assets/static/images/phoenix.png

* creating blog/assets/static/robots.txt

Fetch and install dependencies? [Yn]

If you choose not to install the dependancies, you must make sure to run the below mentioned mix tasks in order to complete your setup process. Otherwise, you won’t be able to start your application.

* running mix deps.get

* running mix deps.compile

* running cd assets && npm install && node node_modules/webpack/bin/webpack.js --mode development

And that’s it — you now have a working application. Well… not just yet. There are still a couple of extra things you need to take care of first.

Check the dev.ex and test.ex files that have been created under the config folder of your project. Make sure that the database username and password match the ones you have setup on your local machine.

config :blog, Blog.Repo,

username: "postgres",

password: "postgres",

database: "blog_dev",

hostname: "localhost",

show_sensitive_data_on_connection_error: true,

pool_size: 10

You need to create the database of your application

$ mix ecto.create

The database for Blog.Repo has been created

And now it’s done. You can go ahead and start the server using the following command:

$ mix phx.server

 

By default your app will be running on http://localhost:4000

STEP 2: Setting up the Post resource

Phoenix provides us out of the box with a neat set of generators — mix tasks that are responsible for setting up all the modules we need to spin up a resource in a fast and efficient manner.

To create a new Post resource we are gonna use phx.gen.html — it will generate the controller, views and context for us.

mix phx.gen.html Posts Post posts title:string body:text

Basically, we just told mix that we want to create a new resource named Post that has context Posts and a database table posts that has two fields — title of type string and body of type text.

Next, we need to go to the Blog.Router module and add the route definitions for our new resource:

resources "/posts", PostController

Adding the new Post resource has generated a new database migration. To persist the changes in the database, we must run the following command:

$ mix ecto.migrate

Now we can start the server via mix phx.server and see the new changes by hitting the http://localhost:4000/posts route.

 

STEP 3: Adding Comments to a Post

To create a new Comment resource we are gonna use phx.gen.context — another phoenix generator that will help us to setup a context with some functions around an ecto schema.

mix phx.gen.context Comments Comment comments name:string content:text post_id:references:posts

A closer look at the command above tells us that we have used the generator to define the relationship between Post and Comment by adding the post_id field to the comments table.

However, we still need to manually define the association between the two schemas. To do that we will use the functions — belongs_to and has_many.

If you want to know more about them, you can check the Ecto.Schema docs.

A small side note — all of the code samples in this blog post are using aliases to substitute the full module names. That’s why writing belongs_to(:post, Post) is equal to belongs_to(:post, Blog.Posts.Post)

defmodule Blog.Comments.Comment do

use Ecto.Schema

import Ecto.Changeset

alias Blog.Posts.Post

schema "comments" do

field :content, :string

field :name, :string

belongs_to(:post, Post)

timestamps()

end

@doc false

def changeset(comment, attrs) do

comment

|> cast(attrs, [:name, :content, :post_id])

|> validate_required([:name, :content, :post_id])

end

end

defmodule Blog.Posts.Post do

use Ecto.Schema

import Ecto.Changeset

 

alias Blog.Comments.Comment

schema "posts" do

field :body, :string

field :title, :string

has_many :comments, Comment

 

timestamps()

end

@doc false

def changeset(post, attrs) do

post

|> cast(attrs, [:title, :body])

|> validate_required([:title, :body])

end

end

After making the above changes, don’t forget to run mix ecto.migrate ;)

Having properly set up our schema associations, we turn our attention to completing the task at hand — we want to enable a user to add a comment to a blog post from the UI.

First, we are gonna add a new route to our Blog.Router module:

resources "/posts", PostController do

post "/comment", PostController, :add_comment

end

Next, we are gonna add the add_comment action to our post_controller.ex

It will create a new comment in the database and associate it to a post.

In addition, it will load the post page and display the status of the create operation in a flash message.

def add_comment(conn, %{"comment" => comment_params, "post_id" => post_id}) do

post =

post_id

|> Posts.get_post!()

|> Repo.preload([:comments])

case Posts.add_comment(post_id, comment_params) do

{:ok, _comment} ->

conn

|> put_flash(:info, "Comment added :)")

|> redirect(to: Routes.post_path(conn, :show, post))

{:error, _error} ->

conn

|> put_flash(:error, "Comment not added :(")

|> redirect(to: Routes.post_path(conn, :show, post))

end

end

Personally, I am not a fan of big controller modules. That’s why I have chosen to put the add_comment function as part of Blog.Posts context module.

def add_comment(post_id, comment_params) do

comment_params

|> Map.put("post_id", post_id)

|> Comments.create_comment()

end

Next, we are going to implement a simple Add Comment form and add it to the page that displays a single post. For that to happen, we need to modify the show action in the post controller. We are going to add the Comment.changeset and pass it to the show.html template.

def show(conn, %{"id" => id}) do

post =

id

|> Posts.get_post!

|> Repo.preload([:comments])

changeset = Comment.changeset(%Comment{}, %{})

render(conn, "show.html", post: post, changeset: changeset)

end

After that we are going to create a new comment_form.html template that will be used to enter and save comment data.

<%= form_for @changeset, @action, fn f -> %>

<div class="form-group">

<label>Name</label>

<%= text_input f, :name, class: "form-control" %>

</div>

<div class="form-group">

<label>Content</label>

<%= textarea f, :content, class: "form-control" %>

</div>

 

<div class="form-group">

<%= submit "Add comment", class: "btn btn-primary" %>

</div>

<% end %>

Lastly, we need to add the comments_form.html template to the show.html and make sure to pass it the correct data:

<%= render "comments_form.html", post: @post, changeset: @changeset, action: Routes.post_post_path(@conn, :add_comment, @post) %>

 

http://localhost:4000/posts/<post_id>

STEP 4: Displaying all comments for a given post

To display all the comments of a blog post we will create a new template — comments.html

<h3>Comments:</h3>

<div class="comments">

<div class="comment header">

<div>Name</div>

<div>Content</div>

</div>

<%= for comment <- @comments do %>

<div class="comment">

<div><%= comment.name %></div>

<div><%= comment.content%></div>

</div>

<% end %>

</div>

We can pretty up the template by adding some css styles. These can be inserted into the main app.scss file.

.comments {

padding-bottom: 2em;

}

.comment {

display: grid;

grid-template-columns: 1fr 1fr;

padding: 0.5em;

border-bottom: 1px solid lightgrey;

}

.comment.header {

font-weight: bold;

}

Lastly, we need to insert the comments.html template into the show.html and make sure to pass it the correct data.

<%= render “comments.html”, comments: @post.comments %>

 

http://localhost:4000/post/<post_id>

STEP 5: Displaying number of comments per post

We can write a simple function to calculate the number of comments associated with a post and add it to the Blog.Posts context.

def get_number_of_comments(post_id) do

post = Posts.get_post!(post_id) |> Repo.preload([:comments])

Enum.count(post.comments)

end

Next we can call that function from the BlogWeb.PostView module in the following manner:

defmodule BlogWeb.PostView do

use BlogWeb, :view

alias Blog.Posts

def get_comments_count(post_id) do

Posts.get_number_of_comments(post_id)

end

end

Lastly we can extend the index.html template by adding a couple of lines of code that will help us to display the number of comments.

<th># of Comments</th>

...

<td><%= get_comments_count(post.id) %></td>

Total Comments: 0

Meet the Author


PC
Emmanuel Rapha

Content writer

follow me

INTERSTING TOPICS


Connect and interact with amazing Authors in our twitter community