Reputation: 3343
I'm wondering if I should create a category controller in this case. Presently I have an items controller that has #index and #show actions. I was thinking about adding a filter for a specific category in the items#index action - but it seems much easier to do from a category controller. Here are my associations which effectively sets up a has_many through relationship between items and categories:
class Category < ActiveRecord::Base
has_many :categorizations
has_many :items, :through => :categorizations
end
class Item < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :item
belongs_to :category
end
I need the API I am building to return "a list of available items for a particular category". It is very easy to this from a category controller(which I do not have yet) like so:
category.items
Is it better and more restful to do this? Or should I create a filter in my items#index action, which presently looks like this:
# Returns full list of items
def index
@items = Item.all
render json: @items
end
Of course, if you have any ideas that are more efficient/inline with best practices - please let me know!
Thanks!
EDIT - One Solution:
I decided to add a categories controller, so that I can access the available items for a particular category using the following relative path:
categories/:id/available_items
class CategoriesController < ApplicationController
def available_items
@available_items = Category.find(params[:id]).items.available
render json: @available_items
end
end
The criteria was to return all items, that are associated with a particular category, and have a status of 'available'.
EDIT:
I'm finding that Item.where(category: 1)
isn't returning all items which are categorized under category 1. Please see below the byebug console output:
1: class ItemsController < ApplicationController
2: # Returns full list of items
3: def index
4: @items = Item.all
5: byebug
=> 6: end
7:
(byebug) Item.where(category: 1)
Item Load (0.6ms) SELECT "items".* FROM "items" WHERE "items"."category" = 1
#<Item::ActiveRecord_Relation:0x007fb5d1a37f08>
(byebug) Category.find(1).items
Category Load (0.7ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT 1 [["id", 1]]
Item Load (1.7ms) SELECT "items".* FROM "items" INNER JOIN "categorizations" ON "items"."id" = "categorizations"."item_id" WHERE "categorizations"."category_id" = $1 [["category_id", 1]]
#<ActiveRecord::Associations::CollectionProxy [#<Item id: 1, title: "Gorgeous Cotton Pants", description: "Dolor dicta suscipit aut cupiditate quia officiis ...", price: 73960, status: 0, published_date: "2016-07-14 05:35:49", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 5, title: "Sleek Marble Shoes", description: "Qui mollitia corporis qui placeat. Reiciendis ea s...", price: 35146, status: 0, published_date: "2016-07-14 05:45:02", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 7, title: "Rustic Concrete Lamp", description: "Sit odio non exercitationem. Atque non sapiente vo...", price: 82016, status: 2, published_date: "2016-07-13 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 10, title: "Awesome Wooden Table", description: "Possimus consequatur nulla. Quidem molestiae volup...", price: 59519, status: 2, published_date: "2016-07-09 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 12, title: "Lightweight Concrete Bag", description: "Amet ullam assumenda eligendi consectetur quae. Bl...", price: 72081, status: 2, published_date: "2016-07-16 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 2>, #<Item id: 13, title: "Mediocre Plastic Computer", description: "Excepturi modi est non qui iusto. Molestiae offici...", price: 94357, status: 2, published_date: "2016-07-15 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 2>, #<Item id: 15, title: "Incredible Plastic Bag", description: "Vel voluptas ducimus soluta atque voluptatem eum. ...", price: 15661, status: 2, published_date: "2016-07-14 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 2>, #<Item id: 16, title: "Lightweight Iron Watch", description: "Id sequi rerum dolor sit sunt nemo laborum. Omnis ...", price: 65306, status: 4, published_date: "2016-07-11 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 17, title: "Rustic Linen Chair", description: "Explicabo qui ad nihil. Voluptatem placeat autem. ...", price: 39752, status: 4, published_date: "2016-07-04 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>, #<Item id: 18, title: "Mediocre Copper Car", description: "Minus qui ut est non vero saepe. Qui sed quos et v...", price: 87765, status: 4, published_date: "2016-07-05 00:00:00", created_at: "2016-07-17 05:15:07", updated_at: "2016-07-17 05:15:07", seller_id: 1>]>
Upvotes: 0
Views: 276
Reputation: 80041
Both of your proposed solutions are valid, but let's look at some code:
resources :categories do
resources :items
end
# /categories/42/items/7
On its own, Rails would route this to the ItemsController
, which would be responsible for doing something with the category_id
that is passed in. If your application architecture/logic doesn't really ask to be done differently, I would start with this approach.
resources :categories, :items
class CategoriesController < ApplicationController
def index
@categories = Category.all
end
def show
@category = Category.includes(:items).find params[:id]
end
end
This approach is great if the notion of showing a controller would naturally show its items. This might not be appropriate in your case given the typical nature of many-to-many relationships, but it really depends on the context.
If you find that your items#show
or items#index
methods are starting to get overly conditional, I would look into changing up your routing and adding a controller:
class CategorizedItemsController < ApplicationController
# ...
end
resources :categories
resources :items
scope '/categories/:category_id/' do
resources :items, controller: :categorized_items
end
This is less obvious to another developer joining your project, and starts to make reasoning about your routes a little more challenging, so I wouldn't begin with it. It's a great solution, though, and one that you shouldn't hesitate to adopt if you find your existing controller hierarchy failing to represent your actions within the bounds of RESTful routing.
Cheers!
Upvotes: 1