Kristiyan Tsvetanov
Kristiyan Tsvetanov

Reputation: 1047

Rails Model Configuring

I am learning Rails by writing a simple restaurant app. I have a restaurant model, meal model and an order model. I want to be able to send order to the restaurant by clicking buy button next to a certain meal. I am able to create an order and send it to the restaurant but I do not know how to link that order to the meal.

These are the models and the routes:

class Order < ActiveRecord::Base
  belongs_to :restaurant
end

class Restaurant < ActiveRecord::Base
    has_many :meals
    has_many :orders
end

class Meal < ActiveRecord::Base
  belongs_to :restaurant
end

resources :restaurants do
  resources :staff_members
  resources :meals
  resources :orders
end

Meals are listed and below each meal, I render a form for creating an order. This is wrong for sure but I cannot find how to do that properly.. I would be so thankful to anybody who could help with this!

Upvotes: 0

Views: 82

Answers (3)

Vishal
Vishal

Reputation: 394

Try has_many :through association like this.

class Meal < ActiveRecord::Base
  has_many :orders
  belongs_to :restaurant
end

class Order < ActiveRecord::Base
  belongs_to :meal
  has_one :restaurant, through: :meal
end

class Restaurant < ActiveRecord::Base
    has_many :meals
    has_many :orders, through: :meals
end

Basically, you'll have to create meal that belongs_to a restaurant. Then for each of the meals in the list of meals for a restaurant, you'll have a form with a button to order. That form is to create an order that belongs_to the corresponding meal. In turn, the meal will automatically belong to the restaurant through the associations mentioned above because the order belongs to the meal and the meal belongs to the restaurant.

Edit:

(Assuming that you have a username field in the Order model as an example; and your route is set as get '/restaurants/:restaurant_id/meals/:meal_id/orders/new' => 'orders#new' )

Orders Controller

def new
    @meal = Meal.find(params[:meal_id])
end

def create
    @meal = Meal.find(params[:meal_id])

    @order = @meal.orders.build(order_params)

    if @order.save
        flash[:success] = "Successfully created the order..."
        redirect_to root_url
    else
        flash[:danger] = "Failed to create the order..."
        redirect_to request.referrer    # Redirects back to the last url
    end
end

    private
        def order_params
            params.require(:order).permite(:username, :more_order_fields)   # Add other fields for order here
        end

Form for new order

<%= form_for(@meal.orders.build) do |order_form| %>
    <%= hidden_field_tag :meal_id, params[:meal_id] %>

    <%= order_form.label :username %>
    <%= order_form.text_field :username %>

    <%= order_form.submit "Create Order" %>
<% end %>

I haven't tested the code yet. But hope it gives you a good idea of the procedure at least.

Upvotes: 1

Hieu Pham
Hieu Pham

Reputation: 6692

My idea is:

  • A meal belongs to a restaurant and has available_amount
  • When an order is created, it has many meals but these meals are not meal actually, it is only 1 of available_amount meals.

So I suggest adding a new model called OrderedMeal, the new schema will be like:

class Order < ActiveRecord::Base
  belongs_to :restaurant
  has_many :ordered_meals # Change here
end

class Restaurant < ActiveRecord::Base
  has_many :meals
  has_many :orders
end

class Meal < ActiveRecord::Base
  belongs_to :restaurant
end

class OrderedMeal < ActiveRecord::Base
  belongs_to :meal
  belongs_to :order
end
  • So when you create an order, you just link meal with order by creating ordered_meals, then decrease the value of available_amount for each meal, I just suggest having available_amount if it is actually needed!
  • Btw, the relation Order belongs to Restaurant may be redundant since you can get the restaurant info from ordered_meals ----> meal ----> restaurant, you can consider to remove it.

Upvotes: 1

Deepesh
Deepesh

Reputation: 6418

I will do it like this:

In routes:

resources :meals do
  member do
    post :new_order
  end
end

Now this will create a new member route. Now I will submit the form to this action and in the meals_controller I will write:

def new_order
  @meal = Meal.find(params[:id]) # Skip if already present in a before_action
  if @meal.create_order(current_user)
    # success response
  else
    # error message
  end
end

In the Meal model create an instance method:

def create_order(user)
  # user because obviously order will belong to some user
  restaurant.orders.create(meal: self, user: user, other attributes)
end

There can be more better ways but mine would be this. And this code is not tested may have errors but will give you an idea.

Hope this helps.

Upvotes: 1

Related Questions