Ciao a tutti.
Oggi vorrei mostrare come implementare un contatore di visualizzazioni generico per un qualsiasi modello della vostra applicazione Rails.
Supponiamo di avere un model News e di volere tenere traccia di quante volte la singola news è stata visualizzata, in modo da implementare box del tipo "le piu' viste" ecc.
Supponiamo di avere un model News creato in questo modo:
myapp/db/migrate/001_create_news.rb:
class CreateNews < ActiveRecord::Migration
def self.up
create_table :news do |t|
t.string :title
t.text :content
t.date :online_date_start, :null => true
t.date :online_date_end, :null => true
t.boolean :online, :null => false, :default => true
t.timestamps
end
end
def self.down
drop_table :news
end
end
myapp/app/models/news.rb:
class News < ActiveRecord::Base
validates_length_of :title, :within => 2..255
validates_presence_of :title, :content, :online_date_start
end
L'idea di base è quella di incrementare un contatore di visualizzazioni ogni volta che un utente visualizza la news.
Alcune considerazioni:
un'implementazione di questo tipo permetterebbe ad un utente di premere il tasto refresh ed incrementare continuamente il contatore.
Quindi l'obiettivo è quello di avere un meccanismo intelligente che incrementi il contatore:
- 1 sola volta per ogni singolo utente registrato
- 1 sola volta per IP nel caso di guest
class CreateViewings < ActiveRecord::Migration
def self.up
create_table :viewings do |t|
t.string :ip
t.string :viewable_type
t.integer :viewable_id
t.references :person
t.datetime :created_at
end
end
def self.down
drop_table :viewings
end
end
e il modello corrispondente:
myapp/app/models/viewing.rb:
class Viewing < ActiveRecord::Base
# RELATIONSHIPS
belongs_to :viewable, :polymorphic => true, :counter_cache => :popularity
belongs_to :viewer, :class_name => "Person", :foreign_key => "person_id"
# VALIDATIONS
validates_uniqueness_of :ip, :scope => [:viewable_id, :viewable_type, :person_id]
validates_uniqueness_of :person_id, :allow_nil => true
# OTHER
def viewable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
end
Come si può notare, il model Viewing non è strettamente legato al model News, ma, data la sua natura polimorphic, può essere associato ad un qualsiasi model.
A questo punto, possiamo associare il model News a Viewing:
myapp/app/models/news.rb:
class News < ActiveRecord::Base
validates_length_of :title, :within => 2..255
validates_presence_of :title, :content, :online_date_start
has_many :viewings, :as => :viewable
end
e aggiungiamo anche la colonna "counter cache" alla tabella News:
myapp/db/migrate/003_add_popularity_to_news.rb:
class AddPopularityToNews < ActiveRecord::Migration
def self.up
add_column :news, :popularity, :integer, :default => 0
end
def self.down
remove_column :news, :popularity
end
end
A questo punto, abbiamo tutto il necessario per applicare il meccanismo di viewing al nostro model News.
Mostriamo come incrementare il contatore allo "show" della news.
myapp/app/controllers/news_controller.rb:
class NewsController < ApplicationController
after_filter :record_view, only => :show
def show
@news = News.find(params[ :id ])
end
private
def record_view
@news.viewings.create(:ip => request.remote_ip, :viewer => current_user) unless @news.nil?
end
end