QueryObject với Ruby on Rails
RubyOnRails
40
White

checkraiser viết ngày 18/04/2017

Giả sử bạn có một mô hình User has_many Courses through Subscribes

Bạn muốn tìm kiếm User dựa vào các query params user_name, course_title, has_subscribed, has_not_subscribed

Phiên bản 1: Xử lý tất cả ở controller User#index

user = params[:user_name].strip
    course = params[:course_name].strip
    subscribed = params[:subscribed]
    unsubscribed = params[:unsubscribed]
    if user.present? || course.present?
      # including all user name, course name
      # passing user name, course name and subscribed checkbox
      if user.present? && course.present? && subscribed.present? && !unsubscribed.present?
        @users = User.joins([:courses,:subscribes]).where("users.name LIKE ? AND courses.title LIKE ? AND subscribes.start_at is not null", "%#{user}%", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing user name, course name and unsubscirbed check box
      elsif user.present? && course.present? && unsubscribed.present? && !subscribed.present?
        @users = User.joins([:courses,:subscribes]).where("users.name LIKE ? AND courses.title LIKE ? AND (subscribes.start_at is null OR subscribes.end_at is not null)", "%#{user}%", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing user name, course name without any checkbox
      elsif user.present? && course.present?
        @users = User.joins(:courses).where("users.name LIKE ? AND courses.title LIKE ?", "%#{user}%", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      # only user name
      # passing user name, subscribed checkbox
      elsif !course.present? && !unsubscribed.present? && subscribed.present?
        @users = User.joins(:subscribes).where("users.name LIKE ? AND subscribes.start_at is not null", "%#{user}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing user name, unsubscribed checkbox
      elsif !course.present? && !subscribed.present? && unsubscribed.present?
        @users = User.joins(:subscribes).where("users.name LIKE ? AND (subscribes.start_at is null OR subscribes.end_at is not null)", "%#{user}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing only user name(without checkbox)
      elsif !course.present?
        @users = User.where("name LIKE ?", "%#{user}%").order("updated_at ASC").page(params[:page]).per(30)
      # only course name
      # passing course name, subscribed checkbox
      elsif !user.present? && !unsubscribed.present? && subscribed.present?
        @users = User.joins([:courses, :subscribes]).where("courses.title LIKE ? AND subscribes.start_at is not null", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing course name, unsubscribed checkbox
      elsif !user.present? && !subscribed.present? && unsubscribed.present?
        @users = User.joins([:courses, :subscribes]).where("courses.title LIKE ? AND (subscribes.start_at is null OR subscribes.end_at is not null)", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      # passing only course name(without checkbox)
      else
        @users = User.joins(:courses).where("courses.title LIKE ?", "%#{course}%").order("updated_at ASC").page(params[:page]).per(30)
      end
    else
      if subscribed.present? && !unsubscribed.present?
        @users = User.joins(:subscribes).where("subscribes.start_at is not null").order("updated_at ASC").page(params[:page]).per(30)
      elsif unsubscribed.present? && !subscribed.present?
        @users = User.joins(:subscribes).where("subscribes.start_at is null OR subscribes.end_at is not null").order("updated_at ASC").page(params[:page]).per(30)
      else
        @users = User.order("updated_at ASC").page(params[:page]).per(30)
      end

Phiên bản 2: Xây dựng một UserSearchQuery object

def index
    user = Services::UserSearchQuery.new
    @users = user.check_params(params[:user_name], params[:course_title], params[:has_subscribed], params[:has_not_subscribed]).page(params[:page]).per(30)
  end
class Services::UserSearchQuery
  def initialize(relation = User.all)
    @relation = relation.extending(Scopes)
  end
  def search
    @relation
  end
  def check_params(user_name, course_title, has_subscribed, has_not_subscribed)
    # only subscibed checked
    if has_subscribed.present? && !has_not_subscribed.present?
      # both user name and course title are passed
      if user_name.present? && course_title.present?
        @user = search.has_subscribed.user_name(user_name).course_title(course_title)
      # only user name is passed
      elsif user_name.present? && !course_title.present?
        @user = search.has_subscribed.user_name(user_name)
      # only course title is passed
      elsif !user_name.present? && course_title.present?
        @user = search.has_subscribed.course_title(course_title)
      # neither user name nor course title 
      else
        @user = search.has_subscribed
      end
    # only unsubsribed checked
    elsif !has_subscribed.present? && has_not_subscribed.present?
      # both user name and course title are passed
      if user_name.present? && course_title.present?
        @user = search.has_not_subscribed.user_name(user_name).course_title(course_title)
      # only user name is passed
      elsif user_name.present? && !course_title.present?
        @user = search.has_not_subscribed.user_name(user_name)
      # only course title is passed
      elsif !user_name.present? && course_title.present?
        @user = search.has_not_subscribed.course_title(course_title)
      # neither user name nor course title 
      else
        @user = search.has_not_subscribed
      end
    else
      # both user name and course title are passed
      if user_name.present? && course_title.present?
        @user = search.user_name(user_name).course_title(course_title)
      # only user name is passed
      elsif user_name.present? && !course_title.present?
        @user = search.user_name(user_name)
      # only course title is passed
      elsif !user_name.present? && course_title.present?
        @user = search.course_title(course_title)
      # neither user name nor course title
      else
        @user = search
      end
    end
    #@user.ranking
  end
  module Scopes
    def user_name(name)
      where("name LIKE ? ", name)
    end
    def course_title(title)
      joins(:courses).where("courses.title LIKE ?", title)
    end
    def has_subscribed
      joins(:subscribes).where.not("subscribes.start_at LIKE ?", nil)
    end
    def has_not_subscribed
      joins(:courses).where("courses.title LIKE ?", nil)
    end
    def ranking
      order("updated_at ASC")
    end
  end
end

Điểm cải thiện:

  • Code ở controller đã được rút gọn, dễ hiểu.
  • Tách phần scope ra module riêng để nhúng vào QueryObject

Cần cải thiện:

  • Code ở check_params vẫn quá phức tạp.

Phiên bản 3: overload setter

class Services::UserSearchQuery
  def initialize(relation = User.all)
    @user = relation.extending(Scopes)
  end

  def search
    @user
  end

  def subscribe
    @user = search.has_subscribed
  end

  def user_name=(value)
    @user = @user.user_name(value)
  end

  def course_title=(value)
    @user = @user.course_title(value)
  end

  def unsubscribe
    @user = search.has_not_subscribed
  end

  module Scopes
  end

end

module Scopes
    def user_name(name)
      where("name LIKE ? ", name)
    end

    def course_title(title)
      joins(:courses).where("courses.title LIKE ?", title)
    end

    def has_subscribed
      joins(:subscribes).where.not("subscribes.start_at LIKE ?", nil)
    end

    def has_not_subscribed
      joins(:courses).where("courses.title LIKE ?", nil)
    end

    def ranking
      order("updated_at ASC")
    end
  end

Lúc này code ở controller sử dụng overload setters:

def index
    user = Services::UserSearchQuery.new
    user.user_name = params[:user_name] if params[:user_name].present? 
    user.course_title = params[:course_title] if params[:course_title].present? 
    user.subscibe if subscibed?(params)
    user.unsubscribe unless subscibed?(params)
    @users = user.search.page(params[:page]).per(30)
  end
protected

def subscribed?(params)
      params[:has_subscribed].present? && !params[:has_subscribed]has_not_subscribed.present?
end
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

checkraiser

17 bài viết.
20 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
6 1
Tôi dự sẽ viết 1 loạt series về việc thiết kế 1 ứng dụng Rails như thế nào để nó có thể giúp bạn ăn ngon ngủ yên trong hằng năm trời: Khi mà việc t...
checkraiser viết hơn 2 năm trước
6 1
White
5 10
Cũng ngót nghét đi làm hơn 6 năm rồi, mình chỉ thấy một điều khá "ngược đời": Các ông chủ , những người trả tiền cho bạn lại có tư duy lập trình ké...
checkraiser viết 10 tháng trước
5 10
White
3 3
This post is a sample chapter from my (Link) How to render a ReactJS component isomorphically ? The trick is in defaultProps and componentDidM...
checkraiser viết 3 năm trước
3 3
Bài viết liên quan
White
8 1
Tiếp theo (Link) Mình sẽ hướng dẫn cách test căn bản cho API mình tạo. Thật ra mà nói thì mình phải viết test trước khi làm nhưng mà để tránh việc...
My Mai viết 3 năm trước
8 1
White
7 4
Nếu ai đã làm Rails thì không còn lạ lẫm gì với chức năng Migration của Rails. Nếu ai chưa biết thì tôi xin giới thiệu sơ qua chức năng này. Rails ...
Lơi Rệ viết hơn 3 năm trước
7 4
White
0 0
Performance regression in ActiveRecord model instance creation in Rails 4.1.x When you guys met the performance issue in Rails 4.1.x. And after be...
Luu Nguyen viết gần 2 năm trước
0 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
17 bài viết.
20 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!