the title sounds strange, so is my problem... I am building a simple app - I have a restaurant model which has_many meals that users can add to their carts as orders(the order model has a meal's id and a quantity). Now, I would like to be able to add options to the meals - e.g. a pizza meal would have "small", "medium", and "large" option which would affect the price of this item. How do you think is the best way to implement this?
My problem is that each meal might have different options, and options can have different prices. Therefore, I cannot just add an attribute "option" as a string. On the other hand, if I use another model, there would be too much nesting in the routes which I read is a bad practice:
resources :restaurants do
resources :meals do
resources :options
end
end
How to avoid this? Thank you for any suggestions!!
There's no reason not to have an options model to hold each of your options as necessary - though you should really consider a shallow route for restaurants as the restaurant itself has nothing to do with options. So instead your routes might look like:
resources :restaurants, shallow: true do
resources :meals do
resources :options
end
end
If you look at your routes now (rake routes) you'll see how much they change.
But its worth considering that you don't need the resources for options. A visitor won't be creating new options. They'll be adding existing options to a collection (meal + option = order .. or something similar) -- so you might only have that route in the admin namespace for creating new options for a menu.
Related
So this may be more of a convention question, but im writing a todo app to learn how to use rails as an API (Im somewhat intermediate with using rails normally) but this time im using it with React for the front end.
Im making a simple todo app, two models in particular being "Lists" and "ListItems". Lists has_many ListItems of course, and a ListItem belongs_to a List.
So naturally I have the routes set up like so:
resources :lists do
resources :list_items
end
Giving me routes similar to: /api/v1/lists/:list_id/list_items etc.., However I saw some people doing a similar app set it up like:
namespace :v1 do
resources :list_items
resources :lists
end
Which confuses me because how would you handle passing the actual "List" params to the route, when the route itself would not have a List_id param?
Or would this more be used for a join table somehow..but you would still have to populate the List_id regardless when creating a list_item for a specific list correct?
Is there a preferred way of doing this as far as routing goes? (And I suppose creating tables?) Since a has_many_through seems not really necessary in this case?
Unless there is more to the story, you are doing it the more conventional way. I suggest your can safely disregard that not-nested approach. The only enhancement I suggest is using shallow: true, like:
namespace :api do
namespace :v1 do
resources :lists do
resources :list_items, shallow: true
end
end
end
You can read more about shallow nesting in the guide.
I'm trying to understand the best way to architect a fairly simple relationship. I have a Job Model and a Category Model with a has_many relationship between them in a JobCategories model.
I'd like to have a page that lists all Jobs for a specific Category. Should the logic to pull this data be on the Category Controller (on the show action), or should I create a category method on the Job Controller? My gut tells me it should be on the Category side because a Category has Jobs, but it doesn't feel right that a Job would have the logic to pull all the Jobs for a given category.
Having said that, if I want the URL to be something that is more Job specific like:
domain/jobs/:id/{category-name} (for SEO purposes)
How would I structure the route so that it reads like the above, as opposed to
domain/categories/:id
which is what you'd get with resources :categories, only: [:show]
Thanks!
For a pretty slug, I'd suggest using FriendlyId on your categories model.
As for the routes, you will not get the desired route using resources :categories
One way to do it would be
resources :jobs, only: [] do
member do
get '/:slug' => 'categories#some_action'
end
end
the slug will be passed in your parameters.
This will yield a route like this
GET /jobs/:id/:slug(.:format) categories#some_action
UPDATE
the :slug is just an example for pretty url.
In your case you'd want to have :category_name. That would be passed into your controller through the params[:category_name].
One thing that I did start thinking when I re-read your question is that you want to show a list of jobs for a specific category. A url path like /jobs/:id/{category-name} shouldn't actually show a list of jobs as you are specifying an id which means a specific job. I think the url that you're looking to get is more along the lines of /jobs/{category-name}. Am I correct?
UPDATE 2
I suggest you read this Ruby On Rails Routing
UPDATE 3
Since you did want an url more like /jobs/{category-name}
You're routes should look like this
resources :jobs, only: [] do
collection do
get '/:category_name' => 'categories#some_action'
end
end
Good luck with your project! :D
So I am, like many before me, introducing the concept of "liking" items in my app. There are many different types of resources that could be liked (posts, replies, lists, actions etc.), and I am looking for the best way of structuring my routing file.
This may involve making a routes.rb file that looks something like:
resources lists do
resources posts do
resources replies do
resources likes
end
resources likes
end
resources likes
end
and a LikesController that doesn't know what it is meant to be load_and_authorize - ing:
class LikesController < ActionController:Base
load_and_authorize :list # only works if list_id exists in this request
load_and_authorize :post, through: :list # only works if post_id exists in this request
# and so on...
end
Are there any better ways of doing this? I had thought about just having a /likes path that everything posts to, but that means I can't use load_and_authorize, and also means it is harder to sensibly do things like Likes#index for a given list, post, reply, etc.
Thanks.
Instead of nesting Likes inside other resources, try this:
scope "/lists/:list_id(/posts/:post_id(/replies/:reply_id))" do
resources :likes
end
I believe that should get you a single set of LikesController routes which require a :list_id parameter and which optionally take a :post_id and a :reply_id. The url is pretty ugly, but I'm just trying to match the default for a quadruple-nested rails resource route. I recommend cleaning it up a bit.
Edit: It looks like this will also require some additional logic in your LikesController--like a before_filter that detects which params you've got and loads_and_authorizes only those objects. But that seems like a solvable problem to me?
I have an application, built with Rails 3.1.3, that has products and categories. The categories are related to other categories, so a category can be a parent or a child category. The products are then related to a child category.
Now, I'm thinking about how I should define the routes. Is it a good idea to somehow nest the categories and products resources? Ideally, I would like URLs like this:
example.com/parent/child/product-1234
like this:
example.com/clothes/underwear/some-socks-1234
or maybe like this to keep it restful?
example.com/p/clothes/c/underwear/....
But maybe that's a bit messy to achieve with the routes? I would have to nest the category with itself I guess?
Any ideas on how to achieve something like this?
EDIT:
Do I create the category routes like this:
resources :categories, :as => "parent" do
resources :categories, :as => "child"
end
or similar? It's not that important to have the product nested inside the categories. Maybe it will just make it hard to manage...
You can either be RESTful or not, you can't have it both ways.
If Category is a resource, then you can define routes with resource :categories statement and use Rails built-in support for REST. But then all categories should be accessible through categories/id. If you want different categories be accessible through different routes, then you are not RESTful and you can't use Rails REST support.
Alternatively you can say that parent and child categories are different resources and be RESTful again.
Edit: looking at my answer now (after it was accepted) I feel like I was probably too stiff. The answer should probably be "it depends" (as usual). If the application is CRUD (admin), I would stick to plain categories and nested products (shallow). On the storefront where you need nice looking url you can totally have non-REST routes and actions. It just mean more coding.
I would add an extra route besides the RESTful ones to support your URLs. This ignores the categories (they don't even have to exist) and routes directly to the product.
...
resources :products
resources :categories
match ':parent/:child/:product' => "products#show"
...
In your show action you can simply check which of params[:product] and params[:id] that is set and handle the different routes.
params[:parent] and params[:child] will also be available but i would suggest just looking for the product and get the category through that object since that should be more reliable.
Okay, so here's an example scenario. There is a student resource resources :students, and students has and belongs to many collections: resources :clubs, resources :majors, etc.
So we can set up our routes easily enough...
resources :clubs do
resources :students
end
resources :majors do
resources :students
end
resources :students do
resources :clubs
resources :majors
end
which generates us a bunch of standard RESTful routes
/clubs
/clubs/:id
/clubs/:club_id/students
/clubs/:club_id/students/:id
/majors
/majors/:id
/majors/:major_id/students
/majors/:major_id/students/:id
/students
/students/:id
/students/:student_id/clubs
/students/:student_id/clubs/:id
/students/:student_id/majors
/students/:student_id/majors/:id
So here's my question. With REST semantics, how would one delete a major for a student?
Browsing students under a major /majors/:major_id/students/:id would show that student in the specific major's 'collection'. But the route for deleting an :id, points to StudentsController#destroy, which would delete the student completely. Whoops! So maybe we go the other way, and perform DELETE on the resource at /students/:student_id/majors/:id and well now UnderwaterBasketweaving is now no longer offered at this school...Whoops!
Now granted, we could set the destroy method of ClubsController, MajorsController, or StudentsController to look for club_id, or major_id, or student_id, but lets say we also down the road want to add Fraternities and GraduatingClasses, etc. Each class would start to be comprised of huge switch conditionals looking to see what param was present... and then find the collection of either the top resource and remove the bottom resource, or vise versa. The Models themselves should decide whether they delete themselves if they have no more association records... 'Destroy' on that resource has become really a misnomer...
Is there an easier way to do this? Even popular restful rails plugins like make_resourceful or resource_controller would blow away UnderwaterBasketweaving when removing it from Joe's Majors or completely delete JohnDoe when removing him from the major UnderwaterBasketweaving. It would seem there's a potential for looking at the association to understand the desired effects of the semantics and what 'destroy' should do.
Then again, am I looking at this all wrong? Is it not UnderwaterBasketweaving -> Joe but UnderwaterBasketweaving+Joe as a single resource and what we're deleting is truly not Joe, nor UnderwaterBasketweaving, but the resource representing the combination? However, that's not easy when the controllers are Students and Majors, that in effect represent resources of the same name (MVC has really become RV...in the 'convention' approach and not developing Controllers that may have no bearing on a Model name, or the path to reach it) So you'd be deleting a Major or a Student; pick your poison...
How can I avoid managing conditionals across an infinite graph of associated resources where delete really isn't the intent when the delete is desired to be in context of a collection and not pertaining to its singularity...?
...major.student.delete... Is there a way for 'student' ActiveRecord object to know it was sent a 'delete' message in a method chain beginning with the 'major' AR object ?
Well the standard RESTful aproach is to use has_many :through and generate a cotroller for the association resource. Naming association resources is always difficult, but I'll attempt for this example.
resources :majors do
resources :studies
resources :students
end
resources :students do
resources :studies
resources :majors
end
The models would be of course:
class Major < ActiverRecord::Base
has_many :studies
has_many :students, :through => :studies
end
etc. (comment if you want me to elaborate)
Then for a student you wouldn't DELETE it's associated #student.major but his #student.studies.where :major => #major.