Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to order a query by a translated field using globalize

I'm trying to order a query using a field which is translated with globalize2. The problem is that since stored in database and in an association I'm having lot of problems.

  • Doing an include of the translations and ordering by category_translations.name doesn't work.
  • I tried a default_scope but since it doesn't allow to use lambda or a block for the conditions I can't get it working unless I use this patch for ActiveRecord http://gist.github.com/81187
  • I've tried with the with_translations defined in globalize2, however I get an error with this and I couldn't get it to work even without ordering.

I've something like that

class Category < ActiveRecord::Base
  validates_presence_of :name
  validates_uniqueness_of :name
  has_many :products, :dependent => :destroy

  translates :name
end

The question is, how do I order by the translated name?

like image 544
Fran Avatar asked Aug 29 '10 21:08

Fran


3 Answers

The with_translations method seems to be way to go:

Category.with_translations(I18n.locale).order('category_translations.name')

Also, if you are using PostgreSQL you might want to add case insensitive order to that:

Category.with_translations(I18n.locale).order("LOWER(category_translations.name) ASC")

More about this here: https://github.com/globalize/globalize#scoping-objects-by-those-with-translations

like image 115
rebagliatte Avatar answered Nov 19 '22 17:11

rebagliatte


I tested this using sqlite3, and it works.

class Category < ActiveRecord::Base
  ...
  named_scope :ordered, lambda {|locale|
    {
      #:select => "categories.*, categories.name sort_name",
      # For MySQL
      #:select => "categories.*, IF(category_translations.name IS NULL, categories.name, category_translations.name) sort_name",
      # For sqlite3
      :select => "categories.*, (CASE WHEN category_translations.name IS NULL THEN categories.name ELSE category_translations.name END) sort_name",
      :joins => ActiveRecord::Base.sanitize_sql_array([
        "LEFT JOIN category_translations on category_translations.category_id = categories.id AND category_translations.locale = ?", locale]),
      :order => "sort_name"
    }
  }
  ...
end

Category.ordered(some_locale).all # Returns all records, sorted by translated name
like image 40
aceofspades Avatar answered Nov 19 '22 19:11

aceofspades


I'm assuming that any model called Category would have at most hundreds of records if not less. Maybe you can consider sorting the results in memory, after fetching them.

@categories = Category.all # or whatever else to retrieve what you want
@categories.sort! { |a,b| a.name <=> b.name }

Beware though. This would become a bad idea if the categories table contains more than thousands of records.

like image 1
edgerunner Avatar answered Nov 19 '22 19:11

edgerunner