How can show value through association? - ruby-on-rails

I'm trying to show a phone number when customer contact has it
Table:
|customers|
|id| |name|
1 Zidane
2 Mourinho
3 Guardiola
4 Ferguson
|contacts|
|id| |customer_id| |name| |phone|
1 1 Muller
2 1 Alaba 9872147
3 2 Aubameyang 2323234
4 3 Dante
5 3 Robben
6 3 Lewandoski 2343256
7 4 Ribery
Controller:
def index
#customers = Customer.all
end
Model:
class Customer < ActiveRecord::Base
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :customer
end
View:
<% #customers.each do |customer| %>
<%= customer.name %>
<% customer.contacts(:conditions=>['phone>0']).each do |contact| %>
<%= contact.name %>
<%= contact.phone %>
<% end %>
<% end %>
Also tried:
<% #customers.each do |customer| %>
<%= customer.name %>
<% customer.contacts.each do |contact| %>
<%= contact.name %>
<%= contact.phone(:all,:conditions=>['phone>0']). %>
<% end %>
<% end %>
Also tried but is only getting the first contact:
<%= customer.contacts.first(1).map { |c| c.phone } %>
Also tried but is only getting the last contact:
<%= customer.contacts.last(1).map { |c| c.phone } %>
I want this as result:
|customer| |name_contact| |phone|
Zidane Alaba 9872147
CR7 Aubameyang 2323234
Guardiola Lewandoski 2343256
Ferguson Ribery

Something like below can work
<% customer.contacts.all {|c| c.phone.present?}.each do |contact| %>
Full snippet
<% #customers.each do |customer| %>
<%= customer.name %>
<% customer.contacts.all {|c| c.phone.present?}.each do |contact| %>
<%= contact.name %>
<%= contact.phone %>
<% end %>
<% end %>

Related

how to use order by in rails model

Consider I have a index controller which will display all category and sub category
My index controller has
#categories = Category.where(status: true)
category.rb
has_many :sub_categories
here is my sub_category
sub_category.rb
belongs_to :category
In my view I have
<% #categories.each do |category| %>
<%= category.name %>
<% category.sub_categories.each do |sub_category| %>
<%= sub_category.name %>
<% end %>
<% end %>
My sub_category has status(true, false) I need to display only the sub_category with the status as true
How can I do this.
By using where, i.e. category.sub_categories.where(status: true):
<% #categories.each do |category| %>
<%= category.name %>
<% category.sub_categories.where(status: true).each do |sub_category| %>
<%= sub_category.name %>
<% end %>
<% end %>
Or even better to return only what's required, just select the #categories that have subcategories with status = true.
For this update your controller's action code where you have #categories defined:
# controller
#categories = Category.includes(:sub_categories).where('sub_categories.status = ?', true)
Then in your view:
<% #categories.each do |category| %>
<%= category.name %>
<% category.sub_categories.each do |sub_category| %>
<%= sub_category.name %>
<% end %>
<% end %>
I would recommend to use scope in your app. In SubCategory model add scope :active, where(active: true) and then use category.sub_categories.active.each ...

How to sum fields after group_by

I need to group some records by date, then sum one field for each of those days.
This is the code I'm trying to refactor:
<% #startdate.upto(#enddate) do |date| %>
<% visitors = #links.where("created_at >= ? AND created_at < ?", date, date+1).order("created_at ASC").select("id, visit_count") %>
<%= datevisitors.sum("visit_count") || 0 %>
<% end %>
I've started with:
<% visitors = #links.select("visit_count, created_at").group_by{|l| l.created_at.day} %>
<% #startdate.upto(#enddate) do |date| %>
<%= visitors[date] ...?
<% end %>
When you group_by something, you receive an array of the day and the element. So if you group by day, you'll have the Day and the elements that are part of the array:
<% #links.group_by{|l| l.created_at.day}.each do |day, links| %>
<p><%= day %>: <%= links.sum(&:visit_count) %></p>
<% end %>
EDIT --
OK, in that case your code looks like it was very close:
<% visitors = #links.select("visit_count, created_at").group_by{|l| l.created_at.day} %>
<% #startdate.upto(#enddate) do |date| %>
<% if visitors[date] %>
<%= visitors[date].sum(&:visit_count) %>
<% else %>
0
<% end %>
<% end %>
It's probably better to move this to a helper (or presenter)
def daily_visits(links, from, to)
links_by_day = links.select("visit_count, created_at").group_by{|l| l.created_at.day}
range = from.upto(to).to_a
range.inject({}) do |date, hash|
hash[date] = links_by_day.fetch(date, 0)
end
end
Then, in the view:
<% daily_visits(#links, #startdate, #enddate).each do |day, count| %>
<%= day %>: <%= count %>
<% end %>
This should be on controller
#dates_with_visitors = #links.select("visit_count, created_at").group_by(&:created_at)
This should be on the view
<% #dates_with_visitors.each do |date_with_visitors| %>
<%= "#{date_with_visitor[0]} - Visits: #{date_with_visitors[1].sum(&:visit_count)}" %>
<% end %>
Hi can you give me the Model that will be used?
Here's my example on a Activerecord with a aggregate sum function:
<% #sum_visit_count = Visitor.find(:all, :select => "SUM(visit_count)", :group => "id, date_visit") %>
and
<%= #sum_visit_count.sum %>

Rails element if first time appearance

I have a model Post with :mark, :text
I have a list with my post
<% #posts.each do |p| %>
# todo
<% if p.mark? %>
<%= p.mark %> <%= sweet_thing(p.text) %>
<% else %>
<%= sweet_thing(p.text) %>
<% end %>
<% end %>
I need to show p.mark name instead #todo where p.mark first time appearance.
Final txt example:
Audi
Audi, text-text-text-text.
Audi, text-text-text-text.
Audi, text-text-text-text.
Ford
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
Ford, text-text-text-text.
UPDATE
My txt render in controller
def txt_receiver
#posts = Post.where("created_at >= ?", 7.days.ago.utc).find(:all, order: "mark, LOWER(post)")
render "txt_for_newspapper", formats: ["text"]
end
An obvious solution is to keep track of seen marks.
<% seen_marks = {} %>
<% #posts.each do |p| %>
<% unless seen_marks[p.mark] %>
<%= p.mark %>
<% seen_marks[p.mark] = true %>
<% end %>
# rest of your code
<% end %>
A better solution (I think) involves grouping posts by their mark and then outputting in groups. But I'm not sure whether it will match your logic regarding missing marks.
<% #posts.group_by(&:mark).each do |mark, posts| %>
<%= mark %>
<% posts.each do |p| %>
<%= p.mark if mark %> <%= sweet_thing(p.text) %>
<% end %>
<% end %>

How to show parent and child in view with will paginate?

I'm having trouble showing my view correctly. I have this as my code right now:
<% for store in #stores %>
<% store.name %>
<% #stores.products.each do |p| %>
<% p.name %>
<% end %>
<% end %>
def index
#stores = Store.paginate(:page => params[:page], :per_page => 20)
end
But end up with the error:
undefined method `products'
I'm trying to show a store and then all of its products, repeating this on the same page as much as possible e.g:
Store1
Product1
Product2
Store2
Product1
Product2
Product3
Product4
How can I do this?
Instead of <% #stores.products.each do |p| %> I think you mean <% store.products.each do |p| %>:
Also, do you not mean to have <%= on the store.name and p.name lines?
<% for store in #stores %>
<%= store.name %>
<% store.products.each do |p| %>
<%= p.name %>
<% end %>
<% end %>
Shouldn't that be store.products inside the loop, where you're accessing store?
<% for store in #stores %>
<%= store.name %>
<% store.products.each do |p| %>
<%= p.name %>
<% end %>
<% end %>
And = is added to the output lines. <%=

How can I refactor out needing so many for-loops in rails?

I need help refactoring this multi-loop thing. Here is what I have:
Campaign has_many Contacts
Campaign also has many Models which are templates: (Email, Call, and Letter).
Because I am looking for overdue on each, I created an array called Event which I'd like to loop through that contains ['email', 'call', 'letter'].
I need a list of all the Emails, Calls and Letters that are "overdue" for every Contact that belongs to a Campaign. Overdue is determined by a from_today method which looks at the date the Contact was entered in the system and the number of days that needs to pass for any given Event. from_today() outputs the number of days from today that the Event should be done for a given Contact.
Here is what I've done, it works for all Emails in a Campaign across all contacts. I was going to try to create another each do loop to change the class names.
Wasn't sure where to begin: named_scope, push some things into a method, etcetera, or -- minimum to be able to dynamically change the class names so at least it loops three times across the different events rather than repeating the code three times:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<% #events.each do |event| %>
<%= event %>
<% for email in campaign.emails %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<% for contact in campaign.contacts.find(:all, :order => "date_entered ASC" ) %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Just to add to Patrick's answer, I would also use the :collection option of render to simplify this a bit further, e.g. have a partial _contact.html.erb to render each contact:
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>
and then render the contacts collection with
= render :partial => "contact", :collection => #contacts
I also wouldn't do a find in the view, instead I would setup all the variables in the controller, and probably move all the conditional code into a helper. It's preferable to keep as much logic as possible out of the views.
I'd put the output for each resource into a partial, like so:
<% #campaigns.each do |campaign| %>
<h2><%= link_to campaign.name, campaign %></h2>
<%= render 'events', :events => campaign.events %>
<% end %>
then in app/views/campaigns/_events.html.erb
<% events.each do |event| %>
<%= event %>
<%= render 'emails', :emails => event.emails %>
<% end %>
then in app/views/campaigns/_emails.html.erb
<% emails.each do |email| %>
<h4><%= link_to email.title, email %> <%= email.days %> days</h4>
<%= render 'contacts', :contacts => email.contacts.all(:order => "date_entered ASC", :email => email) %>
<% end %>
then in app/views/campaigns/_contacts.html.erb
<% contacts.each do |contact| %>
<% if (from_today(contact, email.days) < 0) %>
<% if show_status(contact, email) == 'no status'%>
<p> <%= full_name(contact) %>
is <%= from_today(contact,email.days).abs%> days overdue:
<%= do_event(contact, email) %>
</p>
<% end %>
<% end %>
<% end %>

Resources