Rails 中的胖模型、瘦控制器以及示例

Fat Model, Skinny Controller in Rails with example

我真的很想开始学习 Rails 最佳实践,尤其是遵循 "fat model, skinny controller" 逻辑。

假设我有以下评论控制器

class CommentsController < ApplicationController

    def create
        @post = Post.find(params[:post_id])
        @comment = @post.comments.create(comment_params)
        @comment.user_id = current_user.id if current_user
        @comment.save!

        if @comment.save
            redirect_to post_path(@post)
        else
            render 'new'
        end
    end

    def edit
        @post = Post.find(params[:post_id])
        @comment = @post.comments.find(params[:id])
    end

    def update
        @post = Post.find(params[:post_id])
        @comment = @post.comments.find(params[:id])

        if @comment.update(params[:comment].permit(:comment))
            redirect_to post_path(@post)
        else
            render 'Edit'
        end
    end

    def destroy
        @post = Post.find(params[:post_id])
        @comment = @post.comments.find(params[:id])
        @comment.destroy
        redirect_to post_path(@post)
    end

    private

    def comment_params
        params.require(:comment).permit(:comment)
    end

从哪里开始重构代码比较好? 我立即认为我可以在 edit@post@commentupdate 到一个单独的方法中,然后在该方法上调用 before_action。但这仍然是将所有代码放在控制器中。

是否有任何代码可以移动到模型中?如果是这样,我应该如何构建它们?

这段代码没有太大的改进空间,它是一个基本的垃圾,这里有一个 before_action 的例子,就像你建议的那样

before_action :load_post_and_comment, only: %i(edit update destroy)

def load_post_and_comment
  @post = Post.find(params[:post_id])
  @comment = @post.comments.find(params[:id])
end

还有一些其他注意事项

def create
  # ...
  @comment.save!
  if @comment.save
    # ...
  else
    # ..
  end
end

在这个条件下你应该删除额外的@comment.save!你只需要保存一次。

def update
  # ...
  if @comment.update(params[:comment].permit(:comment))
    # ...
  else
    # ...
  end
end

您已经有了 comment_params 方法,请使用它,因为如果您在任何时候向评论添加新属性,您将更新该方法,但您可能会忘记这部分,并且您'在你注意到你也需要在这里允许之前,你会遇到奇怪的错误。

如果你想真的全力以赴使用瘦控制器模型,有这个gem:https://github.com/NullVoxPopuli/skinny_controllers

在哪里,您可以像这样配置 CommentsController

class CommentsController < ApplicationController
  include SkinnyControllers::Diet

  def create
    if model.errors.present?
      render 'new'
    else
      redirect_to post_path(model)
    end
  end

  def update
    redirect_to post_path(model)
  end

  # ... etc

  private

  def comment_params
    params.require(:comment).permit(:comment)
  end

end