加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby-on-rails – Rails关联的最佳代码结构

发布时间:2020-12-17 02:56:03 所属栏目:百科 来源:网络整理
导读:舞台 让我们谈谈我们遇到的最常见的关联类型. 我有一个用户:has_many Post(s) class User ActiveRecord::Base has_many :postsendclass Post ActiveRecord::Base belongs_to :userend 问题陈述 我想在用户的所有帖子上做一些(非常轻快的)处理.我正在寻找构
舞台

让我们谈谈我们遇到的最常见的关联类型.

我有一个用户:has_many Post(s)

class User < ActiveRecord::Base
  has_many :posts
end

class Post < ActiveRecord::Base
  belongs_to :user
end

问题陈述

我想在用户的所有帖子上做一些(非常轻快的)处理.我正在寻找构建我的代码来实现它的最佳方法.以下是几种方式以及它们工作或不工作的原因.

方法1

在User类本身中执行此操作.

class User < ActiveRecord::Base
  has_many :posts

  def process_posts
    posts.each do |post|
      # code of whatever 'process' does to posts of this user
    end
  end
end

邮政课程保持不变:

class Post < ActiveRecord::Base
  belongs_to :user
end

该方法称为:

User.find(1).process_posts

为什么这看起来不是最好的方法

使用用户帖子做某事的逻辑应该属于Post类.在现实世界的场景中,用户可能还具有:与许多其他类的has_many关系,例如订单,评论,儿童等

如果我们开始向User类添加类似的process_orders,process_comments,process_children(yikes)方法,它将导致一个包含大量代码的巨型文件,其中很多代码可以(并且应该)分发到它所属的位置,即目标关联.

方法2

代理关联和范围

这两种结构都需要向User类添加方法/代码,这又使它变得臃肿.我宁愿将所有实现转移到目标类.

方法3

目标类的类方法

在目标类中创建类方法,并在User对象上调用这些方法.

class User < ActiveRecord::Base
  has_many :comments
  # all target specific code in target classes
end

class Post < ActiveRecord::Base
  belongs_to :user

  # Class method
  def self.process
    Post.all.each do |post|  # see Note 2 below
      # code of whatever 'process' does to posts of this user
    end
  end
end

该方法称为:

User.find(1).posts.process   # See Note 1 below

现在,这看起来和感觉比方法1和2更好,因为:

>用户模型仍然没有杂乱.
>流程函数称为process而不是process_posts.现在我们也可以为其他类创建一个进程,并将它们调用为:User.find(1).orders.process等,而不是User.find(1).process_orders(方法1).

注1:

是的,您可以在关联上调用这样的类方法. Read why here. TL; DR是User.find(1).posts返回一个CollectionProxy对象,该对象可以访问目标(Post)类的类方法.它还方便地传递一个scope_attributes,它存储调用posts.process的用户的user_id.这很方便.见下面的注2.

笔记2:

对于那些在类方法中执行Post.all.each时不确定的人,它会返回调用此方法的用户的所有帖子,而不是数据库中的所有帖子.

所以当调用User.find(99).posts.process时,Post.all执行:

SELECT "notes".* FROM "posts" WHERE "posts"."user_id" = $1  [["user_id",99]]

这是用户ID:99的所有帖子.

Per @ Jesuspc的评论如下,Post.all.each可以简洁地写成all.each.它更具惯用性,并不会让我们看起来像是在查询数据库中的所有帖子.

我正在寻找的答案

>解释处理此类关联的最佳方式是什么.人们如何正常地做到这一点?如果方法3中有任何明显的设计缺陷.

解决方法

还有第四种选择.将此逻辑完全移出模型:

class PostProcessor
  def initialize(posts)
    @posts = posts
  end

  def process
    @posts.each do |post|
      # ...
    end
  end
end

PostProcessor.new(User.find(1).posts).process

这有时称为服务对象模式.这种方法的一个非常好的好处是它使这种逻辑的编写测试非常简单.这是关于这个和其他重构“胖”模型的方法的精彩博文:http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读