Reputation: 2988
I have the following associations:
class Certificate < ApplicationRecord
has_many :certificates_users
has_many :users, :through => :certificates_users
end
class CertificatesUser < ApplicationRecord
belongs_to :user
belongs_to :certificate
end
class User < ApplicationRecord
has_many :certificates_users, :dependent => :delete_all
has_many :certificates, :through => :certificates_users
end
I am trying to get a list of Certificates and then group it by the user. Basically, list out all users and show a sublist of their certificates under them. Here's how the UI looks like Just click on 'Mayor' on the table.
Here's the structure of the CertificatesUser
table
=> CertificatesUser(certificate_id: integer, user_id: integer, expiry_date: date, certificate_number: string, renewal_date: date, ispublic: integer)
Following this other answer, I was able to produce these possible solutions,
Certificate.joins(:users).group('users.id').select('users.id')
User.joins(:certificates).group('certificates.id').select('certificates.id, certificates.name')
The problem here is that most of the fields I need are on the certificates_users
not on the certificates
table.
This is another attempt at trying to get that to work, but did not work - CertificatesUser.group(:user_id).select('user.id')
What are other possible solutions to this?
Desired Output
Upvotes: 0
Views: 409
Reputation: 2988
I was able to get this to work by changing my approach and doing it the simplest way possible.
In the controller, I have this
@users_and_certificates = CertificatesUser.all.each_with_object({}) { |item, hash| hash[item.user_id] = item }
And then in the template, I query like this:
.card-body.table-responsive
%table.table.table-hover.table-valign-middle
%thead
%tr
%th Name
%th E-mail
%tbody
- @users_and_certificates.each do |user_id, certificate_object|
%tr{"data-target" => "#certificate_#{certificate_object.certificate_id}", "data-toggle" => "collapse"}
- user = User.find(user_id)
%td
= user.full_name
%td
= user.email
%tr
%td.card-body.table-responsive.collapse{'id' => "certificate_#{certificate_object.certificate_id}"}
%table.table.table-hover.table-valign-middle
%thead
%tr
%th Certificate
%th Certificate No.
%th Expiry Date
%th Certificate
%tbody
%tr
- certificate = Certificate.find(certificate_object.certificate_id)
%td
= certificate.name
%td
= certificate_object.certificate_number
%td
= certificate_object.expiry_date
%td Show
This works perfectly well for now. If anyone has a better approach, please don't hesitate to leave a comment or post an answer.
Thanks.
Upvotes: 0
Reputation: 2624
Based on relation define in your models, below should work:
CertificatesUser.joins(:user, :certificate)
.select('user_id, users.name, users.email, certificates.name')
.group_by(&:user_id)
You use .group_by(&:user_id)
to group the fetched data based on user_id in array of json like below:
Sample of result:
[1, [{ user_id: 1, certificate_id: 29, ....},{ user_id: 1, certificate_id: 22, ....}] ,
2, [{ user_id: 2, certificate_id: 34, ....},{ user_id: 2, certificate_id: 56, ....}] ,
3, [{ user_id: 3, certificate_id: 29, ....},{ user_id: 3, certificate_id: 12, ....}] ]
NB:
You can add fields from users or certificates as you like but make sure you use them in plural and not in the singular.
Upvotes: 0
Reputation: 3521
you can do
@users = User.includes(certificates_users: [:certificate])
then you can loop through them
@users.each do |u|
u.certificates_users.each do |cu|
cu.certificate_number
cu.certificate.some_column
...
end
end
to only get users with certificates
@users = User.includes(certificates_users: [:certificate]).where("certificates_users.user_id = users.id")
Upvotes: 3