Я работаю над приложением, которое моделирует дружбу между пользователями.
class User
has_many :friendships
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
end
class Friendship
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
end
Когда два пользователя становятся друзьями, создаются два экземпляра класса дружбы, по одному для каждого направления дружбы. Это необходимо, поскольку в каждом направлении могут быть установлены разные разрешения, а также для упрощения поиска, поэтому мы всегда ищем по user_id, не создавая второй индекс и не удваивая количество поисков.
Возможно ли, используя либо находку, либо именованную область видимости, чтобы вытащить друга вместе с контр-дружбой? Я хотел бы сделать это, потому что я позволяю пользователям редактировать разрешения дружбы, которые хранятся в их собственной части дружбы, и, таким образом, когда я показываю информацию о друге пользователю, мне нужно проверить противоположную ногу, чтобы увидеть как установлены разрешения. (Кроме того, кажется ли это логичным способом сделать что-то? Или вы храните на противоположной стороне, или вы должны проконсультироваться с противоположной стороной перед отображением, оба вида неприятны.)
Мне удалось создать пакет SQL, который выполняет эту работу:
def self.secure_friends(user_id)
User.find_by_sql("SELECT u.*, cf.permissions
FROM users u
INNER JOIN friendships f ON u.id = f.friend_id
INNER JOIN friendships cf ON u.id = cf.user_id AND cf.friend_id = #{user_id}
WHERE ((f.user_id = #{user_id}) AND (f.status = #{Friendship::FULL}))")
end
Функция возвращает все имена и идентификаторы друзей пользователя вместе с разрешениями. Тем не менее, это на самом деле не кажется идеальным, и это означает, что я должен получить доступ к разрешениям, вызвав friend.permissions, где разрешения на самом деле не являются членами друга, а просто объединяются как атрибут с помощью find_by_sql. В общем, я бы действительно предпочел сделать это с помощью named_scope или вызова find, но, к сожалению, каждая попытка использовать этот подход приводила к ошибкам, так как Rails не давал присоединиться к одной и той же таблице соединения Friendship. запрос дважды.