Counting how many objects belong to a parent parent in rails - ruby-on-rails

Basically I have 3 different models
class Zone < ApplicationRecord
has_many :providers
end
class Provider < ApplicationRecord
has_many :referrals
belongs_to :zone
end
class Referrals < ApplicationRecord
belongs_to :provider
end
Referrals belong to Providers which then belongs to a Zone. Now the problem im having is counting how many referrals from a zone?
this is the erb tag I tried
<% #zones.each do |z| %>
<li style="color: green">
<%= link_to z.name, z %>
<% z.providers.each do |p| %>
<%= p.referrals.count %>
<% end %>
</li>
<% end %>
But that counts the referrals from each provider and displays it. I want it to just show the total only?

class Zone < ApplicationRecord
has_many :providers
has_many :referrals, through: :providers
end
This will let you do...
z.referrals.count

Related

Querying and linking to from join tables

Two Questions:
1) I have a retreats/:id view where I can display the team names that are affixed to a specific retreat. I can view the team names with the following query in the view:
<p>Teams: <%= #retreat.teams.pluck(:name).to_sentence %></p>
However, instead of just displaying the name, how would I both display the name of the team and link to the team team/:id
2) In this retreats/:id view, I would also like to display the users that are part of a team, but I am really stuck trying to go through sql joins, etc.
models
retreat.rb
class Retreat < ApplicationRecord
belongs_to :user
delegate :name, to: :user, prefix: true
belongs_to :account
validates :name, presence: true
has_many :retreat_teams
has_many :teams, through: :retreat_teams
accepts_nested_attributes_for :retreat_teams
end
team.rb
class Team < ApplicationRecord
belongs_to :account
has_many :team_members
has_many :users, through: :team_members
accepts_nested_attributes_for :team_members
has_many :retreats
has_many :retreats, through: :retreat_teams
end
team_members.rb
class TeamMember < ApplicationRecord
belongs_to :team
belongs_to :user
end
First part can be done this way
<% retreat.teams.each do |team| %>
<%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
<% end %>
Second part, you can run this query instead
#teams = Team.where(retreat_id: #retreat.id).includes(:users)
Then in UI you can show like this
<% #teams.each do |team| %>
Team: <%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
Team Users: <%= team.users.pluck(:name).to_sentence %>
<% end %>
Hope that helps!
1) I have a retreats/:id view where I can display the team names that
are affixed to a specific retreat. ...However, instead of just
displaying the name, how would I both display the name of the team and
link to the team team/:id
Don't use .pluck unless you actually want just a single column as an array or the raw data from several columns without instantiating model instances. .pluck is both overused and misused. It makes zero sense to pluck something if you then need to fetch the rest of the columns later anyways.
Instead just iterate though the model instances:
<% #resort.teams.each do |team| %>
<%= link_to team.name, team %>
<% end %>
If you have declared the route with resources :teams. Rails will figure out the route all by itself - that's the power of convention over configuration.
2) In this retreats/:id view, I would also like to display the users
that are part of a team, but I am really stuck trying to go through
sql joins, etc.
You don't really have to do any work joining. Just eager_load the association to avoid n+1 queries:
def show
#resort = Resort.eager_load(teams: :users).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.users.any? %>
<ul>
<%= team.users.each do |user| %>
<li><%= link_to user.name, user %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>
Another note about naming
The name TeamMember is unfortunate as it implies that its the actual person that's a member and not just a join model.
Membership or Position are better name choices.
class Team
has_many :memberships
has_many :members, through: :memberships
end
class Membership
belongs_to :team
belongs_to :member, class_name: 'User'
end
class User
has_many :memberships, foreign_key: :member_id
has_many :teams, through: :memberships
end
This will let you iterate through team.members and actually get the users instead of some join model. The above example would read after refactoring:
def show
#resort = Resort.eager_load(teams: :members).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.members.any? %>
<ul>
<%= team.members.each do |member| %>
<li><%= link_to member.name, member %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>

I want to get the data from many to many table

I want to get the data from many to many relationship table (Tag-Service-Category) like this below in tag/show.html.erb.
class Tag < ActiveRecord::Base
has_many :service_tags
has_many :services, through: :service_tags
end
class ServiceTag < ActiveRecord::Base
belongs_to :service
belongs_to :tag
end
class Service < ActiveRecord::Base
has_many :service_tags
has_many :tags, through: :service_tags
has_many :service_categories
has_many :categories, through: :service_categories
end
class ServiceCategory < ActiveRecord::Base
belongs_to :service
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :service_categories
has_many :services, through: :service_categories
end
I wrote the code like this, but it`s not working.
#tag = Tag.find(params[:id])
<% #tag.services.each do |service| %>
<% service.categories.each do |category| %>
<span class="category" class="<%= category.id %>"><%= category.name %></span>
<% end %>
<% end %>
controllers/tags_controller.rb
class TagsController < ApplicationController
def show
#tag = Tag.find(params[:id])
#tags = Tag.all
end
end
Although you have the db relationships correct, you'll still need to call out the connecting model. Because you're doing a double many to many relationship you'll need to create an in-between array. Try adding this to your view:
<% services_array = [] %>
<% #tag.service_tags each do |service_tag| %>
<% services_array << service_tag.service %>
<% end %>
<% services_array.each do |service| %>
<span class="category">
<%= service.service_category.category.id %>
<%= service.service_category.category.name %>
</span>
<% end %>

How to get values from referenced table Rails

I have 3 models with the following structure:
Application and Photo belongs_to Listing. Listing has_many photos and applications.
class Listing < ActiveRecord::Base
belongs_to :user
has_many :photos
has_many :applications
end
class Application < ActiveRecord::Base
belongs_to :user
belongs_to :listing
end
class Photo < ActiveRecord::Base
belongs_to :listing
end
I'm trying to get the photos of the listings that are associated with a users applications.
I start in the controller by passing all the applications of the current user:
#apps = current_user.applications
In the view I want to display all the photos like this:
<% #apps.each do |app| %>
<%= image_tag app.listing.photos[0].image.url(:thumb) if app.listing.photos.length > 0 %>
<% end %>
But I get this error when trying to render the view:
undefined method `photos' for nil:NilClass
Can't I access the photo that belongs to the listing that belongs to the application with this syntax - app.listing.photos[0].image.url ?
Is it possible that some of your applications are not associated with any listing? If so:
<% #apps.each do |app| %>
<%= image_tag app.listing.photos[0].image.url(:thumb) if app.listing.try(:photos).present? %>
<% end %>

Rails: How do I create a controller for a has_many :through using a join model?

I have two models "Stores" and "Vendors". I created a separate join model called "Partnerships" so a store can have many vendors and the Vendor can have many stores. When the user is logged in they are affiliated to either a store or vendor. I want them to be able to create partnerships. I think I have the model down based on my research, but I can't seem to get the controller right. Most of the online examples only show the models not the controller.
class Store < ActiveRecord::Base
attr_accessible :industry, :name
has_many :users
has_many :workorders
has_many :locations
has_many :partnerships
has_many :vendors, :through => :partnerships
class Vendor < ActiveRecord::Base
attr_accessible :industry, :name
has_many :users
has_many :workorders
has_many :locations
has_many :partnerships
has_many :stores, :through => :partnerships
class Partnership < ActiveRecord::Base
belongs_to :store
belongs_to :vendor
attr_accessible :store_id, :vendor_id, :store, :vendor
This is my current partnerships_controller#new where I get a type mismatch error during my test.
if params[:store_id]
#store = Store.where(:id => params[:store_id])
#partnership = Partnership.new(store: #store)
else
flash[:error] = "Store partnership required"
end
Here is my new.html.erb for Partnerships:
<% if flash[:error] %>
Not found.
<% else %>
<% if current_user.affiliation == 'Vendor' %>
<div class="page-header">
<h1> <%= #store.name %></h1>
</div>
<% else %>
<div class="page-header">
<h1> <%= #vendor.name %></h1>
</div>
<% end %>
<% end %>
My User model includes an affiliation field that is either "Store" or "Vendor" and also a company_id field.
What would my controller look like to create a new partnership if I am a user whose affiliation = 'Store'? Would I do it in the partnership controller?

How to calculate average in rails app view through has_many?

I am trying to calculate the average rating in the view of my rails app through a has_many relationship.
Models:
class Review < ActiveRecord::Base
belongs_to :product
attr_accessible :rating, :review
belongs_to :user
end
class Product < ActiveRecord::Base
attr_accessible :name
has_many :reviews
has_many :supplements
acts_as_taggable
end
Controller:
#products = Product.all
View:
<% #products.each do |product| %>
<%= product.NOT SURE %>
<% end %>
I would like this to show the average rating for that given product in the view, but am not sure how. Any advice?
In your view, you want something like:
<% #products.each do |product| %>
<%= product.average_rating %>
<% end %>
Now you just need to do the average_rating, and that can go in the model:
def average_rating
if self.reviews.size > 0
self.reviews.average(:rating)
else
'undefined'
end
end
So average_rating sums up the review ratings and divides by the number of ratings for the average. You can polish it up for format, etc.

Resources