简体   繁体   中英

Rails 5 routing resources using custom actions

About routing, If I do something like this:

resources :students
resources :teachers

I will get something like:

students GET /students(.:format) students#index
...
teachers GET /teachers(.:format) teachers#index
...

Changing to:

resources :students, controller: :users
resources :teachers, controller: :users

will give me:

students GET /students(.:format) users#index
teachers GET /teachers(.:format) users#index

Note that now, both resources are using the same controller Users and the same action index . But what I need, instead of using the same index action, is the students resource to use actions prefixed by students like students_index and teachers resources prefixed by teachers like teacher_index .

In other words, I want bin/rails routes to give me the following output:

students GET /students(.:format) users#students_index
teachers GET /teachers(.:format) users#teachers_index

I know that I can do the same with:

get 'students', to: 'users#students_index'

But there is a way to do the same with resources?

I don't think there's a way to do that with resources helper. What you could do (if it's only the index action you wanna override) is add an except, like this:

resources :students, controller: :users, except: [:index]
resources :teachers, controller: :users, except: [:index]

then, as you already suggested, do the individuals index actions like that:

get 'students', to: 'users#students_index', as: :student
get 'teachers', to: 'users#teachers_index', as: :teacher

Or you could reconsider the structure of your controllers... Good luck!

There is a far better way to do this as you might have surmised - inheritance.

# app/controllers/users_controller.rb
class UsersController < ApplicationController

  delegate :singular, :plural, :param_key, to: :model_name

  before_action :set_resource, only: [:show, :edit, :update, :destroy]
  before_action :set_resources, only: [:index]

  def initialize
    @model_name = resource_class.model_name
    super
  end

  def show
  end

  def index
  end

  def new
    @resource = resource_class.new
    set_resource
  end

  def create
    @resource = resource_class.new(permitted_attributes)

    if @resource.save
      redirect_to @resource
    else
      set_resource
      render :new
    end
  end

  def edit
  end

  def update
    if @resource.update(permitted_attributes)
      redirect_to @resource
    else
      set_resource
      render :edit
    end
  end

  def destroy
    @resource.destroy
    redirect_to action: "index"
  end

  # ...

  private

  # Deduces the class of the model based on the controller name
  # TeachersController would try to resolve Teacher for example.
  def resource_class
    @resource_class ||= controller_name.classify.constantize
  end 

  # will set @resource as well as @teacher or @student
  def set_resource
    @resource ||= resource_class.find(params[:id])
    instance_variable_set("@#{singular}", @resource)
  end

  # will set @resources as well as @teachers or @students
  def set_resources
    @resources ||= resource_class.all
    instance_variable_set("@#{plural}", @resources)
  end

  def permitted_attributes
    params.require(param_key).permit(:a, :b, :c)
  end
end

# app/controllers/teachers_controller.rb
class TeachersController < UsersController
end

# app/controllers/students_controller.rb
class StudentsController < UsersController
end

# routes.rb
resources :students
resources :teachers

This lets you follow the regular Rails convention over configuration approach when it comes to naming actions and views.

The UsersController base class uses quite a bit of magic through ActiveModel::Naming both to figure out the model class and stuff like what to name the instance variables and the params keys.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM