Rails后台控制器复用sort_field方法:避免子类重复实现
将子控制器的sort_field方法迁移至父控制器的实现方案
场景说明
现有后台父控制器Admin::AdminController,多个子控制器(如Admin::BooksController)继承自它。需要将子控制器中重复的sort_field方法迁移到父控制器,同时确保父控制器的方法能正确读取各个子控制器中定义的SORT_FIELDS和SORT_FIELD常量。
原父控制器代码
class Admin::AdminController < ApplicationController ... private def sort_direction %w[asc desc].include?(params[:sort_direction]) ? params[:sort_direction] : 'asc' end end
修改后的父控制器
在父控制器中添加sort_field方法,通过self.class访问子类的类常量,同时增加容错判断避免未定义常量时出错:
class Admin::AdminController < ApplicationController ... private def sort_direction %w[asc desc].include?(params[:sort_direction]) ? params[:sort_direction] : 'asc' end def sort_field # 优先使用参数中合法的排序字段,否则使用子类定义的默认字段 if self.class.const_defined?(:SORT_FIELDS) && self.class::SORT_FIELDS.include?(params[:sort_field]) params[:sort_field] else # 若子类未定义默认字段,使用全局默认值'id' self.class.const_defined?(:SORT_FIELD) ? self.class::SORT_FIELD : 'id' end end end
子控制器调整
子控制器只需保留常量定义,删除重复的sort_field方法即可:
class Admin::BooksController < Admin::AdminController LIMIT = 40 SORT_FIELD = "name" SORT_DIR = "asc" SORT_FIELDS = %w(id title author publication_date created_at).freeze # GET /admin/books def index _books = Book.limit(LIMIT).order(sort_field => sort_direction) respond_to do |format| format.html { render(inertia: 'Admin/Books/Index', props: {}) } end end # 移除原本的sort_field方法 end
原理说明
在父控制器的实例方法中,self.class会指向当前请求对应的子类(例如访问/admin/books时,self.class即为Admin::BooksController),因此可以通过self.class::常量名的方式读取子类中定义的常量。添加const_defined?判断是为了保证当子类未定义对应常量时,代码不会抛出异常,同时提供全局默认值作为 fallback。
内容的提问来源于stack exchange,提问作者Boris Barroso
相关产品推荐
相关产品推荐

