Kristiyan Tsvetanov
Kristiyan Tsvetanov

Reputation: 1047

Nesting in Rails

I am writing an app which has a Restaurant, Menu, Category, and Meal models. How to implement these without nesting more than 2 levels deep?

class Restaurant < ActiveRecord::Base
  has_many :menus
  has_many :categories, through: :menus
  has_many :meals, through: :categories
end

class Menu < ActiveRecord::Base
  has_many :categories
  has_many :meals, through: :categories
end

class Category < ActiveRecord::Base
  has_many :meals
end

class Meal < ActiveRecord::Base
  belongs_to :category
end

What I am asking for is how to write the routes. Without nesting the links would look like:

restaurants GET      /restaurants(.:format)          restaurants#index
....
menus GET            /menus(.:format)                menus#index
....
categories GET       /categories(.:format)           categories#index
....

How am I then going to reach restaurants/1/menus/2/categories/3/meals/2 for example?

Upvotes: 1

Views: 143

Answers (1)

Martin M
Martin M

Reputation: 8668

The standard way is to not use nested routes to access items.
With the normal routes

Rails.application.routes.draw do
  resources :restaurants
  resources :menus
  resources :meals
  resources :categories
end

you get all the routes you need:

         Prefix Verb   URI Pattern                     Controller#Action
          menus GET    /menus(.:format)                menus#index
                POST   /menus(.:format)                menus#create
       new_menu GET    /menus/new(.:format)            menus#new
      edit_menu GET    /menus/:id/edit(.:format)       menus#edit
           menu GET    /menus/:id(.:format)            menus#show
                PATCH  /menus/:id(.:format)            menus#update
                PUT    /menus/:id(.:format)            menus#update
                DELETE /menus/:id(.:format)            menus#destroy
    restaurants GET    /restaurants(.:format)          restaurants#index
                POST   /restaurants(.:format)          restaurants#create
 new_restaurant GET    /restaurants/new(.:format)      restaurants#new
edit_restaurant GET    /restaurants/:id/edit(.:format) restaurants#edit
     restaurant GET    /restaurants/:id(.:format)      restaurants#show
                PATCH  /restaurants/:id(.:format)      restaurants#update
                PUT    /restaurants/:id(.:format)      restaurants#update
                DELETE /restaurants/:id(.:format)      restaurants#destroy
          meals GET    /meals(.:format)                meals#index
                POST   /meals(.:format)                meals#create
       new_meal GET    /meals/new(.:format)            meals#new
      edit_meal GET    /meals/:id/edit(.:format)       meals#edit
           meal GET    /meals/:id(.:format)            meals#show
                PATCH  /meals/:id(.:format)            meals#update
                PUT    /meals/:id(.:format)            meals#update
                DELETE /meals/:id(.:format)            meals#destroy
     categories GET    /categories(.:format)           categories#index
                POST   /categories(.:format)           categories#create
   new_category GET    /categories/new(.:format)       categories#new
  edit_category GET    /categories/:id/edit(.:format)  categories#edit
       category GET    /categories/:id(.:format)       categories#show
                PATCH  /categories/:id(.:format)       categories#update
                PUT    /categories/:id(.:format)       categories#update
                DELETE /categories/:id(.:format)       categories#destroy

So if you have a @meal of restaurant#1 with id:5 you link to it by

<%= link_to @meal %>

and get the path /meals/5.

OK, it can be handy to manage meals by restaurant (especially list and new).
You can add nested routes just for that:

Rails.application.routes.draw do
  resources :restaurants do
    resources :menus, only: [:index,:new]
  end
  resources :menus do
    resources :meals, only: [:index,:new]
  end
  resources :meals
  resources :categories
end

and get some new routes:

             Prefix Verb   URI Pattern                                     Controller#Action
   restaurant_menus GET    /restaurants/:restaurant_id/menus(.:format)     menus#index
new_restaurant_menu GET    /restaurants/:restaurant_id/menus/new(.:format) menus#new
   restaurant_meals GET    /restaurants/:restaurant_id/meals(.:format)     meals#index
new_restaurant_meal GET    /restaurants/:restaurant_id/meals/new(.:format) meals#new
...
         menu_meals GET    /menus/:menu_id/meals(.:format)                 meals#index
      new_menu_meal GET    /menus/:menu_id/meals/new(.:format)             meals#new

Nesting is never deeper than 1.
Now you can link to all meals of one restaurant by

<%= link_to 'All Meals', restaurant_meals(@restaurant) %>

and to all meals of a menu by

<%= link_to 'All Meals', menu_meals(@menu) %>

As you can see, all these link call meal#index, so your controller has to take care of the different lists, i.e.:

class MealsController < ApplicationController

  # GET /meals
  # GET /restaurants/:restaurant_id/meals
  # GET /menus/:menu_id/meals
  def index
    if params[:restaurant_id].present?
      @restaurant = Restaurant.find params[:restaurant_id]
      @meals = @restaurant.meals
    elsif params[:menu_id].present?
      @menu = Menu.find params[:menu_id]
      @meals = @menu.meals
    else
      @meals = Meal.all
    end
  end

end

Upvotes: 1

Related Questions