rctneil
rctneil

Reputation: 7220

Get slug to update when attribute of association changes

I have Attraction and Venue models. Their associations are:

Attraction
belongs_to :venue
Venue
has_many :attractions, dependent: :nullify

I'm using the friendly_id gem to add slugs to both. They are added with:

Attraction
friendly_id :slugged_name_and_venue, use: :slugged
Venue
friendly_id :slugged_name_and_location, use: :slugged

The problem I have is that, because the Attraction slug contains the name and location_1 attributes of the venue association, if the name or location_1 attributes get changed, then the slug of the Attraction is incorrect. I need them to be updated to always stay in sync.

  1. I know the concept of slugs is to keep one unique consistent string that never changes. I understand this, but this is not how i need this to work.

  2. I know that including values from associations in a slug is also a bad idea, but in this case I need this. I don't typically do this.

I've tried a varying number of options, but none of them actually successfully get the associated Attraction slugs to update.

My latest attempt is this:

Venue
  def should_generate_new_friendly_id?
    slug.blank? || name_changed? || location_1_changed?
    end

    after_update :update_attraction_slugs, if: :should_update_attraction_slugs?

  private

  def should_update_attraction_slugs?
    saved_change_to_name? || saved_change_to_location_1?
  end

  def update_attraction_slugs
    attractions.find_each(&:save)
  end
Attraction
    def should_generate_new_friendly_id?
        slug.blank? || slug_changed? || name_changed? || full_name_changed? || venue_id_changed? || venue_name_or_location_1_changed?
    end

  def venue_name_or_location_1_changed?
    saved_change_to_attribute?(:venue_id) || venue.saved_change_to_name? || venue.saved_change_to_location_1?
  end

Is anyone able to help me out here?

Upvotes: 0

Views: 70

Answers (1)

Alex
Alex

Reputation: 30121

Just make sure your methods return true when needed.

class Venue < ApplicationRecord
  has_many :attractions, dependent: :destroy

  after_update :update_attraction_slugs, if: :should_update_attraction_slugs?

  def should_update_attraction_slugs?
    saved_change_to_name? || saved_change_to_location_1?
  end

  def update_attraction_slugs
    attractions.find_each(&:save)
  end
end
class Attraction < ApplicationRecord
  extend FriendlyId

  belongs_to :venue

  friendly_id :slugged_name_and_venue, use: :slugged

  def slugged_name_and_venue
    "#{venue.name} #{venue.location_1}"
  end

  # `saved_change_to_attribute?(:venue_id)` doesn't work on creation
  def should_generate_new_friendly_id?
    venue_id_changed? || venue.saved_change_to_name? || venue.saved_change_to_location_1?
  end
end
>> Venue.create!(name: "ven1", location_1: "loc1")
=> #<Venue:0x00007fd62fbaf650 id: 6, name: "ven1", location_1: "loc1">

>> Venue.first.attractions.create!
=> #<Attraction:0x00007fd62fd15bc0 id: 1, venue_id: 6, slug: "ven1-loc1">
#                                                             ^^^^^^^^^

>> Venue.first.update!(name: "ven2")
  Venue Load (0.2ms)  SELECT "venues".* FROM "venues" ORDER BY "venues"."id" ASC LIMIT ?  [["LIMIT", 1]]
  TRANSACTION (0.1ms)  begin transaction
  Venue Update (0.4ms)  UPDATE "venues" SET "name" = ? WHERE "venues"."id" = ?  [["name", "ven2"], ["id", 6]]
  Attraction Load (0.1ms)  SELECT "attractions".* FROM "attractions" WHERE "attractions"."venue_id" = ? ORDER BY "attractions"."id" ASC LIMIT ?  [["venue_id", 6], ["LIMIT", 1000]]
  Attraction Exists? (0.1ms)  SELECT 1 AS one FROM "attractions" WHERE "attractions"."id" != 1 AND "attractions"."slug" = ? LIMIT ?  [["slug", "ven2-loc1"], ["LIMIT", 1]]
  Attraction Exists? (0.1ms)  SELECT 1 AS one FROM "attractions" WHERE "attractions"."id" != 1 AND "attractions"."slug" = ? LIMIT ?  [["slug", "ven2-loc1"], ["LIMIT", 1]]
  Attraction Update (0.1ms)  UPDATE "attractions" SET "slug" = ? WHERE "attractions"."id" = ?  [["slug", "ven2-loc1"], ["id", 1]]
  TRANSACTION (0.1ms)  commit transaction
=> true

>> Venue.first.attractions
=> [#<Attraction:0x00007fd62fba7810 id: 1, venue_id: 6, slug: "ven2-loc1">]
#                                                              ^^^^^^^^^

Upvotes: 1

Related Questions