有关可以具有不同父类的资源控制器的最佳做法

时间:2018-07-27 09:13:41

标签: ruby-on-rails ruby ruby-on-rails-5

我目前正在尝试重构一些控制器代码,但遇到了一些我不确定如何以正确方式实现的代码。

我的应用程序有用户和公司,并且都可以有项目。 当前的情况是,我们有2个网址:

  • example.com/projects/*action(用于用户项目)
  • example.com/company/:company_id/projects/*action(用于公司项目)

那些将路由到同一控制器,该控制器将根据company_id是否存在来不同地处理请求。我认为这不是很干净,因此我一直在考虑一种更好的方法。

到目前为止,我认为最好的方法是将它们分成单独的控制器,例如:

  • Users :: ProjectsController
  • Companies :: ProjectsControler

但是,由于用户项目和公司项目之间的唯一区别几乎是一个项目具有一个“ user_id”,另一个项目具有一个“ company_id”,因此感觉就像我将要写的那样不那么干很多重复的代码。

当前的解决方案可能并不是什么大问题,但我想以正确的方式进行操作,因此希望这里的人对如何处理此问题提出建议。

谢谢!

编辑:

这是我的ProjectsController#create当前的外观

  def create
    if params[:company_id]
      company = current_user.get_companies.find(params[:company_id])
      @project = Project.new(project_params)
      @project.company_id = company.id
    else
      @project = Project.new(project_params)
      @project.user_id = current_user.id
    end
    @project = Project.new(project_params)

    if @project.save
      flash[:notice] = "Project '#{@project.name}' created."
      if @project.company
        redirect_to company_project_path(@project.company, @project)
      else
        redirect_to project_path(@project)
      end
    else
      flash[:error] = @project.errors.full_messages
      if params[:company_id]
        redirect_to new_company_project_path(params[:company_id], @project)
      else
        redirect_to new_project_path(@project)
      end
    end
  end

主要是我想摆脱的if / else逻辑 所以我可能应该只将company_id和user_id添加到allowed_pa​​rams中,然后使用函数将其中一个放置在params中。

2 个答案:

答案 0 :(得分:2)

好的,我设法以自己满意的方式来处理它。 ProjectsController#create现在看起来像这样:

  def create
    @project = owner.projects.new(project_params)

    if @project.save
      flash[:notice] = "Project '#{@project.name}' created."
      redirect_to action: :show, id: @project.id
    else
      flash[:error] = @project.errors.full_messages
      @project = Project.new(project_params)
      render action: :new
    end
  end

  def owner
    if params[:company_id]
      return policy_scope(Company).find(params[:company_id])
    else
      return current_user
    end
  end

我添加了所有者类以返回项目所属的实体。 仍然欢迎任何改进建议!

答案 1 :(得分:2)

由于您说的唯一区别是将company_iduser_id关联起来,就像@TomLord所说的那样,您可能会发现以下类似的内容对您有用:

假设您正在使用shallow nested routes

class ProjectsController < ApplicationController
  COLLECTION_ACTIONS = [:index, :new, :create].freeze
  MEMBER_ACTIONS = [:show, :edit, :update, :destroy].freeze

  before_action :set_associated_record, only: COLLECTION_ACTIONS
  before_action :set_project, only: MEMBER_ACTIONS

  def index
    @projects = @associated_record.projects
    # ...
  end

  def new
    @project = @associated_record.projects.new
    # ...
  end

  def create
    @project = @associated_record.projects.new(project_params)
    # ...
  end

  private

  def set_project
    @project = Project.find(params[:id])
  end

  def set_company
    if params[:company_id].present?
      @company = Company.find(params[:company_id])
    end
  end

  # you might want to remove this set_user method, because perhaps you are already setting @user from sesssion
  def set_user
    if params[:user_id].present?
      @user = User.find(params[:user_id])
    end
  end

  def set_associated_record
    set_company
    set_user
    @associated_record = @company || @user
  end
end