문제

배지가 있습니다(StackOverflow와 같은 것).

그 중 일부는 배지를 부착할 수 있는 항목(예:게시물의 X개 이상의 댓글에 대한 배지가 게시물에 첨부됩니다.거의 모든 것이 여러 수준으로 제공됩니다(예:>20, >100, >200), 배지를 받을 수 있는 x 배지 유형당 하나의 레벨만 가질 수 있습니다(= badgeset_id).

배지당 1레벨 제약 조건을 더 쉽게 적용하기 위해 배지에서 2열 외래 키로 배지를 지정하고 싶습니다. badgeset_id 그리고 level - 기본 키 대신(badge_id), 하지만 배지에는 표준 기본 키도 있습니다.

코드에서:

class Badge < ActiveRecord::Base
  has_many :badgings, :dependent => :destroy
  # integer: badgeset_id, level

  validates_uniqueness_of :badgeset_id, :scope => :level
end

class Badging < ActiveRecord::Base
  belongs_to :user
  # integer: badgset_id, level instead of badge_id
  #belongs_to :badge # <-- how to specify? 
  belongs_to :badgeable, :polymorphic => true

  validates_uniqueness_of :badgeset_id, :scope => [:user_id, :badgeable_id]
  validates_presence_of :badgeset_id, :level, :user_id  

  # instead of this:
  def badge
    Badge.first(:conditions => {:badgeset_id => self.badgeset_id, :level => self.level})
  end
end

class User < ActiveRecord::Base
  has_many :badgings, :dependent => :destroy do
    def grant badgeset, level, badgeable = nil
      b = Badging.first(:conditions => {:user_id => proxy_owner.id, :badgeset_id => badgeset,
        :badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)}) ||
        Badging.new(:user => proxy_owner, :badgeset_id => badgeset, :badgeable => badgeable)
      b.level = level
      b.save
    end
  end
  has_many :badges, :through => :badgings
  # ....
end

어떻게 지정할 수 있나요? belongs_to 그렇게 하는 협회(그리고 badge_id), 내가 사용할 수 있도록 has_many :through?

예상 시간:이는 부분적으로 작동합니다(예:@badging.badge는 작동하지만 더러운 느낌이 듭니다.

belongs_to :badge, :foreign_key => :badgeset_id, :primary_key => :badgeset_id, :conditions => 'badges.level = #{level}'

조건이 있으니 참고하세요 하나의 큰따옴표가 아닌 따옴표를 사용하면 로드 시간이 아닌 런타임에 해석됩니다.

그러나 이것을 :through 연관과 함께 사용하려고 하면 오류가 발생합니다. undefined local variable or method 'level' for #<User:0x3ab35a8>.그리고 명백한 것은 없습니다(예: 'badges.level = #{badgings.level}') 작동하는 것 같습니다 ...

도착 예정 시간 2:EmFi의 코드를 가져와서 약간 정리하면 효과가 있습니다.추가가 필요합니다 badge_set_id 중복되는 Badge에, 하지만 아 글쎄요.

코드:

class Badge < ActiveRecord::Base
  has_many :badgings
  belongs_to :badge_set
  has_friendly_id :name

  validates_uniqueness_of :badge_set_id, :scope => :level

  default_scope :order => 'badge_set_id, level DESC'
  named_scope :with_level, lambda {|level| { :conditions => {:level => level}, :limit => 1 } }

  def self.by_ids badge_set_id, level
    first :conditions => {:badge_set_id => badge_set_id, :level => level} 
  end

  def next_level
    Badge.first :conditions => {:badge_set_id => badge_set_id, :level => level + 1}
  end
end

class Badging < ActiveRecord::Base
  belongs_to :user
  belongs_to :badge 
  belongs_to :badge_set
  belongs_to :badgeable, :polymorphic => true

  validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
  validates_presence_of :badge_set_id, :badge_id, :user_id  

  named_scope :with_badge_set, lambda {|badge_set|
    {:conditions => {:badge_set_id => badge_set} }
  }

  def level_up level = nil
    self.badge = level ? badge_set.badges.with_level(level).first : badge.next_level
  end

  def level_up! level = nil
    level_up level
    save
  end
end

class User < ActiveRecord::Base
  has_many :badgings, :dependent => :destroy do
    def grant! badgeset_id, level, badgeable = nil
      b = self.with_badge_set(badgeset_id).first || 
         Badging.new(
            :badge_set_id => badgeset_id,
            :badge => Badge.by_ids(badgeset_id, level), 
            :badgeable => badgeable,
            :user => proxy_owner
         )
      b.level_up(level) unless b.new_record?
      b.save
    end
    def ungrant! badgeset_id, badgeable = nil
      Badging.destroy_all({:user_id => proxy_owner.id, :badge_set_id => badgeset_id,
        :badgeable_id => badgeable.try(:id), :badgeable_type => badgeable.try(:class)})
    end
  end
  has_many :badges, :through => :badgings
end

이것이 작동하고 아마도 더 나은 솔루션일 수 있지만 a) 다중 키 외래 키 또는 b) :through 연관과 함께 작동하는 동적 조건 연관을 수행하는 방법에 대한 질문에 대한 실제 대답이라고 생각하지 않습니다.따라서 이에 대한 해결책이 있는 사람이 있으면 말해주세요.

도움이 되었습니까?

해결책

Badge를 두 가지 모델로 분리하면 운동 효과가 가장 좋을 것 같습니다.원하는 기능을 달성하기 위해 분석하는 방법은 다음과 같습니다.실제로 작업을 수행하는 코드를 깔끔하게 유지하기 위해 명명된 범위를 추가했습니다.

class BadgeSet
  has_many :badges
end

class Badge
  belongs_to :badge_set
  validates_uniqueness_of :badge_set_id, :scope => :level

  named_scope :with_level, labmda {|level
    { :conditions => {:level => level} }
  }

  named_scope :next_levels, labmda {|level
    { :conditions => ["level > ?", level], :order => :level }
  }

  def next_level 
    Badge.next_levels(level).first
  end
end

class Badging < ActiveRecord::Base
  belongs_to :user
  belongs_to :badge 
  belongs_to :badge_set
  belongs_to :badgeable, :polymorphic => true

  validates_uniqueness_of :badge_set_id, :scope => [:user_id, :badgeable_id]
  validates_presence_of :badge_set_id, :badge_id, :user_id  

  named_scope :with_badge_set, lambda {|badge_set|
    {:conditions => {:badge_set_id => badge_set} }
  }

  def level_up(level = nil)
    self.badge = level ? badge_set.badges.with_level(level).first 
      : badge.next_level
    save
  end
end

class User < ActiveRecord::Base
  has_many :badgings, :dependent => :destroy do
    def grant badgeset, level, badgeable = nil
      b = badgings.with_badgeset(badgeset).first() || 
         badgings.build(
            :badge_set => :badgeset,
            :badge => badgeset.badges.level(level), 
            :badgeable => badgeable
         )

      b.level_up(level) unless b.new_record?

      b.save
    end
  end
  has_many :badges, :through => :badgings
  # ....
end
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top