kyussmondo
kyussmondo

Reputation: 27

Save successful on nested attributes on has many through but doesn't add to database

I and order and item system with Rails 4 with a has many through association. When I select create order the webpage says that the order was created successfully however the link is not made in the OrderItems linking tables meaning that the items relating to that order do not appear on the show page or edit page for an order.

The order is also linked to an employee. The current employee ID is linked to that order. I have just not been able to figure out how to add each item to the database.

p.s. I am using a gem called nested_form to handle all of the jQuery on the front end of dynamically adding and removing new items on the _form.html.erb for Orders.

orders_controller.rb

class OrdersController < ApplicationController
  before_action :logged_in_employee, only:[:new, :show, :create, :edit, :update, :index]
  before_action :admin_employee, only:[:destroy]
  before_action :set_order, only: [:show, :edit, :update, :destroy]

  def new
    @order = Order.new
    @items = Item.all
  end

  def edit
    @items = Item.all
  end

  def create
    @order = current_employee.orders.build(order_params)
    if @order.save
      flash[:success] = "Order successfully created"
      redirect_to @order
    else
      render 'new'
    end
  end

  def update
    if @order.update_attributes(order_params)
      flash[:success] = "Order updated!"
      redirect_to current_employee
    else
      render 'edit'
    end
  end

  ....

  private

    def set_order
      @order = Order.find(params[:id])
    end

    def order_params
      params.require(:order).permit(:table_number, :number_of_customers, :status, :comment, order_items_attributes: [:id, :order_id, :item_id, :_destroy])
    end

end

order.rb

class Order < ActiveRecord::Base

  belongs_to :employee
  has_many :order_items
  has_many :items, :through => :order_items

  default_scope { order('status DESC') }

  validates :employee_id, presence: true
  validates :table_number, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 20 }, presence: true
  validates :number_of_customers, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 50 }, presence: true
  validates :status, inclusion: { in: %w(Waiting Ready Closed), message: "%{value} is not a status" }

  accepts_nested_attributes_for :order_items, :reject_if => lambda { |a| a[:item_id].blank? }

end

item.rb

class Item < ActiveRecord::Base
  belongs_to :menu
  has_many :order_items
  has_many :orders, :through => :order_items

  before_save { name.capitalize! }
  VALID_PRICE_REGEX = /\A\d+(?:\.\d{0,2})?\z/

  validates :name, presence: true, length: { maximum: 100 }
  validates :price, format: { with: VALID_PRICE_REGEX }, numericality: { greater_than: 0, less_than_or_equal_to: 100}, presence: true
  validates :course, inclusion: { in: %w(Starter Main Dessert Drink), message: "%{value} is not a course" }
  validates :menu_id, presence: true

end

order_item.rb

class OrderItem < ActiveRecord::Base

  belongs_to :item
  belongs_to :order

  validates :order_id, presence: true
  validates :item_id, presence: true

end

orders/_form.html.erb

<% provide(:title, "#{header(@order)} #{@order.new_record? ? "order" : @order.id}") %>

<%= link_to "<< Back", :back, data: { confirm: back_message } %>

<h1><%= header(@order) %> <%= @order.new_record? ? "order" : @order.id %></h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">

    <%= nested_form_for @order do |f| %>

      <%= render 'shared/error_messages', object: f.object %>

      <div class="row">
        <div class="col-xs-6">
          <%= f.label :table_number %>
          <%= f.number_field :table_number, class: 'form-control' %>
        </div>
        <div class="col-xs-6">
          <%= f.label :number_of_customers %>
          <%= f.number_field :number_of_customers, class: 'form-control' %>
        </div>
      </div>

      <%= f.fields_for :order_items do |oi| %>
        <%= oi.grouped_collection_select :item_id, Menu.where(active: true).order(:name), :items, :name, :id, :name, { include_blank: 'Select Item' }, class: 'items_dropdown' %>
        <%= oi.hidden_field :item_id %>
        <%= oi.hidden_field :order_id %>
        <%= oi.link_to_remove "Remove item" %>
      <% end %>
      <p><%= f.link_to_add "Add an item", :order_items %></p>

      <br>

      <% if [email protected]_record? %>
        <%= f.label "Status" %>
        <%= f.select(:status, options_for_select([['Waiting', 'Waiting'], ['Ready', 'Ready'], ['Closed', 'Closed']], @order.status), class: 'form-control') %>
      <% end %>

      <%= f.label "Comments - how would you like your steak cooked? Or feedback" %>
      <%= f.text_area :comment, size: "20x5", class: 'form-control' %>

      <%= f.submit submit_label(@order), class: "btn btn-success col-md-6" %>
    <% end %>

    <% if [email protected]_record? && current_employee.try(:admin?) %>
      <%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-2" %>
      <%= link_to "Delete", @order, method: :delete, data: { confirm: "Are you sure? The employee will be deleted! "}, class: "btn btn-danger col-md-offset-1 col-md-2" %>
    <% else %>
      <%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-5" %>
    <% end %>

  </div>
</div>

Upvotes: 1

Views: 104

Answers (1)

Ajay
Ajay

Reputation: 4251

Issue: Let's say your order has got many items and you want the items to be saved just when the order is created.

   order = Order.new(order_params)
   item = Item.new(name: 'item-name', product_info: 'etc etc')

   if order.save
    item.create
    order.items << item
   end

You need to follow similar approach in your case. just get the item_params properly and apply above rule. Try below approach, hope that helps. :)

def create
  @order = current_employee.orders.build(order_params)

  if @order.save
    item = params["order"]["order_items_attributes"]
    #please debug above one and try to get your items from params. 
    order_item = Item.create(item)
    #makes sure above item hold all item attributes then create them first
    @order.items << item 
    redirect_to @order
  end

end

Upvotes: 1

Related Questions