I implemented a simple way to let users upvotes pins, But I would like to order the pins by the number of upvotes, here is my upvote system:
app/controllers/pins_controller.rb
def upvote
@pin = Pin.find(params[:id])
@pin.votes.create
redirect_to(pins_path)
end
app/models/vote.rb
class Vote < ActiveRecord::Base
belongs_to :pin
end
app/models/pin.rb
has_many :votes, dependent: :destroy
And my routes.rb looks like:
resources :pins do
member do
post 'upvote'
end
en
So I would like my app/views/pins/index.html.erb
to show the pins by most upvoted to less upvoted.
I know that acts_as_votable provide a system to do that, but as I did not use that gem to implement my vote system, I am not sure it's a good idea to use it.
What do you think?
My app/views/pins/index.html.erb is as following:
<% @pins.each do |pin| %>
<div class="box panel panel-default">
<%= image_tag pin.image.url(:medium) %>
<div class="panel-body">
<p class="startupinfo">
<%= pin.startupname %><span class="reward"><i class="fa fa-heart"></i> <%= pin.reward %></p></span>
<p class="startupname"><%= pin.description %></p>
<p class="startuptag"><i class="fa fa-map-marker turquoise"></i> <%= pin.tag %> <i class="fa fa-tags turquoise"></i> <%= pin.city %><%= link_to upvote_pin_path(pin), method: :post do %> <i class="fa fa-chevron-circle-up fa-1x" style="color:#28c3ab"></i> <% end %>
<%= pluralize(pin.votes.count, "upvote") %></p>
<p class="users-edit">
<%= link_to pin_path(pin), class: "btn btn-success" do %> Tweet <% end %>
<% if pin.user == current_user %>
<%= link_to 'Edit', edit_pin_path(pin) %>
<%= link_to 'Delete', pin, method: :delete, data: { confirm: 'Are you sure?' } %></p>
<% end %>
</div>
</div>
<% end %>
BASED ON BALOO ANSWER:
I ran:
rails generate migration add_votescount_to_pins votes_count:integer
rails generate migration add_countercache_to_votes counter_cache:integer
I updated manually the migration _add_votescount_to_pins.rb so it looks like:
class AddVotescountToPins < ActiveRecord::Migration
def change
add_column :pins, :votes_count, :integer
Pin.reset_column_information
Pin.includes(:votes).all.each do |pin|
Pin.update_counters(pin.id, :votes_count => pin.votes.size)
end
end
I ran:
rake db:migrate
Then I added the following line into my pins.controller.rb :
def index
@pins = Pin.all
@pins = Pin.order(votes_count: :desc)
end
Now my schema.rb file looks like this:
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20140704125839) do
create_table "pins", force: true do |t|
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.string "tag"
t.string "reward"
t.string "startupname"
t.string "city"
t.string "tweet"
t.string "logo"
t.string "website"
t.string "rewardcode"
t.string "logo_file_name"
t.string "logo_content_type"
t.integer "logo_file_size"
t.datetime "logo_updated_at"
t.integer "votes_count"
end
add_index "pins", ["user_id"], name: "index_pins_on_user_id"
create_table "startups", force: true do |t|
t.string "name"
t.text "description"
t.text "location"
t.text "tag"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.string "provider"
t.string "uid"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
create_table "votes", force: true do |t|
t.integer "pin_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "counter_cache"
end
end
But my pin index still doesn't order by number of upvotes ! Any ideas?
So when you down vote it destroys?
You can add votes_count
integer column to pins then add a counter_cache
to the vote.
class Vote < ActiveRecord::Base
belongs_to :pin, counter_cache: true
end
This will increment votes_count on a pin as someone votes for it.
Then simply query based on that
@pins = Pin.order(votes_count: :desc)
In your migration you'll want to update votes_count based on the existing votes.
add_column :pins, :votes_count, :integer, default: true
Pin.reset_column_information
Pin.all.each do |pin|
Pin.reset_counters(pin.id, :votes)
end