2016-07-16 7 views
0

Im Modell:Kette Scopes als OR Abfrage

scope :verified, -> { where(verified: true)} 
scope :active, -> { where(active: true) } 

Nun Model.active.verified Ergebnisse als active and verified. Wie kann ich Bereiche wie OR verketten? Bitte beachten Sie, dass ich nicht will beiden Bereiche als kombinieren wie:
where("active = ? OR verified = ?", true, true)

+1

Wenn Sie nicht wollen wie 'where (" active =? ODER verifiziert =? ", True, true)', dann müssen Sie auf * Rails 5 * warten, in denen 'ActiveRecord' verwendet '.oder' Betreiber.So können Sie die Bereiche wie 'Model.active.or.verified' kombinieren – Pavan

+0

Ich bin neugierig zu wissen, gibt es einen bestimmten Grund, nicht zu verwenden oder abzufragen? – power

+0

Es gibt mehr als 10 Bereiche und sie werden bedingt verwendet. Also wenn ich es auf diese Weise benutze, muss ich viele Permutations-Kombinationsfälle schreiben. – Imran

Antwort

1

Sie müssen or nicht ein Upgrade auf Rails 5, um zu verwenden. Es gibt Möglichkeiten, dies in Rails 4 zu tun. Obwohl diese sich nicht besonders gut eignen, um in verketteten Bereichen verwendet zu werden, können Sie ein ähnliches Maß an Bequemlichkeit beim Aufbau Ihrer Kriterien erreichen.

  1. Arel Tabellen. Arel ist ActiveRecords zugrundeliegende Implementierung der relationalen Algebra. Es unterstützt or unter anderen mächtigen Dingen.

    arel_table = Model.arel_table 
    
    Model.where(
        arel_table[:active].eq(true). 
        or(
        arel_table[:verified].eq(true) 
    ) 
    ) 
    
    # => SELECT "models".* FROM "models" WHERE ("models"."active" = 't' OR "models"."verified" = 't') 
    

Wie Sie sehen können, ist Kriterien Chaining durch die Tatsache erschwert die or innerhalb where aufgebracht werden muss. Kriterien könnten extern erstellt werden, bevor sie an where übergeben werden, aber wiederum macht ihre hierarchische Struktur es weniger einfach.

  1. Monkeypatching ActiveRecord, Hinzufügen or Implementierung. Setzen Sie dieses in config/initializers/active_record.rb:

    ActiveRecord::QueryMethods::WhereChain.class_eval do 
        def or(*scopes) 
        scopes_where_values = [] 
        scopes_bind_values = [] 
        scopes.each do |scope| 
         case scope 
         when ActiveRecord::Relation 
         scopes_where_values += scope.where_values 
         scopes_bind_values += scope.bind_values 
         when Hash 
         temp_scope = @scope.model.where(scope) 
         scopes_where_values += temp_scope.where_values 
         scopes_bind_values += temp_scope.bind_values 
         end 
        end 
        scopes_where_values = scopes_where_values.inject(:or) 
        @scope.where_values += [scopes_where_values] 
        @scope.bind_values += scopes_bind_values 
        @scope 
        end 
    end 
    

Damit können Sie or Abfragen wie dies zu tun:

Model.where.or(verified: true, active: true) 

# => SELECT "models".* FROM "models" WHERE ("models"."verified" = $1 OR "models"."active" = $2) [["verified", "t"], ["active", "t"]] 

Sie weitere Kriterien hinzufügen können, wie so :

Model.where.or(verified: true, active: true, other: false) 

Die Abfrage kann in eine Klasse mich gelegt werden Thod wie folgt aus:

def self.filtered(criteria) 
    where.or(criteria) # This is chainable! 
end 

oder in ihrem Umfang, der im Grunde die gleiche ist:

scope :filtered, lambda { |criteria| where.or(criteria) } 

Da criteria nur einen Hash, können Sie es auf bequeme Weise mit so vielen Elementen aufbauen können, wie Sie möchten :

criteria = {} 
criteria[:verified] = true if include_verified? 
criteria[:active] = true if include_active? 
... 

Model.filtered(criteria).where(... more criteria ...) 

Und da hast du es. Lesen Sie für weitere Informationen zu dieser SO Fragen:

ActiveRecord Arel OR condition

OR operator in WHERE clause with Arel in Rails 4.2

Schließlich, falls Sie nicht auf Lösungen von Drittanbietern entgegengesetzt sind, werfen Sie einen Blick auf die Squeel gem.