Search code examples
sqlruby-on-railsactiverecordchaining

which rails query/chain is better?


I have a rails app with the models below. I have both assigned_tasks and executed_tasks for a given user. I would like to know which option is better for getting all the tasks (executed and assigned as well) for that given user.

task.rb

belongs_to :assigner, class_name: "User"
belongs_to :executor, class_name: "User"

user.rb

has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy

Solution 1:

task.rb

scope :completed, -> { where.not(completed_at: nil) }
scope :uncompleted, -> { where(completed_at: nil) }

user.rb

def tasks_uncompleted
  tasks_uncompleted = assigned_tasks.uncompleted.order("deadline DESC")
  tasks_uncompleted += executed_tasks.uncompleted.order("deadline DESC")
  tasks_uncompleted.sort_by { |h| h[:deadline] }.reverse!
end

tasks_controller:

@tasks = current_user.tasks_uncompleted.paginate(page: params[:page], per_page: 12)

Solution 2:

task.rb

scope :completed, -> { where.not(completed_at: nil) }
scope :uncompleted, -> { where(completed_at: nil) }
scope :alltasks, -> (u) { where('executor_id = ? OR assigner_id = ?', u.id, u.id) }

tasks_controller

@tasks = Task.alltasks(current_user).uncompleted.order("deadline DESC").paginate(page: params[:page], per_page: 12)

Solution

  • You should define an association on User that will return all of the Tasks associated by either executor_id or assigner_id:

    class User < ActiveRecord::Base
      has_many :assigned_and_executed_tasks,
               ->(user) { where('executor_id = ? OR assigner_id = ?', user, user) },
               class_name: 'Task',
               source: :tasks
    end
    
    user = User.find(123)
    user.assigned_and_executed_tasks
    # => SELECT tasks.* FROM tasks WHERE executor_id = 123 OR assigner_id = 123;
    

    Then you can do as you do in "Solution 2," but instead of the unfortunate Task.alltasks(current_user) you can just do current_user.assigned_and_executed_tasks (of course you could give it a shorter name, but descriptive names are better than short ones):

    @tasks = current_user.assigned_and_executed_tasks
               .uncompleted
               .order("deadline DESC")
               .paginate(page: params[:page], per_page: 12)