始めに
投稿のいいね数の順番によってソートする機能実装中に遭遇したエラーに関するメモです。
前提
Ruby 2.6系
Rails 5.2系
Slim導入済
devise導入済
実装されている内容
1.User、Post、Favoriteモデルが存在する。
#####2.favoritesテーブルを中間テーブルとして、usersテーブルとpostsテーブルが関連付けされている。
Favoriteモデル
app/models/favorite.rb
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :post
end
>Postモデル
>```ruby:app/models/post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :favorites
end
Userモデル
app/models/user.rb
class User < ApplicationRecord
has_many :favorites
has_many :favorite_posts, through: :favorites, source: :post
has_many :posts
end
#####3.セレクトボックスフォームのコード。
>```ruby:app/views/index.html.slim
= form_with url: search_path, method: :get, local: true do |form|
= form.select :keyword, [
['いいねが多い順', 'likes'],
['いいねが少ない順', 'dislikes'],
]
= form.submit
#####4.ルーティング。
config/routes.rb
Rails.application.routes.draw do
#検索結果をpostsコントローラのsearchアクションへ送信
get 'search' => 'posts#search'
resources :posts
end
#####5.条件に一致したPostを取得するpostsコントローラのsearchアクション。
>```ruby:app/controllers/posts_controller.rb
def search
@selection = params[:keyword]
if @selection == 'likes'
@posts = Post.find(Favorite.group(:post_id).order('count(user_id) desc').pluck(:post_id))
elsif @selection == 'dislikes'
@posts = Post.find(Favorite.group(:post_id).order('count(user_id) asc').pluck(:post_id))
end
end
###エラー地点
#####postsコントローラのsearchアクション内。
app/controllers/posts_controller.rb
def search
@selection = params[:keyword]
if @selection == 'likes'
@posts = Post.find(Favorite.group(:post_id).order('count(user_id) desc').pluck(:post_id))
elsif @selection == 'dislikes'
@posts = Post.find(Favorite.group(:post_id).order('count(user_id) asc').pluck(:post_id))
end
end
###エラー内容
>```ruby
DEPRECATION WARNING:
Dangerous query method
(method whose arguments are used as raw SQL)
called with non-attribute argument(s): "count(user_id) desc".
Non-attribute arguments will be disallowed in Rails 6.0.
This method should not be called with user-provided values, such as request parameters or model attributes.
Known-safe values can be passed by wrapping them in Arel.sql().
読んでみると、"count(user_id) desc"
に問題があって、Known-safe values can be passed by wrapping them in Arel.sql().
を適用させれば解決できるとのこと。
なので、該当箇所を以下のように記述します。
def search
@selection = params[:keyword]
if @selection == 'likes'
@posts = ... ...order(Arel.sql('count(user_id) desc'))... ...
elsif @selection == 'dislikes'
@posts = ... ...order(Arel.sql('count(user_id) asc'))... ...
end
end
先ほどとの違いは、order内のcountメソッドを使った文字列をArel.sql()で囲んでるところ。
こうしなければならない理由は、Rails6.0以降SQLインジェクションの脆弱性ゆえに、第三者から勝手にパラメータを潜り込ませるのを防ぐためとかなんとか。
しかしこれで解決はできました、良かったです。