Use case: A user creates a service and can have many services. A user can book the service of another user through a servicebooking. A user can accept/decline the booking for their service from another user.
I am trying to display the outgoing service bookings made by the current user for another users services by checking a servicebookings model and also check/display the incoming bookings made by other users for the current users services on a myservicebookings page.
Myservicebookings view is as follows:
<h1>My Service bookings</h1>
<% if #owns_s %>
<table>
<tr>
<th><%= sortable "date" %></th>
<th><%= sortable "time" %></th>
<th><%= sortable "service name" %></th>
</tr>
<h4>Incoming requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
</table>
<%else%>
<%= "You have no incoming service booking requests"%>
<%end%>
<% if #owns_sb %>
<table>
<tr>
<th><%= sortable "date" %></th>
<th><%= sortable "time" %></th>
<th><%= sortable "service name" %></th>
</tr>
<h4>Outgoing requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
</table>
<%else%>
<%= "You have made no outgoing service booking requests"%>
<%end%>
<%= will_paginate #servicebookings %>
<%= link_to "Homepage", :controller => "welcome", :action => "index" %>
In my servicebookings controller I have the following to check whether a user owns a service or a servicebooking, currently it just returns all the services and servicebookings instead of displaying only the services created by the current user that another user has booking(incoming requests) and instead of displaying the services booked by the current user(outgoing requests). Can anyone give some tips here?? Thanks in advance guys, much appreciated.
def myservicebookings
#servicebookings = current_user.servicebookings.includes(:user).search(params[:search]).order(sort_column + " " + sort_direction).paginate(:per_page => 4, :page => params[:page])
owns_servicebooking = current_user.servicebookings.detect do |sb|
sb.user == current_user
end
owns_service = current_user.services.detect do |s|
s.user == current_user
end
#owns_sb = owns_servicebooking
#owns_s = owns_service
end
Servicebooking model:
class Servicebooking < ActiveRecord::Base
attr_accessible :service_id, :date, :time, :user_id, :service_name, :accept_booking
belongs_to :user
belongs_to :service
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Services model:
class Service < ActiveRecord::Base
attr_accessible :avatar, :avatar2, :avatar3, :avatar4, :name, :date_available, :time_available, :description, :price, :size, :company_name, :company_details
has_attached_file :avatar, :default_url => "/images/:style/missing.png"
has_attached_file :avatar2, :default_url => "/images/:style/missing.png"
has_attached_file :avatar3, :default_url => "/images/:style/missing.png"
has_attached_file :avatar4, :default_url => "/images/:style/missing.png"
belongs_to :user
belongs_to :event
has_many :comments, dependent: :destroy
has_many :servicebookings
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
has_many :through
I think you'll benefit from the has_many :through association
Your ServiceBooking model seems to be a join model, which needs to reference booking_id and service_id
The way you've got the system set up currently is to pull directly from this model. I think you'll be better to use it in conjunction with your other models. You'll literally just have to adapt your code slightly to affect this change:
#app/models/Service.rb
Class Service < ActiveRecord::Base
belongs_to :user
has_many :service_bookings
has_many :bookings, :through => :service_bookings
end
#app/models/Booking.rb
Class Booking < ActiveRecord::Base
belongs_to :user
has_many :service_bookings
has_many :services, :through => :service_bookings
end
#app/models/ServiceBooking.rb
Class Service < ActiveRecord::Base
belongs_to :service
belongs_to :booking
end
This will allow you to pull the actual data from the relative models, rather than just relying on the ServiceBooking model
The beauty of this is that because you can add extra attributes to join models in Rails, you'll be able to include other fields, such as user_id, inbound and outbound
Your Code
I'd do it like this:
#config/routes.rb
resources :users do
resources :bookings
resources :services
end
#app/controllers/users_controller.rb
def index
user = User.find(params[:id])
#bookings = user.bookings
#services = user.services
end
#app/controllers/bookings_controller.rb
def index
user = User.find(params[:user_id])
#bookings = user.bookings
end
#app/controllers/services_controller.rb
def index
user = User.find(params[:user_id])
#services = user.services
end
This will allow you to display the actual bookings / services for each user (all the services / bookings on the user index page; all relative records on bookings and services index actions)
Validation
If you follow these ideas, validation becomes somewhat more methodical
You can either use a before_create function on your ServiceBooking model (to check if the object matches the user_id), or you could perform some controller-based validation to see if the user_id is consistent
I should also mention that checking ownership of booking or service moves to the other models with this (I.E service belongs_to :user)
Related
I have service and servicebooking models. I want to be able to check whether the current user owns a service and display the servicebookings that have been made for that users services.
I am trying to display this in a myservicebookings page.
This is the myservicebookings action in the servicebookings_controller, in the commented line Iam trying to check whether the current user is the owner of any of the servicebookings made or the owner of any of the services booked, I need to advise on how to go about this?
def myservicebookings
#servicebookings = current_user.services.servicebooking.find(params[:user_id])
#servicebookings = current_user.servicebookings.search(params[:search]).order(sort_column + " " + sort_direction).paginate(:per_page => 5, :page => params[:page])
end
This is the myservicebookings view, Iam trying to display both incoming/outgoing requests for services for the current user:
<h1>My Service bookings</h1>
<table>
<tr>
<th><%= sortable "date" %></th>
<th><%= sortable "time" %></th>
<th><%= sortable "service name" %></th>
</tr>
<% if #servicebookings.any? %>
<h4>Incoming requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
<h4>Outgoing requests:</h4>
<% #servicebookings.each do |servicebooking| %>
<tr>
<td><%= servicebooking.date %></td>
<td><%= servicebooking.time %></td>
<td><%= servicebooking.service_name %></td>
<td><%= link_to "View this booking", servicebooking_path(servicebooking) %></td>
</tr>
<% end %>
</table>
<%else%>
<%= "You have no incoming or outgoing service booking requests"%>
<%end%>
<%= will_paginate #servicebookings %>
<%= link_to "Homepage", :controller => "welcome", :action => "index" %>
Servicebooking model:
class Servicebooking < ActiveRecord::Base
attr_accessible :service_id, :date, :time, :user_id, :service_name, :accept_booking
belongs_to :user
belongs_to :service
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Services model:
class Service < ActiveRecord::Base
attr_accessible :avatar, :avatar2, :avatar3, :avatar4, :name, :date_available, :time_available, :description, :price, :size, :company_name, :company_details
has_attached_file :avatar, :default_url => "/images/:style/missing.png"
has_attached_file :avatar2, :default_url => "/images/:style/missing.png"
has_attached_file :avatar3, :default_url => "/images/:style/missing.png"
has_attached_file :avatar4, :default_url => "/images/:style/missing.png"
belongs_to :user
belongs_to :event
has_many :comments, dependent: :destroy
has_many :servicebookings
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
end
Thanks guys, any help is appreciated.
You can do something like this in your controller. Assuming that there is a owner method on ServiceBooking and Service
def myservicebookings
#servicebookings = current_user.servicebookings.includes(:user).search(params[:search]).order(sort_column + " " + sort_direction).paginate(:per_page => 5, :page => params[:page])
owns_servicebooking = current_user.servicebookings.detect do |sb|
sb.user == current_user
end
owns_service = current_user.services.detect do |s|
s.user == current_user
end
#is_an_owner = owns_servicebooking and owns_service
end
detect returns an object if it matches the block and since it is ruby you can use it in a boolean expression to get what you want.
UPDATE: added controller method to the code example above. To use it in your view something like:
<% if #is_an_owner %>
<markup/>
<% end %>
I am pretty certain that this is not the best option in your case but I don't know enough about your stuff to suggest otherwise. It may be a start though. You may want to consider putting parts of this down to the model or into a helper method instead.
I have the following models where UserTreatment is essentially a lookup table but it has another field called instance_cost which stores an integer against each relationship.
class User < ActiveRecord::Base
has_many :user_treatments
has_many :users, :through => :user_treatments
end
class UserTreatment < ActiveRecord::Base
attr_accessible :instance_cost
belongs_to :user
belongs_to :treatment
end
class Treatment < ActiveRecord::Base
attr_accessible :name
has_many :user_treatments
has_many :users, :through => :user_treatments
end
So I can do things like this to get the first instance cost for user id 14
1.9.3p429 :181 > User.find(14).user_treatments.first.instance_cost
=> 100
and this to get the name of the treatment
1.9.3p429 :181 > User.find(14).user_treatments.first.treatment.name
=> "Sports Massage"
However I have a problem representing them in a form using simple_form_for
<% simple_form_for #user do |f| %>
# This sucessfully gives us the checkboxes of all treatments and stores them in the UserTreatments table
<%= f.collection_check_boxes(:treatment_ids, Treatment.all, :id, :name) %>
<%= f.fields_for :user_treatments do |pt| %>
<tr>
<td>
<!-- I WANT THE NAME OF THE TREATMENT HERE -->
</td>
<td><span>£</span> <%= pt.input :instance_cost, :as => :string, wrapper: false, label: false %></td>
</tr>
<% end %>
end
There are two things I need to do.
Show the name of the treatment (how do I do express User.user_treatments.treatment.name with simple_form ?)
Set the instance_cost correctly. Its currently not getting set at all.
When you do fields_for for a nested association then you need to add the following in your User model:
accepts_nested_attributes_for :user_treatments
Otherwise, it will not save the information.
And to access the name of the treatment, you could go through the object method of the form creator object, like this:
<%= f.fields_for :user_treatments do |pt| %>
<tr>
<td><%= pt.object.treatment.name %></td>
...
</tr>
<% end %>
I've got an app that sets timed challenges for users. Each user is associated with one or more challenges. I've setup the models so they connect via a join table. That works fine, but I'm having problems on my view level. In the index view for the challenges, data from the challenge model and from the user model are displayed. But where the view should display the user's name, it's just displaying "User." If you click on "User", you are taken to the correct "show" page for that user. So the link works fine, but I can't get the user's name to appear. Instead, I'm just getting the class name to appear. Any idea why?
Here's the code for the view. The file directly below is is views/challenges/index.html.erb
<%- model_class = Challenge.new.class -%>
<h1><%=t '.title', :default => model_class.model_name.human.pluralize %></h1>
<table class="table table-striped">
<thead>
<tr>
<th><%= model_class.human_attribute_name(:date) %></th>
<th><%= model_class.human_attribute_name(:time) %></th>
<th><%= model_class.human_attribute_name(:rider) %></th>
<th><%=t '.actions', :default => t("helpers.actions") %></th>
</tr>
</thead>
<tbody>
<% #challenges.each do |challenge| %>
<tr>
<td><%= link_to challenge.date, challenge_path(challenge) %></td>
<td><%= link_to challenge.duration, challenge_path(challenge) %></td>
<td><%= link_to challenge.users.name, user_path(challenge) %></td>
</tr>
<% end %>
</tbody>
</table>
And here's the relevant models.
Challenge.rb
class Challenge < ActiveRecord::Base
attr_accessible :date, :duration, :user
has_many :user_challenges
has_many :users, :through => :user_challenges
validates_presence_of :date, :duration
def self.winner
Challenge.find(:first, :order => "duration desc")
end
end
User.rb
class User < ActiveRecord::Base
attr_accessible :name, :email
has_many :user_challenges
has_many :challenges, :through => :user_challenges
validates_presence_of :name, :email
validates_uniqueness_of :email
def self.find_or_create(name, email)
user = User.find_by_email(email)
if user.present?
user.challenge = challenge
user.save
else
User.create(:name => name, :email => email)
end
end
end
Join table, aka User_challenge.rb
class UserChallenge < ActiveRecord::Base
belongs_to :challenge
belongs_to :user
end
challenge.users is a collection and .name is a ruby method that gives you the name of the class:
ruby-1.9.2-head :002 > Object.name
=> "Object"
Call the attribute something else (username).
Also, user_path(challenge) doesn't really make sense when a Challenge can have_many :users (which users path should it be?).
I think the users list is empty, and that's why users.first.username is nil.
the user_path(challenge) 'seems' like its working, but its probably always taking you to the users/challenge_id path, which is not what you want.
most likely something isn't saving correctly for you. verify that your users lists are not empty. hop in the console and test out the saving and reloading of your data
My goal is to display select box for each relation for users and specific project.
All users need to be listed but only project users have some type of relation. Other users have none selected in theirs select box.
I have this model:
class Project < ActiveRecord::Base
belongs_to :company
has_many :tasks, :order => 'state_type_id ASC'
has_many :project_user_relations
has_many :users, :through => :project_user_relations
def t_name
name.camelcase
end
end
class User < ActiveRecord::Base
belongs_to :company
has_many :tasks , :foreign_key => :assigned_user_id
has_many :project_user_relations
has_many :projects, :through => :project_user_relations
def full_name
firstname + ' ' + lastname
end
def relation_to(project)
relation=ProjectUserRelation.find_by_project_id_and_user_id(project.id, id)
relation ||= relation=ProjectUserRelation.new
end
end
class ProjectUserRelation < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_user_relation_type
end
class ProjectUserRelationType < ActiveRecord::Base
def t_name
I18n.t("app.projects.users.relation.type."+code)
end
end
I want make a form to display all users, with collection_select.
I used code:
def edit_all
#project = Project.find(params[:project_id])
#users = User.all
....
in my controler
routes works ok.
in my view:
<% #users.each do |user| %>
<%= f.fields_for :users, user do |user_fields| %>
<tr class="reference" rel="<%= parent_user_path(user) %>" >
<td class="name"><%= link_to user.full_name, parent_user_path(user) %></td>
<td class="email"><%= mail_to user.email %></td>
<td class="type">
<%= user_fields.fields_for user.relation_to #project do |relation_fields| %>
<%= relation_fields.collection_select :project_user_relation_type, ProjectUserRelationType.all, :id, :t_name, {:include_blank => false, :prompt => t("helpers.select.prompt") } %>
<% end %>
</td>
</tr>
<% end %>
<% end %>
or for test:
<%= f.fields_for :users, #users do |xuser_fields| %>
<% logger.debug "#{self.to_s} xuser_fields = #{xuser_fields.object.inspect} ;" %>
<tr>
<td><%= xuser_fields.text_field :firstname %></td>
<td></td>
<td></td>
<td></td>
</tr>
<% end %>
but notnihng woks right
first one generates wrong name in html:
select id="project_users_project_user_relation_project_user_relation_type" name="project[users][project_user_relation][project_user_relation_type]"
second one generates error:
undefined method `firstname' for # Array:0x4d03658
Can you help me to solve this situation.
PS:sorry for long code :(
SOLUTION (probably - solved by reading RoR sources)
I found sollution i thing.
A method
def name_attributes=(attributes)
# Process the attributes hash
end
in Project model was missing.
It is unbelievable sollution :].
There is also exact syntax after fields_for: :name, #some_collection, where name must be exactly same name as in the beginign of mentioned def in Model.
Its maybe not the best solution in most cases, but i want a table with data form 3 tables.
class Media < ActiveRecord::Base
belongs_to :user
belongs_to :type
has_many :ratings
end
class User < ActiveRecord::Base
has_many :medias
has_many :ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :media
end
Thats the view I want
<table>
<tr>
<th>Name</th>
<th>Comment</th>
<th>Creator</th>
<th>Type</th>
<% for user in #users %>
<th><%=h user.login %></th>
<% end %>
</tr>
<% for media in #medias %>
<tr>
<td><%=h media.name %></td>
<td><%=h media.comment %></td>
<td><%=h media.user.login %></td>
<td><%=h media.type.name %></td>
<% for user in #users %>
<td><%=h GET_RATING (media, user) %></td>
<% end %>%>
</tr>
<% end %>
</table>
Basicly i want one new row for each users ratings for each media
What I want is a Table that looks like that:
media.name media.comment ... rating(media, user).rating
I think it would be better to use a join in the Controller with the Media find methods but I dont know how exactly, enougher possible solution could be helper method that takes media and user as parameters.
What do you think is the best solution for this?
This kind of association belongs in your model, a has many through relationship is perfect for this.
class User < ActiveRecord::Base
has_many :ratings
has_many :media, :through => :ratings
end
class Media < ActiveRecord::Base
has_many :ratings
has_many :users, :through => ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :media
end
Then you can access
media.name media.comment
Then could also access
user.ratings
or:
<% media.users.each do |user| %>
## Do stuff with user.ratings array
<% end %>
You can also:
media.ratings.each do |rating|
rating.your_attribute
rating.user.your_attribute
end