Model Association - ruby-on-rails

Hi I have a little problem with associations, I'm working with Rails 3.2, I want to create a special blog, this blog has many sections and this sections has many articles and an article belongs to a Category. So my models are:
class Article < ActiveRecord::Base
belongs_to :section
belongs_to :category
class Category < ActiveRecord::Base
has_many :articles
class Section < ActiveRecord::Base
has_many :article
So articles belongs_to section, in routes.rb :
resources :sections do
resources :articles
end
Rake Routes:
POST /sections/:section_id/articles(.:format) articles#create
new_section_article GET /sections/:section_id/articles/new(.:format) articles#new
edit_section_article GET /sections/:section_id/articles/:id/edit(.:format) articles#edit
section_article GET /sections/:section_id/articles/:id(.:format) articles#show
PUT /sections/:section_id/articles/:id(.:format) articles#update
DELETE /sections/:section_id/articles/:id(.:format) articles#destroy
sections GET /sections(.:format) sections#index
POST /sections(.:format) sections#create
new_section GET /sections/new(.:format) sections#new
edit_section GET /sections/:id/edit(.:format) sections#edit
section GET /sections/:id(.:format) sections#show
PUT /sections/:id(.:format) sections#update
DELETE /sections/:id(.:format) sections#destroy
So my question is How do I create a Categories_controller with index and show action.
to show the articles that belongs to that category, and have a link_to in views(Category#show) for the articles path.

Assuming that you do need nested routes to fit your domain model (ex. articles need to display differently based on whether they are being viewed in the context of a category or a section), then you can just create another set of nested routes like so:
routes.rb
resources :categories, only: [:index, :show] do
resources :articles
end
That will set up routes to find your categories and articles by categories, but you will have to fork your logic in your ArticlesController on params[:category_id] or params[:section_id] like so:
class ArticlesController < ApplicationController
def index
if params[:section_id]
# handle section_articles_path to display articles by section
elsif params[:category_id]
# handle category_articles_path to display articles by category
else
# handle articles_path to display all articles (assuming you have resources :articles in routes)
end
end
# ...
end
The link to show an article based on a category will then use the generated route helper methods created for you.
categories/show.html.erb
<ul>
<% #category.articles.each do |article| %>
<li><%= link_to article.title, category_article_path(#category, article) %></li>
<% end %>
</ul>
You could then, of course, refactor the view code to render the collection as its own partial rather than manually iterating, but I'll leave it at that for now...
If you don't need to handle the context differently (accessing an article through a section versus a category), I'd recommend just setting up three basic routes (:articles, :sections, :categories) and going from there.

It's Very much simple
match 'catagories' => "catagories#index" and match 'catagories/show/:id' => "catagories#show"
In show action #articles = Article.where("category_id",params[:id])
#articles = Article.where("category_id",params[:id]) This will solve your purpose

Related

How to name nested controllers and routes?

I'm working on creating a wiki app from scratch with the following organization:
Main Controller: Wiki
Nested Controller: WikiCategories
Nested Controller: WikiArticles
In my config/routes.rb I have:
resource :wiki do
resources :wiki_categories, :as => :categories
resources :wiki_articles, :as => :articles
end
I've chosen to name the categories and articles controllers as WikiCategories and WikiArticles to differentiate from other category and article controllers that I want to make in the future under a blog nesting.
This gives me the following routes:
/wiki/wiki_categories/new
/wiki/wiki_articles/new
Is there any way to rewrite the routes to be:
/wiki/categories/new
/wiki/articles/new
... while still using the WikiCategories and WikiArticles controller names?
I've chosen to name the categories and articles controllers as WikiCategories and WikiArticles to differentiate from other category and article controllers that I want to make in the future under a blog nesting.
IMO, it seems like you're bucking convention a bit. As discussed in Controller Namespaces and Routing, why not do:
namespace :wiki do
resources :categories, :articles
end
Which will give you:
wiki_categories GET /wiki/categories(.:format) wiki/categories#index
POST /wiki/categories(.:format) wiki/categories#create
new_wiki_category GET /wiki/categories/new(.:format) wiki/categories#new
edit_wiki_category GET /wiki/categories/:id/edit(.:format) wiki/categories#edit
wiki_category GET /wiki/categories/:id(.:format) wiki/categories#show
PATCH /wiki/categories/:id(.:format) wiki/categories#update
PUT /wiki/categories/:id(.:format) wiki/categories#update
DELETE /wiki/categories/:id(.:format) wiki/categories#destroy
wiki_articles GET /wiki/articles(.:format) wiki/articles#index
POST /wiki/articles(.:format) wiki/articles#create
new_wiki_article GET /wiki/articles/new(.:format) wiki/articles#new
edit_wiki_article GET /wiki/articles/:id/edit(.:format) wiki/articles#edit
wiki_article GET /wiki/articles/:id(.:format) wiki/articles#show
PATCH /wiki/articles/:id(.:format) wiki/articles#update
PUT /wiki/articles/:id(.:format) wiki/articles#update
DELETE /wiki/articles/:id(.:format) wiki/articles#destroy
Then, create namespaced controllers, something like:
app/controllers/wiki/categories.rb
class Wiki::CategoriesController < ApplicationController
...
end
and
app/controllers/wiki/articles.rb
class Wiki::ArticlesController < ApplicationController
...
end
Yes it is by specifying the controller, the resource can then be named whichever way you like.
resource :wiki do
resources :categories, controller: 'wiki_categories'
resources :articles, controller: 'wiki_articles'
end
Please see the guide for further information.
You can use the path: option as follows:
resource :wiki do
resources :wiki_categories, path: 'categories', :as => :categories
resources :wiki_articles, path: 'articles', :as => :articles
end
Which will give you:
/wiki/categories/...
/wiki/articles/...
See Translated Paths section of the guides for further details.

Rails link_to polymorphic parent, which can have a nested route

I have the Comment model, which is polymorphic associated to commentable models like Project, User, Update etc. And I have a page where a user can see every User's comment. I want a link near each comment with an address of an object this comment is associated with.
I could write something like that:
link_to 'show on page', Object.const_get(c.commentable_type).find(c.commentable_id)
But this will work only for not nested routes (like User). Here's how my routes look like:
resources :users do
resources :projects, only: [:show, :edit, :update, :destroy]
end
So when I need a link to a Project page, I will get an error, because I need a link like user_project_path.
How can I make Rails to generate a proper link? Somehow I have to find out if this object's route is nested or not and find a parent route for nested ones
You could use a bit of polymophic routing magic.
module CommentsHelper
def path_to_commentable(commentable)
resources = [commentable]
resources.unshift(commentable.parent) if commentable.respond_to?(:parent)
polymorphic_path(resources)
end
def link_to_commentable(commentable)
link_to(
"Show # {commentable.class.model_name.human}",
path_to_commentable(commentable)
)
end
end
class Project < ActiveRecord::Base
# ...
def parent
user
end
end
link_to_commentable(c.commentable)
But it feels dirty. Your model should not be aware of routing concerns.
But a better way to solve this may be to de-nest the routes.
Unless a resource is purely nested and does not make sense outside its parent context it is often better to employ a minimum of nesting and consider that resources may have different representations.
/users/:id/projects may show the projects belonging to a user. While /projects would display all the projects in the app.
Since each project has a unique identifier on its own we can route the individual routes without nesting:
GET /projects/:id - projects#show
PATCH /projects/:id - projects#update
DELETE /projects/:id - projects#destroy
This lets us use polymorphic routing without any knowledge of the "parent" resource and ofter leads to better API design.
Consider this example:
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :projects
resources :users do
# will route to User::ProjectsController#index
resources :projects, module: 'user', only: [:index]
end
end
class ProjectsController < ApplicationController
def index
#projects = Project.all
end
# show, edit, etc
end
class User::ProjectsController < ApplicationController
def index
#user = User.joins(:projects).find(params[:user_id])
#projects = #user.comments
end
end
This would let us link to any project from a comment by:
link_to 'show on page', c.commentable
And any users projects by:
link_to "#{#user.name}'s projects", polymorphic_path(#user, :projects)

Routing error for rails - uninitialized constant SubscribersController

I have a Subscriber model that takes in a "phone_number" and a "visit" integer. I have two controllers Subscribers and Visits(super and sub) I have never worked with nested controllers before and I'm having some issues with namespace I believe. Because I getting back the uninitialized constant error. Basically the subscriber controller signs up a subscriber and the visit controller counts the amount of times they've visited by user input of their phone_number. Why am I getting this error? I'll show my code for clarity.
CONTROLLERS
class Subscribers::VisitsController < ApplicationController
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if #subscriber
#subscriber.visit += 1
#subscriber.save
redirect_to subscribers_visits_new_path(:subscriber)
else
render "new"
end
end
end
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
ROUTES
Rails.application.routes.draw do
devise_for :users
resources :subscribers, except: :show
get '/subscribers/visits/new', to: 'subscribers/visits#new'
root "welcomes#index"
VIEWS
<h1>hey</hey>
<%= form_for #subscriber do |form| %>
<div class="form-group">
<p>
<%= form.label :phone_number %>
<%= form.text_field :phone_number %>
</p>
<% end %>
ERROR
Hmm, my guess is you are trying to route url subscriber/visits/new to new action in VisitsController?How about changing this line:
get '/subscribers/visits/new', to: 'subscribers/visits#new'
to:
namespace :subscribers do
get '/visits/new', to: 'visits#new'
end
Also try to move this block above resources :subscribers, except: :show if you still get the error.
Cheers
You probably do not need to inherit one controller from another. Simply define the controllers as you normally would:
app/controllers/subscribers_controller.rb
class SubscribersController < ApplicationController
# methods for Subscribers
end
in app/controllers/visits_controller.rb
class VisitsController < ApplicationController
# methods for Visits
end
Note that these must to be located in separate files, so that Rails can find the correct source file by the name of the object that it's looking for. This is a Rails naming convention.
Regarding your routes, you'll need to change to use one of 4 route formats. Reading the section on Adding More RESTful Actions in the Rails Routing from the Outside In guide might help.
1) To route visits as a nested resource, which is what it appears you're actually trying to do, you would use this:
resources :subscribers, except: :show do
resources :visits
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/:id/visits/new
POST /subscribers/:id/visits
GET /subscribers/:id/visits
GET /subscribers/:id/visits/:id
GET /subscribers/:id/visits/:id/edit
PATCH /subscribers/:id/visits/:id/update
DELETE /subscribers/:id/visits/:id/destroy
This is the typical route structure for nested resources and separate controllers.
2) To make visits#new a simple collection (non-member) action in the VisitsController, then you likely want this:
resources :subscribers, except: :show do
collection do
get 'visits/new', to 'visits#new'
post 'visits', to 'visits#create'
end
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/visits/new
POST /subscribers/visits
This is typically used to add new top-level routes in an existing resource and controller.
3) To construct visits as member actions, use this:
resources :subscribers, except: :show do
member do
get 'visits/new', to 'visits#new'
post 'visits', to 'visits#create'
end
end
This will produce these routes:
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
GET /subscribers/:id/visits/new
POST /subscribers/:id/visits
This is normally used to add new member routes in an existing resource and controller.
4) To simply make visits routes appear to be included in subscribers, you could use this:
get '/subscribers/visits/new', to: 'visits#new'
post '/subscribers/visits', to: 'visits#create'
resources :subscribers, except: :show
This will produce these routes:
GET /subscribers/visits/new
POST /subscribers/visits
GET /subscribers/new
POST /subscribers
GET /subscribers
GET /subscribers/:id/edit
PATCH /subscribers/:id/update
DELETE /subscribers/:id/destroy
This may be used to make arbitrary routes appear to be included in an existing resource, when they really may be independent.

Showing index view for all categories in a survey

In rails, I have two models, survey and category. Survey has_many categories and category belongs_to survey. I have an index page showing all of my surveys, and I want a button that directs the user to a view that shows an index of all of the categories for each individual survey. So basically I want them to click on the button and go to something like /surveys/:id/categories. How would I go about doing this?
First define the routes:
# config routes.rb
resources :surveys, only: [:index] do
resources :categories, only: [:index]
end
Using resources gives us nice restful routes:
$ rake routes
Prefix Verb URI Pattern Controller#Action
survey_categories GET /surveys/:survey_id/categories(.:format) categories#index
surveys GET /surveys(.:format)
Checkout the prefix column - this means that we can get a path to the categories by survey_categories_path(survey_id: #survey.to_param).
We then need a controller for categories:
class CategoriesController < ApplicationController
def index
# We use eager_load to get survey and categories in one DB query
#survey = Survey.eager_load(:categories).find(params[:survey_id])
#categories = #survey.categories
end
end
Something like:
routes.rb
...
get 'surveys/:id/categories', :to => 'categories#index'
...
categories_controller:
...
def index
#categories = Survey.find(params[:id]).categories
end
...

Rails RESTful URL's: All Posts under certain Category

Currently I use my posts#index action to show all posts or filter'em by category in case its specified:
PostsController:
def index
#posts = Post.all(:order => "created_at DESC")
#posts = #posts.by_category(params[:category_id]) #Custom named_scope
end
Routes:
map.connect '/post/by_category/:category_id', :controller => :posts, :action => :index
map.resources :users
So /posts will return all the posts, and /posts/by_category/1 will return all posts under category 1
I wonder if there is a way of doing it more RESTful, and maybe to get some pretty url_paths.
I've read the guides (Using latest 2.3 Rails branch) but neither nested routes nor collections seemed appropiate for this case. Thanks :)
resources :posts
resources :categories do |categories|
categories.resources :posts
end
Your urls then:
/posts - all posts
/posts/:id -certain post
/categories - all categories
/categories/:id - certain category
/categories/:id/posts - all posts within a certain category.

Resources