Ash FrameworkAF
Ash Frameworkโ€ข7mo agoโ€ข
9 replies
RootCA

Persisting values from relationships at creation time

Hi there!

I'm working on a demo that allows
product
s to be purchased by users. Since prices might change over time, I'd like to put the price of the product at the time of purchase into the
order
resource record.

Given a product with the following attributes
  attributes do
    uuid_primary_key :id

    attribute :name, :string do
      allow_nil? false
      public? true
    end

    attribute :price, :money do
      allow_nil? false
      public? true
    end

    create_timestamp :inserted_at
    update_timestamp :updated_at
  end


and orders defined with
  attributes do
    uuid_primary_key :id

    attribute :amount, :money do
      allow_nil? false
      public? true
    end

    create_timestamp :inserted_at
    update_timestamp :updated_at
  end

  relationships do
    belongs_to :user, AppName.Accounts.User do
      allow_nil? false
      public? true
    end

    belongs_to :product, AppName.DomainName.Product do
      allow_nil? false
      public? true
    end
  end


what is the best way to preserve the price value from the product?

My first try adds a purchase action to the Order resource:
create :purchase_product do
  accept [:product_id]
  change relate_actor(:user)
  change fn changeset, context ->
    product_id = Ash.Changeset.get_attribute(changeset, :product_id)
    product = AppName.DomainName.get_product!(product_id)
    Ash.Changeset.change_attribute(changeset, :amount, product.price)
  end
end


but this causes me to lose the builtin "not found" error response if the product_id is invalid.

I also considered an after_action, but it didn't feel right to force change an attribute:
change set_attribute(:amount, "$0.00")
      
change after_action(fn changeset, context ->
  product_id = Ash.Changeset.get_attribute(changeset, :product_id)
  product = AppName.DomainName.get_product!(product_id, actor: context[:actor])
  Ash.Changeset.force_change_attribute(changeset, :amount, product.price)
end)
Solution
change before_action(fn changeset, context ->
  product_id = Ash.Changeset.get_attribute(changeset, :product_id)
  product = AppName.DomainName.get_product!(product_id, actor: context.actor)

  Ash.Changeset.force_change_attribute(changeset, :amount, product.price)
end)
Was this page helpful?