[Rails] whereメソッドの「?」(プレースホルダ)
whereメソッドの「?」とは
published.where('published_at < ?', Time.current)
結論、?
はプレースホルダと呼ばれ、第二引数で指定した値が置き換えられます。
つまり、Time.current
で取得された現在の時間が?
と置き換えれて、published_at
の値と<
で比較して検索します。
whereメソッドとは
テーブル内の条件に一致したレコードを配列の形で取得できるメソッドです。
findやfind_byメソッドは1件づつ取得しますが、whereは条件に当てはまるレコードをすべて取得します。
モデル名.where("条件")
基本的な記述方法
SQLインジェクション対策のため、基本的に1つの条件で検索をするときにはwhere(カラム名: "条件")のようにシンボル指定で定義します。
User.where(name: "太郎")
# => nameカラムが太郎のレコードを取得
SQLインジェクションとは
SQLインジェクションとはSQLを呼びだすときにセキュリティ上の不備を利用して、データベースのデータを不正に操作する攻撃方法のことをいいます。
例)
id = params[:id]
User.where("id = #{id}")
仮にユーザーidが1のユーザーのときは下記のようなSQLが発行されます。
SLECT 'users'.* FROM 'users' WHERE (id = 1)
そしてユーザーidが1のユーザーのレコードを取得できます。
ただこの1の部分に1 OR 1 = 1という値が入ってしまうパターンを見ていきます。
id = '1 OR 1 = 1'
User.where("id = #{id}")
発行されるSQLは下記のようになります。
SLECT 'users'.* FROM 'users' WHERE (id = 1 OR 1 = 1)
ORによってid = 1と1 = 1の2つの式を判定します。
SQLではWHEREにtrueが渡されるとすべてのレコードを取得します。
この時1 = 1の式がtrueと判断されるため、すべてのレコードが取得できてしまいます。
これではすべてのユーザーのデータが取得されてしまうため、セキュリティ上危険です。
対策としてハッシュ形式で記述しておけばこれを防ぐことができます。
User.where(id: id)
SELECT `users`.* FROM `users` WHERE `users`.`id` = 1
もしくはプレースホルダを使います。
複雑な検索(プレースホルダ)
基本的な使われ方。
User.where("kind > ?", 1)
kindが1より大きいユーザーをすべて取得できます。
プレースホルダの?が第二引数の1と置き換わって、kindと比較して検索します。
もう一つ複雑な例としてあげます。
app/models/article.rb
published.where('published_at < ?', Time.current)
※publishedはArticleモデルでenum型で定義されています。
class Article < ApplicationRecord
enum state: { draft: 0, published: 1, waiting_for_publication: 2}
end
publishedはstateカラムのenumがpublishedのすべてのオブジェクトが含まれています。
Articleモデル内において下記は同義
published
Article.published
self.published
=> [#<Article:0x00007f9ffb5e77f0
id: 5,
category_id: 1,
author_id: nil,
uuid: "95d9e832-822f-4db1-aaf7-8f9d9e68c71e",
slug: "slug",
title: "例",
description: "例",
body: "",
state: "published",
published_at: Thu, 26 Aug 2021 20:18:00 JST +09:00,
created_at: Tue, 24 Aug 2021 20:15:54 JST +09:00,
updated_at: Wed, 25 Aug 2021 17:27:43 JST +09:00,
deleted_at: nil>,
#<Article:0x00007f9ffb5e7660
id: 6,
category_id: 1,
author_id: nil,
uuid: "abbbc8d5-d37c-44a9-9f14-de8f4796519a",
slug: "例",
title: "例",
description: "",
body: "",
state: "published",
published_at: Wed, 25 Aug 2021 17:46:00 JST +09:00,
created_at: Wed, 25 Aug 2021 17:45:44 JST +09:00,
updated_at: Wed, 25 Aug 2021 17:53:55 JST +09:00,
deleted_at: nil>]
上記のような記述方法をプレースホルダといいます。この書き方もSQLインジェクションを防ぐ書き方です。
プレースホルダの記述方法とまとめ
先程の例です。
published.where('published_at < ?', Time.current)
?
はプレースホルダと呼ばれ、第二引数で指定した値が置き換えられます。
上記の例では現在の時間を取得する、Time.current
などの変数が入ることが多いです。
?
を使うときにはwhere(カラム名: "条件"
)の書き方はできないので注意。
つまり、Time.current
で取得された現在の時間が?
と置き換えれて、published_at
の値と<
で比較して検索します。
参考
Discussion