Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails sort entries based on attributes of related model

I have a strange scenario: I would like to order all posts P by the time at which P's creator and the current user became friends.

The result would be a list of posts with newer friends' posts at the top and older friends' posts at the bottom. We can assume all users are friends of all other users, but each friendship has varying creation times.

I have Post, User, and Friendship models. Posts belong to users and users have many friendships.

class User < ActiveRecord::Base
  has_many :friendships
  has_many :friends, :through => :friendships
  has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
  has_many :inverse_friends, :through => :inverse_friendships, :source => :user
end

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User"
end 

class Post < ActiveRecord::Base
  belongs_to :user
end

What's the best way to do this? Thanks!

like image 608
stackOverlord Avatar asked Dec 02 '11 03:12

stackOverlord


3 Answers

You can order on the associated table by doing a nested join, like so:

@posts = Post.joins(:user => :friendships).order("friendships.friended_at")
like image 185
Sean Hill Avatar answered Nov 02 '22 18:11

Sean Hill


Turn Sean Hill's answer into a scope with:

class Post < ActiveRecord::Base
  belongs_to :user

  scope :ordered, -> {
    joins(:user => :friendships).order("friendships.friended_at")
  }
end
like image 35
creativehandle Avatar answered Nov 02 '22 18:11

creativehandle


Just an addendum to this, as a thank you, Sean Hill's answer also works, slightly modified, to sort results using has_many through relationships with fields_for in Rails 4.

So if Messages belong_to both Conversations and Users and you first want a list of Conversations on the User page—perhaps before updating or modifying some message attribute—but sorted by an attribute on Conversations (e.g. "title"), not Messages, you can go from User through Messages to Conversations and Conversation attributes.

In the view:

<% fields_for :messages, @user.messages.joins(:conversation).order("title ASC") do %>

or in the User.rb model:

has_many :messages, -> {joins(:conversation).order("title ASC")}
like image 1
Matthew Bennett Avatar answered Nov 02 '22 17:11

Matthew Bennett