LoginSignup
0
0

Rails7 find と where では結果の型が異なる件

Last updated at Posted at 2024-05-19

はじめに

みなさん、こんにちは。torihaziです。
今回はタイトルの件について書いていこうと思います。
ちなみに初心者です。
間違いがないよう最善は尽くしていますが、何かあれば指摘をお願いいたします。

環境について

ruby 3.2.1
rails 7.0.6

結論

  • findならArray オブジェクト が
  • whereならActiveRecord::Relation オブジェクトで

返ってくるよということ。
例えば下記のような時。

id = [1]
@hoge_find = Hoge.find(id)
@hoge_where = Hoge.where(id: id)

これをviewのlink_toで参照しようとしたときに

= link_to hoge_path(@hoge_find) 
=> /hoge/1
= link_to hoge_path(@hoge_where) 
=> /hoge%23%3CHoge::ActiveRecord_Relation:・・・・・・"

生成されるURLが違ってくる。

なんで気づいたか

今、XクローンのDM機能(ユーザ同士の1対1チャット)を作っています。
Userにdevise gem は使っています。
スクリーンショット 2024-05-19 9.47.29.png

その時に

ログインユーザがすでにDMし合っているユーザの詳細画面において
表示されているDMアイコンをクリックした際にそのユーザとのDMページに遷移する

という処理を書いていました。
処理イメージだと次の感じです。

  1. user1でログインし、相互フォローしているuser8の詳細ページに遷移します
  2. そのページにおいてDMアイコンをクリックします
    スクリーンショット 2024-05-19 10.00.32.png
  3. するとDMしていなければDMに関するものをcreateしてから、あればそのままDMのページに遷移します。(urlだと/users/8/conversations/2 のような感じ)
    スクリーンショット 2024-05-19 10.20.28.png

この2番において

すでにDMをし合っているか

ということを判別するために下記のロジックをUser.rbに作りました。

user.rb
class User < ApplicationRecord
~~~
  has_many :participants, dependent: :destroy
~~~
  def find_conversation_with(other_user)
    id = ( participants.pluck(:conversation_id) ) &
         ( other_user.participants.pluck(:conversation_id) )
    id.any? ? Conversation.where(id: id) : nil 
  end
end

この結果をusers_controller.rbにおいて次のように使用しました。

users_controller.rb
  def show
  ・・・
    @conversation = current_user.find_conversation_with(@user)
    ・・・
  end

そしてビュー側で次のように表示を分けました。

users/show.html.slim
~~~
- if @conversation
    = link_to user_conversation_path(@user, @conversation), class: "btn btn-outline-dark rounded-circle" do 
    i.bi.bi-envelope-plus
- else    
  = link_to user_conversations_path(@user), data: { "turbo-method": :post }, class: "btn btn-outline-dark rounded-circle" do 
    i.bi.bi-envelope-plus

この時ビュー側で/users/8/conversations/2というlinkが生成されるのかと思いきや
恐ろしいurlが生成されていました。
スクリーンショット 2024-05-19 10.18.06.png

この原因は

whereならActiveRecord::Relation オブジェクトで

だからです。

そのため解決策としてはいろいろあるかと思いますが、自分は次のようにfindで行いました。

user.rb
class User < ApplicationRecord
~~~
  has_many :participants, dependent: :destroy
~~~
  def find_conversation_with(other_user)
    id = ( participants.pluck(:conversation_id) ) &
         ( other_user.participants.pluck(:conversation_id) )
-    id.any? ? Conversation.where(id: id) : nil 
+    id.any? ? Conversation.find(id) : nil 
  end
end

こうすれば urlも次のように/users/1/conversations/1のようになります。

スクリーンショット 2024-05-19 16.26.46.png

終わりに

なんとなくで使い分けていましたが、このような違いがありました。

これからも学習を続けていきたいと思います。

ただ今回のDMし合っているかの判別は 1対1のDMまでしかできないアプリしか使えないと思います。

グループチャットを考えたら機能しないかと思います。

そのためその時はその時考えようかと思います。

あとコードレビューもしていただいていないため、修正入るかもです。

そしたらその時直します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0