Difference Between Collection Route and Member Route in Ruby on Rails

difference between collection route and member route in ruby on rails?

A member route will require an ID, because it acts on a member. A collection route doesn't because it acts on a collection of objects. Preview is an example of a member route, because it acts on (and displays) a single object. Search is an example of a collection route, because it acts on (and displays) a collection of objects.

What does a member and collection route mean?

They're both ways to add additional actions to a resource-based route in Rails.

  • A member route requires an ID, because it acts on a member.
  • A collection route doesn't require an ID because it acts on a collection of objects.

I like to think of them in terms of RESTful URLs. Consider the basics for a resource/model Foo

GET    /foo            # FooController#index
GET /foo/:id # FooController#show
GET /foo/new # FooController#new
POST /foo # FooController#create
GET /foo/:id/edit # FooController#edit
PUT /foo/:id # FooController#update
DELETE /foo/:id # FooController#destroy

Notice how:

  • Some routes have :id placeholders for Foo.id, and so refer to a specific Foo
  • Some routes have no :id, and thus refer to all Foos (and/or no specific foo, as in #new and #create)
  • Some routes (index/create, show/update/destroy) have the same URL, and use HTTP methods to differentiate between them
  • Some routes (edit/show) are basically the same (method & URL prefix) except for a different suffix (including "no suffix") at the end.

Member routes and collection routes let you add additional routes/actions using the same techniques as I listed above.

A member route adds a custom action to a specific instance using the URL suffix and HTTP method you provide. So, if you had a member route declaration of :member => { :bar => :get }. you'd get an additional route of:

GET    /foo/:id/bar # FooController#bar

Note how it overloads GET /foo/:id in the same way that `edit' does. This is how you'd implement a "delete" action that provides a UI for the "destroy" action.

Similarly, a collection route adds an overload to the collection and/or a non-specific instance (it's up to you to decide exactly what it implies). So, if you declared :collection => { :baz => :get }, you'd get an additional route:

GET    /foo/baz        # FooController#baz

...in very much the same way as new.

You can also customize the HTTP method.

For example, I just recently had a project where I needed a "reply" action on a Comment. It's basically the same idea as Comment#create (which uses POST), except that it's in reference to a specific parent Comment. So, I created a member route: :member => { :reply => :post }. This gave me:

POST   /comment/:id/reply   # CommentController#reply

This keeps the routes restful while still expanding upon the basic 7 actions.

What's the difference between :new, :collection and :member routes?

The difference is the URL generated.

Let's guess three resources :

map.resources :users, :collection => { :rss => :get }
map.resources :users, :member => { :profile => :get }
map.resources :users, :new => { :draft => :get }

The first route will create :

/users/rss

With nothing between the controller name and the action name. We don't need any other parameter to get the user's list rss feed.

The second one will create the action "profile" as a member of the object. So we'll have :

/users/1/profile

The "1" is the user's to_param. We need a user's id to display a profile.

The third one will create the action "draft" as a member of the new action. So we'll have :

/users/new/draft

The "draft" action displays a draft of the user before accepting its creation.

So that's the difference between :collection, :member and :new. Every of them creates different routes, each one with their own purpose.

What's the difference between :new, :collection and :member routes?

The difference is the URL generated.

Let's guess three resources :

map.resources :users, :collection => { :rss => :get }
map.resources :users, :member => { :profile => :get }
map.resources :users, :new => { :draft => :get }

The first route will create :

/users/rss

With nothing between the controller name and the action name. We don't need any other parameter to get the user's list rss feed.

The second one will create the action "profile" as a member of the object. So we'll have :

/users/1/profile

The "1" is the user's to_param. We need a user's id to display a profile.

The third one will create the action "draft" as a member of the new action. So we'll have :

/users/new/draft

The "draft" action displays a draft of the user before accepting its creation.

So that's the difference between :collection, :member and :new. Every of them creates different routes, each one with their own purpose.

rails routing member and non-member nesting

The member and collection methods are meant to add additional RESTful actions to resources

resources :writers do
member do
post :favorite
end

collection do
get :unpublished
end
end

They are not intended for nesting resources

# bad
resources :writers do
member do
resources :books
end
end

# good
resources :writers do
resources :books
end

What are the consequences?

Using member here will result in the route

GET    /writers/:id/books/:id(.:format)    

Which means that the id param is ambigous! It could be either the id of the book or the author! Not good! Not using member would give us params[:writer_id] which we can use to fetch the parent record.

GET  /writers/:writer_id/books/:id(.:format)   books#show

See:

  • Rails Routing from the Outside In

Rails: Difference between declaring a route directly inside the `resources` block vs. enclosing it with a `member` block

First of all, if we write the member block or directly write the get routes inside the resources both are considered as member routes.

Its the rails convention to differentiate between both of the routes. If we write the member block it is considered that all the routes declared within that block are declared from the member block explicitly.

resources :animals do
member do
get 'info'
end
end

info_animal GET /animals/:id/info(.:format) animals#info

But if we directly declare get or other routes inside the resources block this will also create the same member route except that the resource id value will be available in params[:animal_id] instead of params[:id]. Route helpers will also be renamed from info_animal_url and info_animal_path to animal_info_url and animal_info_path. I think this is to make difference that request is not coming from the member block.

resources :animals do
get 'info'
end

animal_info GET /animals/:animal_id/info(.:format) animals#info

If we write get route with the on: option with value :member inside the resources directly then this will be treated same as the member block route

resources :animals do
get 'info', on: :member
end

info_animal GET /animals/:id/info(.:format) animals#info

Rails routes: Nested, Member, Collection, namespace, scope and customizable

**Collection & Member routes**
  • A member route requires an ID, because it acts on a member.

  • A collection route doesn't require an ID because it acts on a
    collection of objects

:member creates path with pattern /:controller/:id/:your_method

:collection creates path with the pattern /:controller/:your_method

For example :

map.resources :users, :collection => { :abc => :get } => /users/abc
map.resources :users, :member => { :abc => :get } => /users/1/abc

**Scopes & Namespaces routes**

namespace and scope in the Rails routes affect the controller
names, URIs, and named routes.

The scope method gives you fine-grained control:

scope 'url_path_prefix', module: 'module_prefix', as: 'named_route_prefix' do
resources :model_name
end

For Example :

scope 'foo', module: 'bar', as: 'baz' do
resources :posts
end

produces routes as :

  Prefix Verb         URI Pattern                  Controller#Action
baz_posts GET /foo/posts(.:format) bar/posts#index
POST /foo/posts(.:format) bar/posts#create
new_baz_post GET /foo/posts/new(.:format) bar/posts#new
edit_baz_post GET /foo/posts/:id/edit(.:format) bar/posts#edit
baz_post GET /foo/posts/:id(.:format) bar/posts#show
PATCH /foo/posts/:id(.:format) bar/posts#update
PUT /foo/posts/:id(.:format) bar/posts#update
DELETE /foo/posts/:id(.:format) bar/posts#destroy

The namespace method is the simple case — it prefixes everything.

namespace :foo do
resources :posts
end

produces routes as :

   Prefix Verb        URI Pattern                  Controller#Action
foo_posts GET /foo/posts(.:format) foo/posts#index
POST /foo/posts(.:format) foo/posts#create
new_foo_post GET /foo/posts/new(.:format) foo/posts#new
edit_foo_post GET /foo/posts/:id/edit(.:format) foo/posts#edit
foo_post GET /foo/posts/:id(.:format) foo/posts#show
PATCH /foo/posts/:id(.:format) foo/posts#update
PUT /foo/posts/:id(.:format) foo/posts#update
DELETE /foo/posts/:id(.:format) foo/posts#destroy

**Constraints & Redirect**

Rails routes are executed sequentially, you can mimic conditional
login in the following manner:

match '/route' => 'controller#action', :constraints => Model.new
match '/route' => 'user#action'

The first line checks whether the conditions of the constraint are met (i.e., if the request is emanating from a Model domain). If the constraint is satisfied, the request is routed to controller#action.

We can add constraints to routes for multiple uses like for ip-matching, params matching, restrict format parameter, request-based restrictions etc as :

- ip-matching
=> resources :model, constraints: { ip: /172\.124\.\d+\.\d+/ }
- filtering id params
=> match 'model/:id', to: 'model#show' ,constraints: { id: /\d+/}, via: :get
- restrict format params
=> match 'model/:id', to: 'model#show' ,constraints: { format: 'json' }, via: :get
- request-based constraints
=> get 'admin/', to: 'admin#show', constraints: { subdomain: 'admin' }

How does rails differentiate between users/:id and users/new routes?

Rails does not differentiate, it's just searches for the first record, which is suitable for the conditions

That's why new is generated earlier than id

Example 1:

get '/feedbacks/count' => 'feedbacks#count'
get '/feedbacks/:id' => 'feedbacks#show'

Example 2:

resources :feedbacks do
member do
get '/feedbacks/count' => 'feedbacks#count'
end
end

You can read about it here

Rails combine member & collection in routes.rb

Combine member and collection in resources block. Like this,

resources :conversations, only: [:index, :show, :destroy] do
member do
post :reply
post :restore
end
collection do
delete :empty_trash
end
end

Or you may combine it like this also,

resources :conversations, only: [:index, :show, :destroy] do
post :reply, on: :member
post :restore, on: :member
delete :empty_trash, on: :collection
end


Related Topics



Leave a reply



Submit