Home > Mobile >  How to simplify query
How to simplify query

Time:04-08

I have a controller method for searching and listing invoices, sales orders and homes based on params name, how to simplify this index method`

def index
      @so = QbwcSalesOrder.paginate(:page => params[:page], :per_page => 30)
      if params[:qs] == "Sales Orders"
        @so = @so.where("ref_number = ? ",params[:keyword]) if params[:keyword].present?
   elsif params[:qs] == "Invoices" 
        @so = QbwcInvoice.paginate(:page => params[:page], :per_page => 30)
        @so = @so.where("ref_number = ? ",params[:keyword])  if params[:keyword].present?
   elsif params[:qs] == "All Homes" 
        @so = QbwcHome.paginate(:page => params[:page], :per_page => 30)
   elsif params[:qs] == "Existing Homes" 
        @so = QbwcHome.where(record_type: "FromQB").paginate(:page => params[:page], :per_page => 30)
   end
 end

CodePudding user response:

First of all unify hash syntax and indentation, it's hard to read.

Second paginate once at the end of the index method, looks like you paginate always the same way.

Third think about scopes or query object https://mkdev.me/en/posts/how-to-use-query-objects-to-refactor-rails-sql-queries

Here is simplified version, you can use it in index or move to the query object:

def index
  @so = QbwcSalesOrder.all

  @so = @so.where("ref_number = ? ", params[:keyword]) if params[:keyword] && params[:qs] == "Sales Orders"
  @so = @so.where("ref_number = ? ", params[:keyword]) if params[:keyword] && params[:qs] == "Invoices"
  @so = @so.where(record_type: "FromQB") if params[:qs] == "Existing Homes" 
  @so = @so if params[:qs] == "All Homes" 

  @so = @so.paginate(page: params[:page], per_page: 30)
end

CodePudding user response:

To me this looks like the method is doing way to much and needs to be split into managable parts:

def index
  @so = qs_model_scope(params[:qs])
          .paginate(page: params[:page], per_page: 30)
          .then do |scope|
            filter_by_keyword(scope)
          end
end

# ...

private

def qs_model_scope(qs)
  case qs
  when "Invoices":
    QbwcInvoice.all
  when "All Homes":
    # Smelly - should be a model scope
    QbwcHome.where(record_type: "FromQB")
  else
    QbwcSalesOrder.all
  end
end 

def filter_by_keyword(scope)
  if params[:keyword].present? && ["QbwcSalesOrder","QbwcInvoice"].include?(scope.model.name)
    # no need to use a SQL string
    scope.where(ref_number: params[:keyword])   
  else
    scope
  end
end

If the complexity of this continues to grow you should consider moving this filtering process out of the controller and into a separate object (such as a form object or service object or just a PORO) so that it can be tested in isolation.

CodePudding user response:

I'd split the logic into separate methods. As already mentioned in another answer it makes sense to move it to a query object or something.

def index
  scope = invoices || all_homes || existing_homes || sales_orders
  @so = scope.paginate(:page => params[:page], :per_page => 30)
end

private

def invoices
  return if params[:qs] != 'Invoices'
  return QbwcInvoice if params[:keyword].blank?
  QbwcInvoice.where(ref_number: params[:keyword])
end

def all_homes
  QbwcHome if params[:qs] == 'All Homes'
end

def existing_homes
  QbwcHome.where(record_type: 'FromQB') if params[:qs] == 'Existing Homes'
end

def sales_orders
  if params[:qs] == 'Sales Orders' && params[:keyword].present?
    QbwcSalesOrder.where(ref_number: params[:keyword])
  else
    QbwcSalesOrder
  end
end
  • Related