【问题标题】:Model method depending on current_user in rails模型方法取决于rails中的current_user
【发布时间】:2013-08-30 11:35:51
【问题描述】:

在我的 rails 应用程序中,我有一些用户可以付费的活动。我需要能够根据当前用户更改活动价格。

*我知道已经有很多关于在模型中访问 c​​urrent_user 的主题,但这不是我想要的。*

我有以下 2 个模型(非常简化)。 Checkout 正在管理与事件相关的所有支付事项(我需要它在一个单独的模型中,因为在真实的应用程序中它与事件具有多态关联)。

class Event < ActiveRecord::Base
  attr_accessible :ticket_price, :checkout
  has_one :checkout

  checkout_price
    # Here I'd like to be able to use the current_user to change price accordingly
    # Example: user.premium? ? ticket_price/2 : ticket_price
    ticket_price
  end
end

class Checkout < ActiveRecord::Base
  attr_accessible :event
  belongs_to :event

  def total
    event.checkout_price
  end

  def free?
    total == 0
  end
end

我显然可以定义checkout_price(user),但我必须在任何地方传递它(例如event.checkout_price(current_user)checkout.total(current_user)checkout.free?(current_user))。

我知道从模型中访问current_user 是一种不好的做法(我绝对不想这样做),但是在我的情况下,除了一直将current_user 作为参数传递之外,还有其他解决方案吗?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    这是一个很好的问题,我很欣赏你关于不在模型中访问 c​​urrent_user 的意见。

    实际上,Event 模型应该考虑的更少。模型的主要工作是存储数据并处理与自身相关的数据。定价是您的业务逻辑,而不是事件模型的关注点。一个事件是有代价的。就这样。不再。

    您看,在定价方面您需要考虑很多事情。不仅是用户是否是高级用户。如果用户在您的应用程序中使用 6 个月大,可以享受一些折扣吗?在应用程序生日时进行一些促销怎么样?仅仅因为你喝醉了销售怎么样?如果您使用事件模型来处理它们,所有这些都将非常复杂。即使您现在不需要所有这些功能,也最好留出一些扩展空间。

    那么应该在哪里考虑定价逻辑呢?显然控制器也不是一个好地方。让我们试试服务对象。

    class PricingService
    
      def initialize(event, user)
        @user = user
        @event = event
        @price = event.price
      end
    
      def get_price
        # The place for your easily adding rules. Or use some meta programming.
        # say go through all instance methods starting with "pricing_"
        pricing_premium_user
        pricing_app_birthday
        pricing_old_event
        @price
      end
    
      def pricing_premium_user
        @price -= 10 if user.premium?
      end
    
      def pricing_app_birthday
        @price = @price*0.9 if Time.current.today == APP_BIRTHDAY
      end
    
      def pricing_old_event
        @price = @price*0.9 if event.created_at < (Time.current - 1.week)
      end
    
    end
    

    然后在控制器中使用它

    # Checkout 
    def new
      @event = method_to_find_event
      pricing = PricingService.new(@event, current_user)
      @event.price = pricing.get_price
      # blah blah
    end
    

    好看吗?

    【讨论】:

    • 谢谢,我得改编它,但它看起来真的很棒! (尤其是“醉酒折扣”;))。
    • @TimPetricola,我很高兴:) 你可以谷歌服务对象以获取更多信息。
    【解决方案2】:

    将用户设置为关联或实例变量比在不同对象的多个位置传递它更好的设计。仔细想想,每个结帐都属于用户,不是吗?

    这样:

    class Checkout < ActiveRecord::Base
      attr_accessible :event
      belongs_to :event
      belongs_to :user
    
      def total
        event.checkout_price(user)
      end
    
      def free?
        total == 0
      end
    end
    

    您只需要确保在创建结帐记录时设置用户即可。

    【讨论】:

    • 实际上没有,每个事件只有一个Checkout,并且Checkout 模型通过Charge 模型链接到User 模型。但是我不能依赖这个,因为它是仅在用户付款时创建的,并且我在此之前使用我的方法(例如,只是不显示具有相应价格的事件)
    • 啊,这对我来说看起来有点违反直觉——如果你能多解释一下你的域模型(类)也许会有所帮助,否则它太抽象了,无法提出一些建议......可能值得考虑将 Checkout 和 Charge 模型重构为 Checkout,当用户被收费时它会改变状态(收费);您可以将 checkout_id 保存在用户的会话中。但同样,很难说,因为我根本不知道初始模型或完整域模型的原因......
    • 感谢您的帮助。我真的很喜欢 Billy Chan 建议的服务方法,我认为在这种情况下它真的很有意义。
    【解决方案3】:

    如果您不想传递 current_user,那么您必须传递结帐和事件。方法只能在单个对象上调用。无论哪种方式,您都必须传递另一个对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多