ruby-on-railsrubysimple-form-for

simple_form_for how to pass params to controller


I want to pass params from event/id(show page) to my order_controller.

I use simple_form_for to pass event.id and promocode that input by user

#event.show.html.haml
= simple_form_for order_url, url: orders_path(@event, :promocode), method: :post do |f|
   = f.hidden_field :event_id, params: {id: @event.id}
   = f.input :promocode, value: :promocode, class: 'form-control', placeholder: "Enter your PromoCode"
   = f.submit 'APPLY PromoCode'

IDK if a need hidden_field to pass event_id

#order_controller
class OrdersController < ApplicationController
  before_action :order, only: %i[show]
  
  def index
    @orders = Order.all.order(created_at: :desc).page(params[:page]).per(5)
  end

  def show; end

  def create
    @order = Order.create(title: event.title, user_id: current_user.id, event_id: event.id, order_amount: event.price, order_currency: event.currency)
    if !promo.nil?
      redirect_to_order
    elsif @order.save
      redirect_to checkout_create_path(id: @order.id)
    else
      redirect_to event, alert: 'Something went wrong, try again later'
    end
  end
  
  def redirect_to_order
    promo_validate
    order_amount_promo_code = @order.order_amount - promo.promo_code_amount
    order.update(order_amount: order_amount_promo_code)
    redirect_to @order
  end

  def promo_validate
    if promo.present? && promo.promo_code_amount.positive? && promo.promo_code_currency == event.currency
      promo.update(order_id: @order.id)
    else
      redirect_to event, alert: "This PromoCode is invalid or Your PromoCode Currency doesn't match with Event"
    end
  end

  private

  def promo
    @promo ||= PromoCode.find_by(uuid: params[:promocode])
  end

  def event
    @event ||= Event.find(params[:id])
  end

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

  def order_params
    params.require(:order).permit(:title, :event_id, :promocode, :event)
  end

end

I'm using methods def event and def promo to take this params from view. Also my routes look like this.


Solution

  • I would nest the route:

    resources :events do
      resources :orders, shallow: true
    end
    

    This creates an explicit relationship between the two resources that can be seen by just looking at the URL. To create a order tied to an even you send a POST request to /events/:event_id/orders.

    class EventsController
      def show
        # ..
        @order = @event.orders.new
      end
    end 
    
    = simple_form_for [@event, @order] do |f|
      = f.input :promocode, value: :promocode, class: 'form-control', placeholder: "Enter your PromoCode"
      = f.submit 'APPLY PromoCode'
    
    class OrdersController < ApplicationController
      # POST /events/:id/orders
      def create
        @event = Event.find(params[:event_id])
        @order = @event.orders.new(title: @event.title, user: current_user order_amount: @event.price, order_currency: @event.currency)
        begin
          @promo = PromoCode.find_by!(uuid: params[:order][:promocode])
        rescue ActiveRecord::RecordNotFound
          @order.errors.add(:promocode, 'is invalid')
        end
    
        if @order.save
          redirect_to checkout_create_path(id: @order.id)
        else
          redirect_to @event, alert: 'Something went wrong, try again later'
        end
      end
      # ...
    end
    

    Other then that your handling of promo codes is very iffy. Instead of monkying around and deducting the rebate from the "amount" by updating the record you should store both the original sales price and the rebate and then calculate the total at checkout - which should also be stored separately. Not doing so amounts to pretty dismal record keeping and might get you in trouble - when it comes to money always play it safe.