Passing object ID across models Rails - ruby-on-rails

I am trying to create an app that allows users to create and apply for jobs but seem to have hit a problem.
I can't get the job_id to pass into my apps (job applications) table in my database.
To get this app to work succesfully I need to pass the job_id and the user_id to the user's application form so that when they submit their job application this information is stored in my apps table. The job owner will then be able to review the applications they have received.
I have the following associations in my models:
class App < ActiveRecord::Base
belongs_to :job
belongs_to :user
class Job < ActiveRecord::Base
belongs_to :user
has_many :apps
has_many :applicants, :through => :apps, :source => :user
class User < ActiveRecord::Base
has_many :apps
has_many :jobs
has_many :jobs_applied_for, :through => :apps, :source => :job
Defined on my Jobs controller's show page (the page from which the user can click "apply now" to start an application) I have the following:
def show
#job = Job.find(params[:id])
end
The link to "apply now" on the actual page is:
<%=link_to "Apply Now", new_app_path %>
and on my Apps controller's new page I have:
def new
#user = current_user
#app = #user.apps.build
end
My user_id is passing perfectly and appearing in my apps table but I am totally stumped on how to pass the job_id correctly.
If I have missed anything that I can edit into this question to help you answer it then please do let me know.
Thanks in advance for your help!

You are not passing the job_id in your new_app_path link. Try changing it to new_app_path(:job_id => #job.id), and in your controller add #job = Job.find(params[:job_id])

Assuming your routes nest apps inside jobs, your link to the new application page should be something like
link_to 'apply now', new_app_job_path(#job)
In your new action you'll have a params[:job_id] that you can use to set #job
Then on the form, your call to form for should look like
form_for [#job, #app] do |f|
...
This ensures that your create action will also have a :job_id parameter that you can use when creating the application

The action that you should be invoking is create not new.
Change the link_to code as follows:
<%=link_to "Apply Now", apps_path(:app => {:job_id => #job.id}), :method => :post %>
In your controller:
def create
app = current_user.apps.build(params[:app])
if app.save
# handle success
else
# handle error
end
end

Related

Rails - How to create multiple "matches" in one action between a user and opportunities?

I'm looking for the easiest and the most clever way to create interest_id(match) in one-click.
Here is my MVC :
user.rb
class User < ApplicationRecord
has_many :interests, through: :opportunities
end
interest.rb
class Interest < ApplicationRecord
belongs_to :opportunity
belongs_to :user
end
opportunity.rb
class Opportunity < ApplicationRecord
has_many :interests
end
InterestsController.rb
def create
#user = current_user
#opportunities = Opportunity.all
#interest = Interest.new(interest_params)
if #interest.save!
redirect_to user_interests_path, notice: 'it works'
else
render :new, notice:"it doesn't work"
end
end
def interest_params
params.permit(
:user_id,
:opportunity_id)
end
user/show
<%= link_to "Match", user_interests_path(#user), class:"btn btn-primary", :method => :post %>
For now, I can't pass opportunities (nil). Could you please advise me about the easiest way to create interests? (New on RoR for 6 months).
Many thanks for your help.
If I understand correctly your relation schema, the Interest is the join record associating a User to (eventually) many Opportunity, and vice-versa (many-to-many relationship).
With that being said (and please correct me if I am wrong), you can do the following to achieve what you want:
# in user/show
<% #opportunities.each do |opportunity| %>
<%=
link_to "Match opportunity #{opportunity.id}",
user_interests_path(#user, opportunity_id: opportunity.id),
class: "btn btn-primary",
method: :post
%>
<% end %>
# in interests_controller
def create
if current_user.interests.create(opportunity_id: opportunity_id_param)
redirect_to user_interests_path, notice: 'it works'
else
render :new, notice: "it doesn't work"
end
end
private
def opportunity_id_param
params.require(:opportunity_id)
end
This suggested code:
requires the opportunity_id param for the interests#create action
use current_user to automatically set the user_id on the Interest model, so the end-users can't send a user_id that are not theirs (if they could, then each user could create interest for other users without their agreement... security flaw)
On a side note, I strongly advise you to not select all existing Opportunity record and display it on your page: it does not scale. Someday, you will end up with hundreds of Opportunity records, making this list too big from a User Experience perspective.
I suggest a smarter approach, for example some kind of ordering + limit: max of 10 records ordered by "most interest", which can be accomplished by the following:
# in controller
#popular_opportunities = Opportunity
.joins('LEFT JOINS interests ON interests.opportunity_id = opportunities.id')
.order('count(interests.*) DESC, opportunities.id')
.limit(10)
And then in the view, simply use #populator_opportunities instead of #opportunities.
Other options, like pagination, are also efficient in this case but IMO relevant ordering is the minimum.
First, you need to pass the ids of the opportunities you want to create interest some way, the best is a form, with checkboxes like MrShemek said, or a multi select dropdown.
I think you probably made some mistakes in User and Opportunity with the has_many and belong_to part:
class User < ApplicationRecord
has_many :interests
has_many :opportunities, through: :interests
# interest is the one that links user and opportunity, it has the references for both user and opportunities
end
class Opportunity < ApplicationRecord
has_many :interests
has_many :users, through: :interests
end
then in controller you could do
def create
#user = current_user
#opportunities = Opportunity.all
#user.opportunity_ids = interest_params[:opportunity_ids] # it will create the interrests automatically for the given ids (because the relations of has_many through)
if #user.save!
redirect_to user_interests_path, notice: 'it works'
else
render :new, notice:"it doesn't work"
end
end

Current User to Dynamically Join/Leave a Team

Simple feature to implement: clicking on a big button called "Join This Team" allows the currently signed in user to--gasp--join the aforementioned team.
So, here's what I got:
class User < ActiveRecord::Base
#devise code
has_many :memberships
has_many :teams, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
validates :name, uniqueness: true
end
Important question here: it's key that a user can join more than one team. Is using a join table the right approach here? Without one, correct me if I'm wrong, a user will be stuck to only a single team, right?
So, I got a simple button in teams#show
<%= link_to '#', :class => 'jointeam', :data => {:id => #team.id} do %>
<div class='ui button'>Join This Team</div>
<% end %>
Now, upon clicking this button, I want the user to be added to this team. I have some JS that's incomplete:
$('.jointeam').on('click', function(e){
var id = $(this).data("id");
e.preventDefault;
$.ajax({
type: "POST",
url: "/teams/" + id,
data: {_method:"PUT", }
});
});
So, I'm thinking a POST request with a method "PUT" with jQuery. Here's where I'm stuck. Since I used a Join-table called memberships, what's the right way to implement this feature? Do I need a memberships controller? Can I have a method in my Team's controller that allows the current_user to associate to the team in question? Do I need to write a custom method? How does it tie to the jQuery ajax request to be syntactically correct?
Thanks in advance!
You can add a custom RESTful action to teams by editing the team resource in routes.rb:
resources :team do
post 'join', on: :member
end
this will allow URLs like /teams/<team ID>/join
you can then create a join method in the TeamController the basics of which would be:
def join
team = Team.find(params[:id])
membership = Membership.new(team: team, user: current_user)
current_user.memberships << membership
end

Access current id for a has_one relationship in rails

I have two models in my app. One is called meetings and the other is outcome. I wanted to create the outcome of each meeting using: #outcome=current_meeting.outcome.build(params[:outcome]). Further, each meeting would have only one outcome so it is clearly a has_one relationship. I am really confused about getting the current_meeting. I have the meetings and outcomes models as:
Meeting Model
class Meeting < ActiveRecord::Base
attr_accessible :info, :meeting_date, :name, :venue
has_many :participants, :dependent => :destroy
has_one :outcome, :dependent => :destroy
validates_presence_of :name, :info, :meeting_date, :venue
end
Outcome Model
class Outcome < ActiveRecord::Base
attr_accessible :result
belongs_to :meeting
validates :meeting_id, presence: true
end
I want the new outcome to be present inside the show of the meeting, if there are none present and if there is one present creating new outcome should be made impossible
meetings/show.html.erb
<% if #meeting.outcomes.any? %>
<%= render #outcomes %>
<% else %>
<%= link_to "Add the outcome of meeting", new_outcome_path %>
<% end %>
My controllers are:
Meetings controller
def show
#meeting = Meeting.find(params[:id])
#outcomes = #meeting.outcomes.all
end
Outcomes controller
def new
#outcome = current_meeting.microposts.new
end
def create
#outcome = current_meeting.outcomes.build(params[:outcome])
if #outcome.save
flash[:success] = "Outcome created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
I don't know how to find out the current_meeting. Please help.
First of all, the question is very confusing as to the plurality of outcome vs outcomes. If a Meeting has_one Outcome then you you would use the singular form when referring to the reference. Basically, given has_one :outcome, ":outcome" is the method name to be used. So you'd say meeting.outcome instead of meeting.outcomes. And the build method for has_one would be like meeting.build_outcome instead of meeting.outcomes.build. The latter is the api for a has_many relationship.
With that out of the way, if you want to get the current Meeting from the Outcomes controller, the best way to do this is with nested resources. So in the routes file you'd have, e.g.:
resources :meetings do
resources :outcomes
end
After you do that, run rake routes to see the routes available to you. In there you'll see an expected url format of POST /meetings/:id/outcomes which you would use here. So in this case, the create method would get the Meeting object from params[:id], from which the outcome relationship can be created.
At first glance it does not seem like you are defining current_meeting anywhere. You probably already know this and if so the case would be that you are unsure of how/where to define it. You will probably need to do that somewhere in the code. This could mean saying something like this meeting is current because it is during the current time and/or today. This is based on how your app works to determine this logic.
In your controller or in a helper you will need to write a method that gives you the current meeting if one exists. From there the current_meeting variable in your controller will be set correctly and should call your other methods right.
If I have misunderstood the issue I apologize and please provide any other details you can and I can try to help.

RoR duplication fields

When an incorrect mandatory fields, the page reloads and you receive an incorrect entry fields and all fields in the form company_form duplicated.
User model(user.rb):
class User < ActiveRecord::Base
....
has_many :companies, :autosave => true
accepts_nested_attributes_for :companies
has_and_belongs_to_many :roles
def role?(role_name)
return !!self.roles.find_by_name(role_name)
end
def with_company
self.companies.build
self
end
end
Company model:
class Company < ActiveRecord::Base
...
belongs_to :user
...
end
views/devise/registration/new.html.haml:
= form_for(resource.with_company, :as => resource_name, :url => registration_path(resource_name), :html => { :class => 'form-horizontal'}) do |f|
...
= f.fields_for :companies do |company_form|
...
...
The problem is that your with_company method uses build to create the new Company object. build will automatically create the new object and save it to the database. So every time that form gets rendered, you'll add another Company to that User. Just hit reload on the page a few times and you should see what I mean.
I think the fix will be to use create instead of build in that method; this creates a new object but doesn't save it to the database.
More likely, though, you should be doing this in the controller that renders the view, rather than in the view itself. Devise doesn't always make this straightforward, but you should be able to create a new controller that inherits from the Devise controller and adds what you need. I do something similar in my current project here.
worked! change def with_company on
def with_company
if self.companies.empty?
self.companies.build
end
self
end

Creating forms for polymorphic associations in Rails

I have a couple classes that can each have comments:
class Movie < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Actor < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
How do I create a form for a new movie-comment? I added
resources :movies do
resources :comments
end
to my routes.rb, and tried new_movie_comment_path(#movie), but this gives me a form containing commentable_id and commentable_type [which I want to be populated automatically, not entered by the user directly]. I also tried creating the form myself:
form_for [#movie, Comment.new] do |f|
f.text_field :text
f.submit
end
(where "text" is a field in the Comment table)
but this doesn't work either.
I'm not actually sure how to associate a comment with a movie at all. For example,
c = Comment.create(:text => "This is a comment.", :commentable_id => 1, :commentable_type => "movie")
doesn't seem to create a comment associated to the movie with id 1. (Movie.find(1).comments returns an empty array.)
As you have created the polymorphic association in your model, you need not worry about that anymore in the view. You just need to do this in your Comments controller.
#movie = Movie.find(id) # Find the movie with which you want to associate the comment
#comment = #movie.comments.create(:text => "This is a comment") # you can also use build
# instead of create like #comment = #movie.comments.create(:text => "This is a comment")
# and then #comment.save
# The above line will build your new comment through the movie which you will be having in
# #movie.
# Also this line will automatically save fill the commentable_id as the id of movie and
# the commentable_type as Movie.
You're going to have to be more descriptive than "...but this doesn't work either," but the general idea is:
#movie.comments.create( :text => params[:movie][:comment][:text] )
More typically:
#movie.comments.create( params[:comment] ) # or params[:movie][:comment]
The important thing is that you find #movie first and create your associated objects through it. That way you won't have to worry about Commentable or types or anything.

Resources