Stuck on this one, if you could take a look.. :)
What I want is to get all unfinished projects, including all unfinished tasks for a certain user.
This is my setup so far:
User (devise)
has_one :employee
Employee
belongs_to :user
has_and_belongs_to_many :tasks
has_and_belongs_to_many :unfinished_tasks, :conditions => { :tasks => { :completed_at => nil } }, :class_name => "Task"
has_many :unfinished_projects, :through => :unfinished_tasks, :source => :project, :uniq => true ( :include => :unfinished_tasks OR :include => :tasks ? )
Project
has_many :tasks
Task
belongs_to :project
has_and_belongs_to_many :employees
In my view (haml) I'd like to have something like this:
- for project in current_user.employee.unfinished_projects
= project.name
# THESE ARE NOT THE ONLY THE TASKS FOR THE CURRENT_USER
- for task in project.tasks ( OR project.unfinished_tasks ? )
= task.name
This setup works for the projects, there are only projects which have unfinished tasks.
But I'm not sure how to include the unfinished tasks with these projects.
Anyone knows the best way for doing this, I'd like to have a single query for all this if that's possible.
EDIT: The tricky part is that the tasks have to be for the current_user. The projects are loaded perfectly.
But when it loads the tasks:
- for task in project.tasks.unfinished
It does this:
Task Load (1.3ms) SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` IN (12, 7, 13, 15, 14, 10, 16, 17, 9, 2, 3)
Task Load (0.4ms) SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 12 AND `tasks`.`completed_at` IS NULL
Task Load (0.3ms) SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 7 AND `tasks`.`completed_at` IS NULL
Task Load (0.6ms) SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 13 AND `tasks`.`completed_at` IS NULL
etc.
What it should do is get the tasks of the employee:
Employee
Projects
Tasks
Which should be the tasks that were inner joined in the projects query.
Assuming my question (above) is a true example of what you'd like to see.
you can use named_scopes to cover this.
For instance:
Task
named_scope :unfinished, :conditions => {:unfinished => true}
or whatever condition used to show that it's unfinished. Then you can use:
- for task in project.tasks.unfinished
The named_scope for "unfinished_projects" is similar but slightly more complex. It requires some bare SQL - which will depend on the db that you use, but generally requires that you join the tasks on and find the project sht at have one unfinished task. I'm guessing this might come close to what you need:
Project
named_scope :unfinished,
:joins => 'inner join tasks on tasks.project_id = projects.id',
:conditions => ['tasks.unfinished IS TRUE']
You would then use it as above:
- for project in current_user.employee.projects.unfinished
For one that only finds projects that have unfinished tasks for a user , you can add the user as a parameter to the scope eg:
Project
named_scope :unfinished_for_user, lambda {|the_user_id|
:joins => 'inner join tasks on tasks.project_id = projects.id',
:conditions => ['tasks.unfinished IS TRUE AND tasks.user_id = ?', the_user_id]
}
So the answer to my question is as follows:
The models:
Employee:
has_and_belongs_to_many :tasks
has_many :projects, :through => :tasks, :uniq => true
Project:
has_many :tasks
scope :unfinished, :include => :tasks, :conditions => { :tasks => { :completed_at => nil } }
Task:
scope :unfinished, :conditions => { :completed_at => nil }
scope :of_user, lambda { |user| { :include => :employees, :conditions => { :employees => { :id => user.employee.id } } } }
The view:
- for project in current_user.employee.projects.unfinished
- for task in project.tasks.unfinished.of_user( current_user )
I find it VERY annoying that Project.unfinished already includes the incomplete tasks.
WHY do I need to get these again, gives me a new query for each project! Should be unnecessary right?
So, after doing this all over again I didn't really find what my problem was. There are a few things changed now: I'm using scopes, includes for eager load are in the scope, and no scope on the project.tasks itself.
The project model:
has_many :tasks
scope :assigned_to, lambda { |employee| { :include => { :tasks => :employees }, :conditions => { :tasks => { :employees => { :id => employee.id } } } } }
scope :unfinished, :include => :tasks, :conditions => { :tasks => { :completed_at => nil } }
The view:
- for project in Project.assigned_to( current_user.employee ).unfinished
= project.name
- for task in project.tasks
= task.name
Gives me this nice MySQL-query:
SELECT `projects`.`id` AS t0_r0, `projects`.`name` AS t0_r1, `projects`.`description` AS t0_r2, `projects`.`completed_at` AS t0_r3, `projects`.`created_at` AS t0_r4, `projects`.`updated_at` AS t0_r5, `tasks`.`id` AS t1_r0, `tasks`.`name` AS t1_r1, `tasks`.`description` AS t1_r2, `tasks`.`estimated_duration` AS t1_r3, `tasks`.`calculated_duration` AS t1_r4, `tasks`.`status` AS t1_r5, `tasks`.`completed_at` AS t1_r6, `tasks`.`project_id` AS t1_r7, `tasks`.`created_at` AS t1_r8, `tasks`.`updated_at` AS t1_r9, `employees`.`id` AS t2_r0, `employees`.`alias` AS t2_r1, `employees`.`comments` AS t2_r2, `employees`.`user_id` AS t2_r3, `employees`.`created_at` AS t2_r4, `employees`.`updated_at` AS t2_r5 FROM `projects` LEFT OUTER JOIN `tasks` ON `tasks`.`project_id` = `projects`.`id` LEFT OUTER JOIN `employees_tasks` ON `employees_tasks`.`task_id` = `tasks`.`id` LEFT OUTER JOIN `employees` ON `employees`.`id` = `employees_tasks`.`employee_id` WHERE `employees`.`id` = 3 AND `tasks`.`completed_at` IS NULL
Works like a charm!
Special thanks to Taryn.. ;)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.