2012-05-21 38 views
5

Dưới đây là mô hình ActiveRecord tôi, với Rails 3.2:Relation trên phạm vi trên ActiveRecord

class User < ActiveRecord::Base 
    has_one :criterion 
    has_many :user_offer_choices 
end 

class Offer < ActiveRecord::Base 
    has_many :user_offer_choices 

    def seen 
     user_offer_choices.where(seen: true) 
    end 

    def accepted 
     user_offer_choices.where(accepted: true) 
    end 
end 

class Criterion < ActiveRecord::Base 
    belongs_to :user 
end 

class UserOfferChoice < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :offer 
end 

Tôi muốn để có được tất cả các tiêu chí của người dùng đã xem một lời đề nghị. Một cái gì đó như:

Offer.find(11).seen.users.criterions 

nhưng tôi không biết làm thế nào để cho nó với ActiveRecord

Tôi biết tôi có thể làm điều gì đó như:

Criterion.joins(user: { user_offer_choices: :offer }).where(user: { user_offer_choices: {accepted: true, offer_id: 11} }) 

Nhưng tôi muốn để có thể sử dụng phạm vi của tôi trên phiếu mua hàng (đã xem & được chấp nhận). Vậy làm thế nào tôi có thể làm điều đó?

Edit: tôi đã tìm thấy những gì tôi đang tìm kiếm, phương pháp hợp nhất của Arel: http://benhoskin.gs/2012/07/04/arel-merge-a-hidden-gem

Trả lời

3

Đầu tiên, những gì bạn thực sự muốn là xác định một phạm vi trên sự lựa chọn của bạn.

class UserOfferChoice < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :offer 

    scope :seen, where(seen: true) 
    scope :accepted, where(accepted: true) 
end 

nào cho phép bạn làm điều này

Offer.find(11).user_offer_choices.seen 

và để có được các tiêu chí:

Offer.find(1).user_offer_choices.seen.map{|choice| choice.user}.map{|user| user.criterion} 

Bây giờ, điều này có thể được làm sạch với một có rất nhiều thông qua trong lớp Offer.

class Offer < ActiveRecord::Base 
    has_many :user_offer_choices 
    has_many :users, :through => :user_offer_choices 
end 

nhưng điều đó đưa chúng tôi đến người dùng nhưng bỏ qua phạm vi.

Offer.find(1).users 

Bây giờ, có một mẹo bạn có thể làm với Rails 3 phạm vi mà bạn không thể thực hiện với Rails 2.3.5 named_scopes. Các named_scopes lấy một băm làm đối số nhưng trả về một quan hệ. Các phạm vi Rails 3 có một mối quan hệ, như từ các phương thức truy vấn như ở đâu. Vì vậy, bạn có thể xác định phạm vi trong người dùng, sử dụng phạm vi được xác định trong lớp lựa chọn của bạn!

class User < ActiveRecord::Base 
    has_one :criterion 
    has_many :user_offer_choices 
    has_many :offers, :through => :user_offer_choices 

    scope :seen, UserOfferChoice.seen 
    scope :accepted, UserOfferChoice.accepted 
end 

Điều đó cho phép chúng ta làm điều này:

Offer.find(1).users.seen 

bản đồ tại trông như thế này:

Offer.find(1).users.seen.map{|user| user.criterion} 

BTW, số nhiều tiêu chí là tiêu chí. Nghe những tiêu chí trong đầu tôi khi tôi đọc nó, đau khổ. Bạn có thể làm điều này để giúp Rails biết số nhiều:

config/initializers/inflections.rb 
ActiveSupport::Inflector.inflections do |inflect| 
    inflect.plural /^criterion$/i, 'criteria' 
end 
+0

Tôi không muốn sử dụng bản đồ (hoặc phương pháp Enum khác), vì Ruby và ActiveRecord đơn giản sẽ không tạo SQL. –

+0

Tại sao điều quan trọng là sử dụng SQL thay vì bản đồ? Điều đó dường như với tôi là một yêu cầu bắt buộc tùy ý. Nếu bạn nhận thấy, tôi đã chứng minh làm thế nào để đi từ lồng hai cuộc gọi bản đồ, (đó là cồng kềnh và khó đọc) để một giải pháp mà được thoát khỏi một bản đồ và sử dụng SQL và phạm vi của bạn. Bạn có thể thử mở rộng kỹ thuật đó, nhưng tôi nghĩ rằng giải pháp đó sẽ cồng kềnh. (Tìm hiểu thêm về bản đồ so với SQL trong bình luận tiếp theo.) –

+0

Sử dụng bản đồ có một nhược điểm là nó lặp qua bộ sưu tập, nhưng sau đó thực hiện cuộc gọi đến cơ sở dữ liệu trong vòng lặp để dereference hiệp hội. Khi tôi thay đổi bản đồ lựa chọn thành có nhiều thông qua nó đã thực hiện một cuộc gọi SQL cho đối tượng Offer đã cho, và xây dựng phép nối cho chúng ta. Tuy nhiên, tôi sẽ không lo lắng về hiệu suất cho đến khi chúng tôi xác định rằng toàn bộ phản hồi trên web mất quá nhiều thời gian. –

Các vấn đề liên quan