← Back to Upcase

How do you cause a change in associations to trigger an action in the parent object


(Philip Bradley) #1

I have an “order” model which has_many lineitems. I need to populate a column on order, lets call it search_body that has certain information about the order and each line item in the order, for easy search.

So, when lineitems are added or removed I need this search_body field updated.

Right now I have an after_save and after_destroy callback in the lineitem that references the order:

after_save ->(lineitem) { lineitem.order.save }

It just feels wrong. Is there any way that the parent model (order) can be aware of changes in associated models and update this column when items are removed or added to an association?


(Jurre) #2

I think you could just do this:

class LineItem < ActiveRecord::Base
  belongs_to :order, touch: :true
end

This way, every time a lifetime is changed the Order is updated. In your order you could then use an after_touch callback there. However, you could try moving all of this logic out of your models and into a service object that does it.

class UpdateLineItem
  def initialize(lineitem)
    @lineitem = lineitem
  end

  def run
    @lineitem.save
    # do whatever with @lineitem.order
  end
end

(Philip Bradley) #3

Jurre, I did not know about the after_touch callback. This is the best and cleanest way to do this so far. thank you.


(Pedro Moreira) #4

However, you could try moving all of this logic out of your models and into a service object that does it.

I’d strongly recommend going this route.


(Philip Bradley) #5

What would be the best way to instantiate that service object from the lineitem being saved? Replace the save method or use a callback? This is a new problem for me.


(Pedro Moreira) #6

I wouldn’t instantiate in your model. If you think about it LineItem has no reason to know about this service, let alone directly called from within one of its methods. I would start by looking at the controller level for a place to call the service.

Hope this helps :slight_smile: