Reputation: 83
I'm building a Rails application for to manage a services company scheduling.
For the scope of this question, the model structure is:
Client Has Many Contracts
Contracts Has Many Services
Workers Has Many Services
In my query i have a worker and a client, and i need to produce a
@contracts relationship
that match all contracts that have in common the client and the worker.
I've tried in lot of ways, but if i produce recursively the @contracts list via select{} and a loop, i obtain an array that is not an ActiveRecord and it cannot be used in the rails app (for example in erb).
I'm stuck :/
Upvotes: 1
Views: 64
Reputation: 11082
Assuming you have the proper has_many
and belongs_to
relationships setup, your query would probably look something like this:
Contract.joins(services: :worker)
.where(services: {worker_id: target_worker_id})
.where(contracts: {client_id: target_client_id}).uniq
where target_worker_id
and target_client_id
are the worker and clients you wish to filter by.
This query will join contracts
to the services
table (because you said "Contracts has many Services"), which also joins the services
table to the workers
table (because you said "Workers has many services"). In short, it appears that services
is a join table, the result of a many-to-many relationship, hence the join clause.
Edit: Note the .uniq
, which is necessary to ensure only distinct Contract
objects are loaded. Otherwise, as you experienced, the inner join caused by the .join
method will essentially create a collection of duplicates.
Let me know if I've misunderstood anything, or if you have additional questions on this answer.
Quick note: You wrote i obtain an array that is not an ActiveRecord and it cannot be used in the rails app (for example in erb). I'm not entirely clear what you mean, but any instance variable created in the controller can be used in ERB; it doesn't need to be an active record object. As stated though, I'm not sure if this is what you meant.
Upvotes: 1
Reputation: 3881
class Client < ActiveRecord::Base
has_many :conctracts, inverse_of: client, dependent: :destroy
end
class Contract < ActiveRecord::Base
has_many :services
end
class Worker < ActiveRecord::Base
has_many :services
end
class Service < ActiveRecord::Base
belongs_to :worker
belongs_to :contract
has_many :clients, through: contract
scope :workers, ->(*w) {
where(worker_id: w.flatten.compact.uniq)
}
scope :contracts, ->(*c) {
where(contract_id: c.flatten.compact.uniq)
}
end
workers = Worker.all
clients = Client.join(contracts: {services: worker}).merge(Service.workers(workers))
Upvotes: 0