通过控制器实现多态 has_many:反模式?

发布于 2024-08-22 03:29:27 字数 1896 浏览 13 评论 0原文

我很想说是的。

一个人为的例子,使用 has_many :through 和 polymorphs:

class Person < ActiveRecord::Base
  has_many :clubs, :through => :memberships
  has_many :gyms, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :member, :polymorphic => true
end

class Club < ActiveRecord::Base
  has_many :people, :through => :memberships
  has_many :memberships, :as => :member
end

etc.

暂时先不考虑健身房是否是俱乐部的问题,或任何其他设计缺陷。

要将用户添加到俱乐部,很容易采用 RESTful 并将 person_id 和 Club_id POST 到 MembersController,如下所示:

form_for club_members_path(@club, :person_id => person.id) ...

在这种情况下,当我们决定这样做时:

form_for gym_members_path(@gym, :person_id => person.id) ...

我们需要让 MembersController 决定父资源是否是俱乐部或健身房,并采取相应行动。一种非 DRY 解决方案:

class MembersController < ApplicationController
  before_filter :find_parent
  ...
  private
  def find_parent
    @parent = Gym.find(params[:gym_id]) if params[:gym_id]
    @parent = Club.find(params[:club_id]) if params[:club_id]
  end
end

如果您多次这样做,那就太糟糕了。

此外,它基于加入俱乐部和加入健身房大致相同的概念。或者至少,Gym#add_member 和 Club#add_member 将以或多或少并行的方式运行。但我们必须假设健身房和俱乐部可能有不同的理由拒绝会员申请。 MembersController 需要处理闪现消息并重定向两个或多个错误状态。

野外有解决方案。 James Golick 出色的 ResourceController 有一种处理parent_type、parent_object 等的方法。Revolution On Rails 有一个很好的解决方案 通过向 ApplicationController 添加一些方法来干掉多个多态控制器。当然,ActionController 有 #polymorhpic_url 用于更简单的情况,如 Blog#posts 和 Article#posts 等。

所有这些让我想知道,真的值得对 MembersController 施加所有压力吗?多态性在 Rails 中处理得很好,但我的感觉是,使用条件(if/unless/case)清楚地表明您不知道您正在处理什么类型。元编程有帮助,但前提是类型具有相似的行为。两者似乎都表明需要进行设计审查。

我很想听听您对此的想法。在这种情况下,是保持 DRY 更好,还是确切地知道您拥有哪种父类型更好?我在这里神经病吗?

I'm tempted to say yes.

A contrived example, using has_many :through and polymorphs:

class Person < ActiveRecord::Base
  has_many :clubs, :through => :memberships
  has_many :gyms, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :member, :polymorphic => true
end

class Club < ActiveRecord::Base
  has_many :people, :through => :memberships
  has_many :memberships, :as => :member
end

etc.

Leaving aside, for the moment, the question of whether a Gym is a Club, or any other design flaws.

To add a User to a Club, it's tempting to be RESTful and POST a person_id and a club_id to MembersController, like so:

form_for club_members_path(@club, :person_id => person.id) ...

In this scenario, when we decide to do:

form_for gym_members_path(@gym, :person_id => person.id) ...

We would need to make MembersController decide whether the parent resource is a Club or a Gym, and act accordingly. One non-DRY solution:

class MembersController < ApplicationController
  before_filter :find_parent
  ...
  private
  def find_parent
    @parent = Gym.find(params[:gym_id]) if params[:gym_id]
    @parent = Club.find(params[:club_id]) if params[:club_id]
  end
end

Shockingly awful if you do it more than once.

Also, it's predicated on the concept that joining a Club and joining a Gym are roughly the same. Or at least, Gym#add_member and Club#add_member will behave in a more or less parallel manner. But we have to assume that Gyms and Clubs might have different reasons for rejecting an application for membership. MembersController would need to handle flash messages and redirects for two or more error states.

There are solutions in the wild. James Golick's awesome ResourceController has a way of dealing with parent_type, parent_object, etc. Revolution On Rails has a nice solution for DRYing up multiple polymorphic controllers by adding some methods to ApplicationController. And of course, ActionController has #polymorhpic_url for simpler cases like Blog#posts and Article#posts, etc.

All this leaves me wondering, is it really worth putting all that pressure on MembersController at all? Polymorphism is handled pretty well in Rails, but my feeling is that using conditionals (if/unless/case) is a clear indication that you don't know what type you're dealing with. Metaprogramming helps, but only when the types have similar behavior. Both seem to point to the need for a design review.

I'd love to hear your thoughts on this. Is it better to be DRY in this scenario, or to know exactly what parent type you have? Am I being neurotic here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文