+2

Lưu các trang đã xem trên cookie trong Rails

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí