5

Rails アプリを Rails 4.2 に移植しています。このRailsアプリには、関連付けにかなり複雑な手動のSQLコードが含まれています。一部はDBの最適化(JOINの代わりにサブセレクトなど)が原因で、一部は執筆時点(Rails 3.0)で実行可能な代替手段がないため、一部は確実に知識不足(少なくとも、それは簡単に解決できることを願っています)。

例: InternalMessage クラス。メッセージはユーザー間で送信でき (InternalMessage の受信者、およびメッセージの「削除」は、InternalMessagesRecipients に保存されます。これは、複数存在する可能性があるためです)、読み取り、返信、転送、および削除が可能です。関連付けは次のようになります。

class User < AR::Base
  has_many :internal_messages,
      :finder_sql => "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " +
          ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
          ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}',
      :counter_sql => 'SELECT count(DISTINCT(internal_messages.id)) FROM internal_messages ' +
          ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
          ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}'
  # ...
end

重要な部分は最後の "OR" 句です。この関連付けにより、受信メッセージと送信メッセージの両方を取得し、ユーザー テーブルと個別に結合します。

  has_many :sent_messages, -> { where(:sender_deleted_at => nil) }, :class_name => 'InternalMessage', :foreign_key => 'sender_id' #, :include => :sender
  has_many :internal_messages_recipients, :foreign_key => 'recipient_id'
  has_many :rcvd_messages, :through => :internal_messages_recipients,  :source => :internal_message, :class_name => 'InternalMessage'

InternalMessage には複数の受信者がいる可能性があるため (また、送信者自身に送信することもできます)。

finder_sqlQ: これを Rails 4.2 互換のhas_many定義に移植するにはどうすればよいですか?

4

2 に答える 2

4

アップデート

これは意味がないことを少し前に学びました。has_manyリレーションシップには、少なくとも一方向の単射接続が必要なので、SQL 句の "OR" は意味がありません。CREATE新しいレコードを作成するために満たす必要がある条件を操作でどのように決定する必要がありますか? この関係は定義上読み取り専用であるため、has_many関係ではありません。

この場合、 の代わりに単純なクラス メソッド (またはスコープ) が正しい答えになりhas_manyます。複数のクエリの結果を連結するには、次のようなものを使用します

def internal_messages
  InternalMessage.where( id: sent_message_ids + received_message_ids)
end

結果のオブジェクトを連鎖可能に保つ(つまり、@user.internal_messages.by_dateなど)

于 2015-12-23T21:45:53.627 に答える
3

SQL 文字列を含む proc をスコープとして渡します。

has_many :internal_messages, -> { proc { "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " +
      ' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
      ' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}' } }
于 2015-01-18T14:34:51.610 に答える