views:

140

answers:

3

I've been trying to deal with this problem for a couple of hours now and haven't been able to come up with a clean solution. It seems I'm not too good with rails...

Anyway, I have the following:

Diagram

In code:

class Article < ActiveRecord::Base
  has_many :line_aspects
  has_many :aspects, :through => :line_aspects
  #plus a 'name' field
end

class LineAspect < ActiveRecord::Base
  belongs_to :article
  belongs_to :aspect
  #plus a 'value' field
end

class Aspect < ActiveRecord::Base
  has_many :line_aspects
  has_many :articles, :through => :line_aspects
  #plus a 'name' field
end

Now, what I would like to do, is to sort these in two steps. First sort of Articles by their Articles.name, and then inside sort them by Aspect.name (note, not the middleman).

For instance, alphabetically (sorry if the notation is not correct):

[{
   article => 'Apple',
   line_aspects => [
      {:value => 'red'}, #corresponding to the Attribute with :name => 'color'
      {:value => 'small'} #corresponding to the Attribute with :name => 'shape'
   ]
},{
   article => 'Watermelon',
   line_aspects => [
      {:value => 'green'}, #corresponding to the Attribute with :name => 'color'
      {:value => 'big'} #corresponding to the Attribute with :name => 'shape'
   ]
}]

Again, note that these are ordered by the aspect name (color before shape) instead of the specific values of each line (red before green). My intention is to display these in a table in the view.

With this result:

Table

This is the code I'm using:

<table>
  <tr>
    <th>Category</th>
    <% @articles.each do |article| -%>
      <th><%= link_to article.name, article -%></th>
    <% end -%>
  </tr>

  <% @aspects.each do |aspect| -%>
    <tr>
      <td><%= aspect.name -%></td>

      <% aspect.line_aspects.each do |line_aspect| -%>
        <td><%= line_aspect.value %></td>
      <% end -%>
    </tr>
  <% end -%>
</table>

I have not found a good way to do this in rails yet (without resorting to N queries). Can anyone tell me a good way to do it (even changing the view, if my approach is wrong)?

(I found a similar question in hyphen)

UPDATE: Here's how I'd do it in SQL:

SELECT line_aspects.value FROM line_aspects, aspects, articles 
WHERE articles.id = line_aspects.article_id 
   AND aspects.id = line_aspects.aspect_id
ORDER BY aspects.name, articles.name

But I'd like to do it the rails way.

UPDATE: Added the view code. Which might expose my predicament a bit better.

+1  A: 

This is only a partial solution, since it doesn't solve your problem entirely.

You could use named_scope to order the model by the respective field. Something along the lines of: named_scope :ordered, :order => "name ASC"

It's a simple solution (at least syntax wise, not sure about complexity). The only issue I can forsee is that you cannot compond multiple named_scopes for sorting in a single query.

For the second sort, you can use Enumerable#sort_by or array.sort on the collection you obtained.

Hope this helps a bit :)

Pran
Thanks for the help.
Kenji Kina
+1  A: 

After trying the other answer, I found a way to do it from the model. I'm not sure if this is the Right Way™, but it seems like a viable solution (to let the database engine sort it).

In the Aspect model I changed this line:

has_many :line_aspects

Into this:

has_many :line_aspects, :include => :article, :order => 'articles.name'

I'd still like to hear from more people if possible, though.

Kenji Kina
A: 

This query fetches all articles and eager loads its aspects and sorts them by article name and ascpect name:

@articles = Article.all(:include => [ :aspects ], :order => "articles.name asc, aspects.name asc")
phil