Lưu các trang đã xem trên cookie trong Rails
Bài đăng này đã không được cập nhật trong 7 năm
Các web developer thường xuyên gặp phải những tình huống mà họ cần lưu trữ một thứ gì đó, đặc biệt là lưu thứ gì đó dạng collection để sử dụng tạm thời, ví dụ một vài trang user đã vào xem trước đó. Tôi đã từng thấy nhiều người tạo hẳn một bảng trong CSDL chỉ để lưu những trang user đã ghé qua trong trường hợp không cần thiết, đó thật sự là một cách làm tệ hại khi mỗi lần truy cập vào 1 trang lại phải insert 1 record vào DB. Thậm chí có người sử dụng session để lưu, thật là một sự lãng phí.
Vấn đề có thể được giải quyết với cookies. Trong bài viết này mình sẽ chỉ ra một cách để "che đậy" cookies và làm sao để tương tác với cookies như một collection một cách đơn giản nhất
Ý tưởng
Trước tiên, một đối tượng cookie collection sẽ phải làm được 2 công việc cơ bản là add new items và retrieve one/all item(s).
Mình sẽ lấy ví dụ là một web bán hàng trực tuyến, cần lưu lại các 10 sản phẩm người dùng vừa ghé xem.
Trong ví dụ này, ý tưởng của mình sẽ có 1 hàm lấy danh sách các sản phẩm vừa xem, và thêm sản phẩm mới mỗi khi gọi hàm show
trong products_controller
.
recent_products #=> Retrive all items.
recent_products.push @product #=> Add new item
Sử dụng cookies để lưu và lấy collection
Mình sẽ tránh việc gọi trực tiếp đến cookies hash, thay vào đó mình sẽ tương tác với 1 collection.
Với lớp CookieCollection
, chúng ta sẽ định nghĩa các phương thức để thêm/xóa item. Dĩ nhiên để làm việc này một cách dễ dàng thứ đầu tiên ta nên nghĩ đến đó là lớp Array
, có nghĩa là chúng ta sẽ kế thừa class Array
Ở đây để tối ưu về mặt hiệu suất, chúng ta chỉ lưu các id sản phẩm chứ không lưu cả object. Việc lưu và lấy các object sẽ do một lớp khác đảm nhiệm.
# lib/cookie_products/cookie_collection.rb
class CookieCollection < Array
attr_accessor :ids
LIFE_TIME = 10
def initialize cookies
@cookies = cookies
if @cookies[cookie_name].present?
self.ids = @cookies[cookie_name].split(',')
else
self.ids = Array.new
end
end
def push object
super object
update_cookie
end
private
def update_cookie
ids = map(&:id)
@cookies[cookie_name] = {
value: ids.join(','),
expires: LIFE_TIME.years.from_now
}
end
def cookie_name
self.class.name.parameterize
end
end
Xây dựng lớp lưu id sản phẩm các trang đã xem
Lớp RecentProducts
sẽ trực tiếp đẩy các sản phẩm vào cookie collection. Ở đây mình sẽ chỉ lưu 10 sản phẩm gần đây nhất.
# lib/cookie_products/recent_products.rb
class RecentProducts < CookieCollection
RECENT_PRODUCT_SIZE = 10
def initialize cookies
super cookies
self.ids = ids.last RECENT_PRODUCT_SIZE
ids.each {|product_id| push Product.find(product_id)}
end
def push product
delete product
while length > RECENT_PRODUCT_SIZE - 1
delete_at 0
end
super product
end
end
Thêm các hàm helpers
Trong application controller
mình sẽ tạo ra các helper methods để truy cập đến object collection, nhờ đó trên controllers và views có thể dễ dàng gọi các phương thức này.
# app/controllers/application_controller.rb
helper_method [:recent_products, :last_viewed_product]
def recent_products
@recent_products ||= RecentProducts.new cookies
end
def last_viewed_product
recent_products.reverse.second
end
Sử dụng trên views và controllers
Ví dụ về sử dụng trên products controller
# app/controllers/products_controller.rb
def show
@product = Product.find_by_id params[:id]
recent_products.push @product #=> Đẩy 1 product vừa xem vào collection
# Lấy toàn bộ danh sách
recent_products
end
Chú ý
Đừng quên khai báo đường dẫn eager load
đến file thư viện để rails load các lớp khi chạy ứng dụng, sau đó khởi động lại server.
# config/application.rb
config.eager_load_paths << Rails.root.join("lib/cookie_products")
Kết luận
Đây là cách làm khá đơn giản và hợp lý nhất trong trường hợp này.
Cách làm có thể áp dụng tương tự với session
, mình sẽ có một bài viết tương tự hướng dẫn trên làm việc trên session để quản lý giỏ hàng bằng session object collection trong thời gian tới.
All rights reserved