-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
The Query Object Pattern
Description
- Return Something / Change Nothing
- Request a collection of data
- File name describes the Query result
- Always returns an ActiveRecord chain-able result (unless Enumerable)
When to use it
- You have a complicated ActiveRecord chain
- You have filters, sorting, pagination, or other configurable arguments
- You need to use same query in multiple locations
- You need to utilize raw SQL
When not to use it (Anti-patterns)
- You need to produce a side-effect
- You have a simple query that could be expressed as a Model scope
Before
class PostsController < ApplicationController
def index
filters = params[:filter]
posts = current_user.organization.posts
posts = posts.where(category: filters[:category]) if filters[:category].present?
posts = posts.where(author: filters[:author]) if filters[:author].present?
posts = posts.where(publised_at: filters[:publised_at_start]..filters[:publised_at_end]) if filters[:publised_at_start].present? && filters[:publised_at_end].present?
posts = posts.order("#{params[:sort][:column]} #{params[:sort][:direction]}, created_at DESC") if params[:sort].present?
@pagy, @posts = pagy(posts, page: params[:pagination][:page], per_page: params[:pagination][:per_page])
end
endAfter
class PostsController < ApplicationController
def index
@pagy, @posts = FilteredPostsQuery.new(
scope: current_user.organization.posts,
filters: params[:filters],
sort: params[:sort],
pagination: params[:pagination]
).call
end
end
class FilteredPostsQuery < ApplicationQuery
attr_reader :scope, :filters, :sort, :pagination
def initialize(scope:, filters:, sort:, pagination:)
@scope = scope
@filters = filters
@sort = sort
@pagination = pagination
end
def call
result = filtered_posts(scope)
result = sorted_posts(result)
pagy(
result,
page: pagination[:page],
per_page: pagination[:per_page]
)
end
private
def filtered_posts(scope)
scope = scope.where(category: filters[:category]) if filters[:category].present?
scope = scope.where(author: filters[:author]) if filters[:author].present?
scope = scope.where(publised_at: filters[:publised_at_start]..filters[:publised_at_end]) if filters[:publised_at_start].present? && filters[:publised_at_end].present?
scope
end
def sorted_posts(result)
result.order("#{sort[:column]} #{sort[:direction]}, created_at DESC") if sort.present?
result
end
endMetadata
Metadata
Assignees
Labels
No labels