Search code examples
ruby-on-railsmodel-associations

Rails 3 Saving with Multiple Associations


I am building a Rails 3 app with a job board, where workers can submit bids for jobs. I have the following associations:

Job:
  has_many :bids
  has_many :workers, :through => :bid

Bid:
  belongs_to :job
  belongs_to :worker

Worker:
  has_many :bids
  has_many :jobs, :through => :bid

I have a form on the Show Job Page in which workers can submit bids. In the controllers, I have the following:

Job Controller:
  def show
    @bid = current_worker.bids.build
  end   

Bid Controller:
  def create
    @bid = current_worker.bids.build(params[:bid])
    @bid.save
  end

With the code above, a submitted bid will not save correctly because it is missing the job_id:

1) What is the correct way to save the bid with BOTH the job_id and worker_id?

2) (I am having trouble passing the job_id from the Job Controller show method to the Bid Controller create method) - is it secure to pass the job_id in a sessions variable?

For reference, the bid form looks like:

<%= form_for(@bid) do |f| %>

  <%= f.label :min_price, "Minimum Price" %>
  <%= f.text_field :min_price %>

  <%= f.label :fee %>
  <%= f.text_field :fee %>

  <%= f.label :comments %>
  <%= f.text_area :comments, placeholder: "Comments..." %>
  <%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>

Solution

  • Code depends on what safety you want, I suppose you want to have to protected jobs which current_worker can't make bids to, so you need that does not seems to depend on bid, instead in job.

    As you are first creating a bid you can pass job_id, in the form or as part of the route.

    If you want to deny a worker to bid to any job you could do something like this:

    Bids Controller:
    
      def create
        job = Job.find(params[:job_id])
        if current_worker.can_bid? job
          @bid = current_worker.bids.build params[:bid]
        else
          # handle unauthorised bidding
    

    In worker model, this is just an example:

    def can_bid?(job)
      # Implement code here
      # example:
      # job.public? or invited_to?(job)
    end
    
    # example of invited_to?(job)
    def invited_to?(job)
      job.invitees.include? self
    end
    

    I am not sure if this answers your question.

    I think you could use this to pass job id in route:

    Routes

    resources :jobs do
      resources :bids
    end
    

    View

    = form_for @job, @bid ...
    

    As in first time you don't have @job, you can use:

    = form_for :job, @bid