mix-fGt
mix-fGt

Reputation: 487

Rails - has_many :through association + save data for association

I have setup has_many and has_many :through association between a Order,User,Product and Order_detail model as a join table.

Models:

class Order < ActiveRecord::Base
  has_many :order_details
  belongs_to :user
  has_many :products, through: :order_details
end

class OrderDetail < ActiveRecord::Base
  belongs_to :order
  belongs_to :product
end

class Product < ActiveRecord::Base
  has_many :order_details
  has_many :orders, through: :order_details
end

class User < ActiveRecord::Base
  has_many :orders
end

How to save automatically for join table order_details. Now data save only to order table. Need to save all products to order_tables for current order and user

class OrdersController < ApplicationController
  before_action :authenticate_user!

  def index
    @order = Order.all
  end

  def new
    # @order = Order.new
    @order = current_user.orders.new
  end

  def create
    @order = current_user.orders.new(order_params)
    @order.date = Date.today.to_s
    if @order.save
       # i think this is bad wrong to implementation of functional)
       # params[:product_id].each do |detail_product_id|
       # @order.order_details.product_id = detail_product_id
       # @order.order_details.user_id = current_user
       # @order.order_details.save
       flash[:success] = "Order was successfully submitted"
       redirect_to new_order_path
    else
      render :new
    end
  end

 private

 def order_params
   params.require(:order).permit(:date, :product_id => [])
 end
end

My schema:

create_table "order_details", force: true do |t|
  t.integer  "order_id"
  t.integer  "product_id"
  t.integer  "quantity"
  t.integer  "price"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "orders", force: true do |t|
 t.integer "user_id"
 t.date    "date"
end

add_index "orders", ["user_id"], name: "index_orders_on_user_id", using: :btree

create_table "orders_products", id: false, force: true do |t|
  t.integer "order_id"
  t.integer "product_id"
end

create_table "products", force: true do |t|
  t.string   "product_name"
  t.integer  "product_price"
  t.datetime "created_at",       null: false
  t.datetime "updated_at",       null: false
  t.boolean  "available_status"
  t.string   "product_type"
end

Upvotes: 0

Views: 3057

Answers (4)

mix-fGt
mix-fGt

Reputation: 487

I change my association type to HABTM and that's enough for my situation. So..

models:

  class Order < ActiveRecord::Base
   has_and_belongs_to_many :products
   before_destroy { products.clear }
  end
  class Product < ActiveRecord::Base
   has_and_belongs_to_many :orders
  end

Order_controller:

def create
  order = current_user.orders.new(date: Date.today.to_s)
  @order_products = Product.where(id: order_params[:product_ids])
  order.products << @order_products
  if order.save
    #blalblal - sucsess
  else
    #blabla - alert-notice
  end

Upvotes: -1

mix-fGt
mix-fGt

Reputation: 487

Am i right add that rows for controller? Order_controller:

  def create
    @order = current_user.orders.new(order_params)
    @order.products = Product.find_by_id(order_params[:product_ids])
    @order.date = Date.today.to_s
    if @order.save
      flash[:success] = "Order was successfully submitted"
      redirect_to new_order_path
    else
      render :new
    end
  end

 private
 def order_params
   params.require(:order).permit(:date, order_details: [:product_id])
 end
end

order model:

  accepts_nested_attributes_for :order_details

I'm trying to save from 3 different types of products.

product_details

  • But how to take one product id from each part ? Because now I can choose only one product from all

View:

= simple_form_for(@order, html: {:class => 'well form-horizontal', :method  => :post, :action=> :create }) do |f|
    .col-xs-12.col-sm-6.col-md-8
    = render 'shared/error_messages', object: f.object
    %br
    = simple_fields_for :order_details do |od|
       = od.collection_radio_buttons :product_ids, Product.get_first_course, :id, :product_name ,:item_wrapper_class => 'inline'
      %hr
      = od.collection_radio_buttons :product_ids, Product.get_main_course, :id, :product_name, :item_wrapper_class => 'inline'
      %hr
      = od.collection_radio_buttons :product_ids, Product.get_drink, :id, :product_name,:item_wrapper_class => 'inline'
     %hr
   = f.button :submit, class: "btn btn-primary"

Upvotes: 0

hypern
hypern

Reputation: 887

Assuming that product_ids is an array of the product ids that you wish to add to the order, you could try assigning them in the following way and Rails should automagically create those association records for order_details when you then call @order.save

@order.products << Product.find_by_id(product_ids)

Upvotes: 1

noman tayyab
noman tayyab

Reputation: 365

In your view, add fields for order_details like:

     <%= f.fields_for :order_details do |od| %>
       <%= od.label 'your attribute for OrderDetail' %>
       <%=  # od.text_field 'your attribute' %>
     <% end %>

Then in your model, accept nested attributes for order_details like:

    accepts_nested_attributes_for :order_details

These are sample values, you can use this logic with your actual attributes.

In your controller, permit attributes for order_details like:

    params.require(:order).permit(:id, :name, order_details: [
      #attributes of order details
    ])

Upvotes: 1

Related Questions