Like / Follow / Vote in Phoenix (Elixir) Web App using Votex

PxSphere

“Like a photo”, “Up-vote response” or “Follow user” come under a basic set of operations one needs in every other web project. Managing a separate entity or handling the execution within the specific models can be a never ending debate. It is smart to leave external libraries take care of these rudimentary tasks while your focus should be on core activities.

Let’s demonstrate a simple blogger app. Start with creating an empty Phoenix project

mix phx.new blogger --no-brunch
cd blogger

It will create a boilerplate code base to start development with. Type “Y” to proceed installing dependencies.

Before creating a database, you need to change the config file so that it has the access to database.

nano config/dev.exs
config :blogger, Blogger.Repo,
adapter: Ecto.Adapters.Postgres,
username: "Your DB Username",
password: "Your DB Password",

Now, it is time to create the database and run the development server.

mix ecto.create
mix phx.server

On browsing http://localhost:4000, you’ll be greeted with the following screen.

Let’s quickly generate some models with the help of generators and migrate.

mix phx.gen.schema Blog blogs title:string views:integer
mix phx.gen.schema Team teams name:string category:string
mix phx.gen.schema User users username:string age:integer
mix ecto.migrate

Having three models at our disposal, we can play around for a while

iex -S mix  # Start Interactive Elixir Shell
%Blogger.User{username: "alex01", age: 25} |> Blogger.Repo.insert
%Blogger.Blog{title: "Elixir on Phoenix", views: 0} |> Blogger.Repo.insert
%Blogger.Team{name: "FYYT", category: "Football Players"} |> Blogger.Repo.insert

Add Votex as a dependency in root project and configure it

defp deps do [
{:votex, "~> 0.3.0"}
]
end

config/config.exs

config :votex, Votex.DB,
repo: Blogger.Repo

Install the newly included dependency and run migration

mix deps.get
mix votex.gen.migration
mix ecto.migrate

Modify the schema files of Ecto, which will expose the votable functions. The goal is to make Blog likeable by either a User or a Team.

In short,

User is a voter
Team is a voter
Blog is votable

lib/blogger/user.ex

defmodule Blogger.User do
use Votex.Voter # Add this line
end

lib/blogger/team.ex

defmodule Blogger.Team do
use Votex.Voter
end

lib/blogger/blog.ex

defmodule Blogger.Blog do
use Votex.Votable
end

Finally the magic methods can be executed now

iex -S mix  # Shell
alias Blogger.{User, Blog, Team, Repo}
user = Repo.all(User) |> Enum.at(0)
blog = Repo.all(Blog) |> Enum.at(0)
team = Repo.all(Team) |> Enum.at(0)
blog |> Blog.vote_by user
# {:ok, %Votex.Vote{}}
user |> User.voted_for? blog
# true
blog |> Blog.votes_for |> length
# 1
blog |> Blog.unvote_by user
# {:ok, %Votex.Vote{}}
user |> User.voted_for? blog
# false

Voter details can be fetched from blog

blog |> Blog.votes_for |> Enum.map(fn vote -> vote.voter end)
[
%Blogger.Team{
__meta__: #Ecto.Schema.Metadata<:loaded, "teams">,
category: "Football Players",
id: 1,
inserted_at: ~N[2018-09-01 20:06:46.827705],
name: "FYYT",
updated_at: ~N[2018-09-01 20:06:46.827720]
},
%Blogger.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
age: 25,
id: 1,
inserted_at: ~N[2018-09-01 19:49:40.815278],
updated_at: ~N[2018-09-01 19:49:40.818200],
username: "alex01"
}
]

Votex can also be used for self referential voting, e.g. A user can follow another user. Just need to declare User as votable too.

lib/blogger/user.ex

defmodule Blogger.User do
use Votex.Voter
use Votex.Votable
end

Now a user can also follow another user

user1 = user
{_, user2} = %User{username: "brian17", age: 20} |> Repo.insert
user2 |> User.vote_by user1
# {:ok, _}

user1 |> User.votes_for
[
%{
__meta__: #Ecto.Schema.Metadata<:loaded, "votex_votes">,
__struct__: Votex.Vote,
id: 4,
inserted_at: ~N[2018-09-01 20:24:59.768915],
updated_at: ~N[2018-09-01 20:24:59.768942],
votable_id: 3,
votable_type: "users",
voter: %Blogger.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
age: 25,
id: 1,
inserted_at: ~N[2018-09-01 19:49:40.815278],
updated_at: ~N[2018-09-01 19:49:40.818200],
username: "alex01"
},
voter_id: 1,
voter_type: "users"
}
]

That’s all for now. I hope this covers majority of the use cases an app might need. I also happen to be the creator of Votex so would appreciate feedback. You can check out Votex at Github. It is directly inspired from Acts as Votable in Ruby. Please do clap if you find this useful.

Like what you read? Give Raman Sah a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.