【Rails】通知機能のメソッドを覚えておく
今回は、通知機能の実装で学んだことと知識の定着をはかるためのアウトプットをメモ書き程度にして行きたいと思います。
実装内容
自分が投稿した内容にコメントが来たら通知が来る通知機能の実装
完成画面
前提
内容
通知機能はnotificationモデルを作成して実装した。
通知機能のコード
- comments_controller.rb
def create @comment = Comment.new(params_comment) @post = @comment.post @comment.user_id = current_user.id if @comment.save @post.create_notification_comment!(current_user, @comment.id) redirect_back(fallback_location: root_path, notice: '投稿が完了しました。') else redirect_back(fallback_location: root_path, notice: '投稿に失敗しました。') end end
- comment.rb
def create_notification_comment!(current_user,comment_id) temp_ids = Comment.select(:user_id).where(post_id: id).where.not(user_id: current_user).distinct temp_ids.each do |temp_id| save_notification_comment!(current_user, comment_id, temp_id['user_id']) end save_notification_comment!(current_user, comment_id, user_id) if temp_ids.blank? end def save_notification_comment!(current_user, comment_id, visited_id) notification = current_user.active_notifications.new( post_id: id, comment_id: comment_id, visited_id: visited_id, action: 'comment' ) if notification.visiter_id == notification.visited_id notification.checked = true end notification.save if notification.valid? end
以下で詳しく見ていきます。
コメントコントローラーのsave
でコメント保存ができた後に下記コードで通知メソッドを呼び出して実行している。
@post.create_notification_comment!(current_user, @comment.id)
(current_user, @comment.id)
で現在のユーザー情報とコメントIDをメソッドに渡している。
@post = @comment.post
ここでcommentインスタンスに紐づいたpostモデルを引っ張ってきて@postに格納している。
@post.create_notification_comment!
postモデルに記述してあるcreate_notification_comment!メソッドを呼び出す。
where.not(user_id: current_user)
ここで現在のユーザー(自分)のユーザーIDは取得しない様にし、where(post_id: id)
で投稿IDを選択し、 select(:user_id)
でユーザーIDのカラムだけ選択しています。
その結果、自分以外で投稿にコメントしているユーザーを取得してtemp_ids
に格納している。
distinct
メソッドで重複する同じレコードを削除し1つだけにしている。
temp_ids = Comment.select(:user_id).where(post_id: id).where.not(user_id: current_user).distinct
複数コメントが来た場合、毎回通知を送る時のレコードを作成する。
temp_ids.each do |temp_id| save_notification_comment!(current_user, comment_id, temp_id['user_id']) end ・ ・ ・ def save_notification_comment!(current_user, comment_id, visited_id) notification = current_user.active_notifications.new( post_id: id, comment_id: comment_id, visited_id: visited_id, action: 'comment' ) ・ ・ ・ end
save_notification_comment!(current_user, comment_id, temp_id['user_id'])
の引数の部分をdef save_notification_comment!(current_user, comment_id, visited_id)
の引数(current_user, comment_id, visited_id)
に格納される。
↓
現在のユーザーとコメントIDと通知を受信した人のIDとcommentアクションをレコードに入れて作成している。
初めて投稿にコメントが来た場合に通知レコードを発行するメソッドを実行する。
if temp_ids.blank?
でtemp_ids
内にオブジェクト情報が入っていない場合にメソッドを実行する後置if文で書いている。
save_notification_comment!(current_user, comment_id, user_id) if temp_ids.blank?
【SQL】テーブル作成と制約に関して
今回の記事はテーブル作成とその際の制約に関して学んだことをアウトプットしていきます。
DBのDCLに関して
DCLとは、データの制御を行ってくれるもので、主に誰に、どの様なデータ操作やテーブル操作を許すかといった権限を設定するためのSQL命令の総称のことを言う。
GRANT文とREVOKE文
・権限を付与するSQL命令が「GRANT文」
GRANT 権限名 TO ユーザ名
・権限を剥奪するSQL命令が「REVOKE文」
REVOKE 権限名 FROM ユーザ名
テーブルの作成
テーブルは「CREATE TABLE」で作成できる。
CREATE TABLE テーブル名( 列名1 列1の型名 列名2 列2の型名 ・ ・ ・ ・ );
デフォルト値(初期値)
テーブル作成時に、列に初期値としてDEFAULT(デフォルト値)を設定しておくことができる。
デフォルト値を設定しておくことで、NULLなど格納されたくないデータが格納されない様に防ぐことができるし、初期から入れて起きたい値を設定しておくことができる。
CREATE TABLE テーブル名( 列名1 列1の型名 DEFAULT デフォルト値 );
テーブルの削除
テーブルを削除したい場合は「DROP」で削除できる。
DROP TABLE テーブル名
ALTER TABLE文(テーブル定義の変更)
既存の作成済みのテーブルの中身を変更したい場合は、ALTERを使用することで更新して変更することができる。
・列の追加 ALTER TABLE テーブル名 ADD 列名 型 制約 ・列の削除 ALTER TABLE テーブル名 DROP 列名 型 制約
制約
情報を追加する時などに起こりうる、人為的なミスなどによる予期せぬミスに備えて、テーブル作成時に制約を設けることが良くある。
制約を設けてあえて制限をかけることで、予期せぬトラブルを未然に防ぐことができる。
代表的な制約
・NOT NULL制約
・UNIQUE制約
・CHECK制約
・主キー制約
・外部キー制約
NOT NULL制約
NOT NULL制約は指定した列内にNULLが格納されることを防ぎたい時に使用する。
UNIQUE制約(ユニーク制約)
ユニーク制約を設定することで、列内に同じ内容の情報が重複して登録されない様に制限をかけてくれる。
なので、メールアドレスなど同じ名前のデータ情報が保存されたくない時に使用する。
ちなみにRailsだと
Railsだとmigrationファイルの制限をかけたいカラムの行に「unique: true」を記述すればユニーク制約を掛けられる。
class AddEmailToUsers < ActiveRecord::Migration def change add_column :users, :email, :string add_index :users, :email, unique: true end end
また、アプリケーションのmodelファイル側でユニーク制約をかけたい場合は「uniqueness: true」を記述する。
class User < ApplicationRecord validates :name, uniqueness: true end
主キー制約
主キー制約はNOT NULL制約とUNIQUE制約を同時に使用したい時に使用する。
主キー制約を使用することで、この2つの役割をになってくれる。
ちなみにRailsでは主キーはIDカラムとして自動生成される。
これによりIDカラムはNOT NULLにもなるし、UNIQUE制約で重複することもなくなる。
ちなみに主キーとは?
前にもチラッと書いたが、主キーとはテーブルの情報を一意に(ユニークに)識別するためのものである。
この表を参照するときのレコード(行)のこのIDだ!というこのIDの部分のこと。
外部キー制約
外部キーが参照している先に、必ず行(レコード)が存在していて、リレーションシップが成立する様にしておくことのことを言う。
内部結合など結合で複数の2つのテーブル間同士がリレーションシップで繋がっている時に、外部キーで参照している先の行(レコード)がなくなってしまうと、エラーになって大問題になるので、これを防ぐために外部キー制約を設ける。
Railsモデルのdependent: :destroyに近い役割かな?
外部キーとは?
外部キーは「他のテーブルのデータを参照することが出来る様にするための制約をつけたカラム」のことを言う。
外部キーを生成した際は、参照先の主テーブルの名前+idの名前が外部キーのカラム名前になる。
なので、仮にUserテーブルに紐づく子テーブルのPostテーブルがUserテーブルを参照していた場合、Postテーブルには、「user_id」という名前の外部キーが生成される。
user_idの中には、対応するuserのidが格納される。
【SQL】複数テーブル同士の結合について
今回は複数のテーブル同士の結合について学んだことをアウトプットしていきます。
リレーションシップ
他のテーブルにある行と関連付けるために、外部キーを利用してリレーションシップを構成する。(結合)
外部キーとは?
外部キーとは、関連する他テーブルのある行(主キー列など)の値を記述したもの。
例えばUserテーブルが主テーブルでPostテーブルが付随する1対多な場合、Userテーブルを参照するために、Postテーブルに外部テーブルとして、外部キーのuser_idカラムを作る。
その外部キー(user_id)の中身に参照先の主テーブルの主キー(id)を格納して紐づける。
結合
結合を使用することで、複数のテーブル同士を繋いで、互いの表の中のデータを取り出すことができる。
また、結合はテーブルをまるっと繋ぐことではなく、結合条件が満たされた時にのみ、その満たされた条件に関する行を1つ1つその場に応じて結合されていく。
JOIN句
JOINで結合する時は内部結合が行われる。
(基本的に内部結合が一番利用される)
SELECT 日付, 名前 AS 費目, メモ FROM 家計簿 (主テーブル) JOIN 費目 (結合したい別のテーブル) ON 家計簿.費目ID = 費目.ID
JOINで結合先のテーブルを決めたら、その結合の条件をON句で指定する。
上記の結合指示しているSQL文は、
①JOINで結合したい費目テーブルを指定 ②FROMで主テーブルである家計簿テーブルを指定 ③ON句で指定した結合条件に則って、家計簿テーブルの費目IDと費目テーブルのIDが同じなら、その一致した行だけ取り出して結合する(全てのテーブルを結合するのではなく、一致したとこだけ結合する)
ON句
ON句が結合条件を書く時に記述するSQL
例として、下記のSQL文は家計簿テーブルの費目IDと費目テーブルのIDが同じ場合に結合するという意味の結合条件です。
ON 家計簿.費目ID = 費目.ID
INNER JOIN
INNER JOINはJOINとほぼ同じ意味で、基本的にRailsではINNER JOINでSQLを発行して内部結合が行われる。
基本的にRailsではINNER JOINの方が利用される。
Railsのjoinsメソッド
このINNER JOINはRailsでいう「joins」メソッドで同じ動きをする。
joinsメソッドは、Railsでアソシエーションしてある場合に使用できる。(belong_toとかで紐付けを先に定義しておく)
【SQL】集計関数やグループ分けに関すること
今回は値を計算する集計関数や、各列で同じ行内容を1つにグループ化したり、そのグループ化したものを計算する方法などに関することを学んだので、メモ書き程度にアウトプットしていきます。
集計関数
まずそもそもなのですが、集計関数はSELECT文でしか利用できないです。
理由は、集計関数は、その名の通り、集計したものを計算してくれるものなので、集計するために検索して選択しないとそもそも始まらないからです。
代表的な集計関数
集計関数の計算は、集計の対象となる全ての行に対して、まとめて1回のみ行われる。
なので、どれだけ行数があろうとも、基本的に返される結果は1つだけ。
ただし、そこはグループ化してから計算したりすると、各項目ごとに計算を行うこともできるので、そこは後ほど書く。
集計関数の記述方法
基本的に「集計関数(列名)」で実行する。
また計算結果の列名は、 ASで変更することができる。
SELECT SUM(出金額) AS 合計金額, AVG(出金額) AS 平均出勤額 FROM 家計簿
GROUP BY(データのグループ化)
各行の同一項目で複数個同じ名前の行が存在している時、それらを1つにグループ化してまとめることができる。
そのグループ化は「GROUP BY」を使用することで行うことができる。
なので、例えば費目列に交際費が3つあった場合、その交際費を1つにまとめてくれる。
交際費 交際費 交際費 水道費 水道費 → 光熱費 光熱費 (グループ化) 交際費
グループ化して計算する基本構文
SELECT グループ化したい列名, 集計関数 FROM テーブル名 (WHERE 絞り込み条件) GROUP BY グループ化したい列名
↓
SELECT 費目, SUM(出金額) AS 費目別の出金額合計 FROM 家計簿 GROUP BY 費目
グループ化ごの集計計算の処理の動きとしては、まず、SELECT文とWHERE句で対象の列を検索し、その次にグループ化し、最後に集計計算が行われて、グループごとに答えが返ってくる。
Railsでも同じ動きがある
group(:カラム名)
Railsでも「group」メソッドを使用することで、同じ様にグループ化して処理を行うことができる。
集計結果はHAVING句で絞り込む
グループ分けされた後にまたWHERE句で絞り込みをしようとするとエラーになります。
なのでこういう場合は、HAVING句を使用します。
HAVING句はWHERE句とよく似ているが、HAVING句は集計関数が全て完了した最後に実行されて絞り込みが行われるので、その点が少し違う。
グループ化してから絞り込む記述方法
SELECT グループ化したい列名, 集計関数 FROM テーブル名 (WHERE 絞り込み条件) GROUP BY グループ化したい列名 HAVING 集計結果に対する絞り込み条件
↓
SELECT 費目, SUM(出金額) AS 費目別の出金額合計 FROM 家計簿 GROUP BY 費目 HAVING SUM(出金額) > 0
この様に記述することで、家計簿テーブルの費目を集計した後に、出金額が0を含まない、0以上のものだけ結果として返す様に命令できる。
なので、処理の流れとして、 SELECTやWHEREでまず絞り込み検索をし、その後に各列名ごとにグループ化が行われ、それらに対して集計関数で計算が行われ、最後にHAVINGで計算結果を特定条件で絞り込こんで答えを返す。
RailsでもHAVINGがある
Railsでロジックを組む時にも、このHAVINGを使ってグループ分けされたものを条件式に沿って絞り込みして戻り値として返すことができる。
group(:カラム名).having("条件式")
havingメソッドとwhereメソッドの違い
余談ですが、Railsでのhavingメソッドとwhereメソッドは処理が似ていますが、各々で処理されるタイミングが違います。
・whereメソッドはgroupメソッドが実行される前のデータを対象に絞り込みを行う ・havingメソッドはgroupメソッドが実行されてグループ化された後のデータを抽出して絞り込み処理を行う
似ている様でwhereはグループ化される前に実行されて、havingはグループ化された後に実行されるということなので、処理のタイミングが違うので注意する。
【SQL】関数や計算式を使う時に関すること
この記事では、関数に関することや、計算式の扱い方などについてアウトプットしていきます。
SELECT文で計算式を使う場合
SELECT文では、真偽を返す条件式以外に、計算結果を返す計算式を使うこともできる。
○計算式(例) 出金額 + 100 = 出金額に+100された金額が返される
SELECT 出金額, -- 列名 出金額 + 100, -- 計算式 'SQL' -- 固定値 FROM 家計簿
この様に、計算式をSELECT文の中に入れても戻り値で計算結果が返ってくる。
ただし、このままだと列名が「出金額+100」となんか不格好な名前になっているので、「AS」を使ってもっと分かりやすい名前に再定義すると良い。
「AS」とは?
ASを使用すると、列名やテーブル名を違う名称に変更することができる。
使い方は「AS + 任意のキーワード」で変更できる。
SELECT 出金額, 出金額 + 100 AS 百円増しの出金額, 'SQL' FROM 家計簿
INSERT文で計算式を使う場合
INSERT INTO 家計簿(出金額) VALUES (1000+105) => 出金額の行に1105が追加される
UPDATE文で計算式を使う場合
UPDATE 家計簿 SET 出金額 = 出金額+100 => 出金額+100された計算結果が出金額に格納される(変更される)
DBMSの処理の原則
DBMSはテーブル内の表の各行1つづつ、上から順番に1 行づつ(1つづつ)評価して処理していくので、計算式なども1行づつ毎回計算される。
関数
関数の呼び出しに指定した情報を「引数」と言い、その引数をその後に処理(加工)をして、処理されて返ってきた結果の情報を「戻り値」という。
関数の呼び出し方
関数名(引数・・・)
・LENGTH関数を使った例
SELECT メモ, LENGTH(メモ) AS メモの長さ FROM 家計簿 ※LENGTH関数は、受け取っとた引数の文字数を戻り値として返す関数
TRIM関数(空白を除去する)
TRIM関数は引数の空白を除去して返してくれる関数。
ある文字列の前後にある余計な空白を除去したい時にTRIM関数を使うと除去してくれる。
他にもLTRIM関数やRTRIM関数などがある。
・TRIM関数 ・・・ 前後の空白を除去する ・LTRIM関数 ・・・ 左側の空白を除去する ・RTRIM関数 ・・・ 右側の空白を除去する
Rubyの「strip」メソッドと同じ
Rubyではstripメソッドを使用することで、文字列の空白を除去してくれる。
ただし、stripメソッドは「全角の空白」は除去してくれないので、全角の空白を除去したい時は、正規法を使って半角へ変換したのちに除去する。
全角空白を半角に変更する方法
Rubyの「gsub」メソッドを使うことで、変更することができる。
ただし、stripメソッドはgsubメソッドで全角空白→半角空白へ変換した後に実行する様に、後ろにstripメソッドを持ってくる様にする。
str.gusb(//," ").strip
【SQL】SELECT文にだけ使える検索に関する修飾
検索の命令文であるSELECT文でしか利用できない抽出されたものをさらに加工(並び替え)して検索結果を返すことができる「修飾」をメモ書き程度に書き残していく。
DISTINCT(重複した行を除外してくれる)
列(カラム)内の重複した行(row)を除外して、同一で複数あるものを1つだけにしてくれる。同じものは保存されない。↓
・before ・After 列(費目) 列(費目) 食費 食費 食費 → 光熱費 光熱費 水道費 水道費 光熱費
※DISTINCTを使用する時は、必ず最初にSELECT をする。
SQLが読み込まれる順番は、まずはSELECTの様な代表的な命令形で抽出される。
その後にそのSELECTで選択されている結果をさらに加工(修飾)する。
SELECT DISTINCT 入金 FROM 家計簿
Railsで対応するメソッド①
DISTINCTはRailsでのメソッドだと、distinctメソッドがこれに該当する。
User.select(:first_name, :age).distinct
※Railsでdistinctメソッドを利用する場合は、必ずselectメソッドで指定してあげてからじゃないと、重複を除外してくれない。
ORDER BY(検索結果を並び替える)
SELECT文の最後に「ORDER BY」を記述することで、指定した列を好きな様に並び替えられる。
SELECT * FROM 家計簿 ORDER BY 日付 DESC
これを利用すれば、ランキング機能なんかも作れる。
Railsで対応するメソッド②
orderメソッドがこの並び替えの処理をしてくれる。
Client.order(:create_at)
OFFSET - FETCH(任意の場所から数行だけ取得する)
行を限定して取得する。
この修飾を活用すれば、ランキング上位5を表示させるとかもできる様になる。
また、3番目に高いものだけ取得するとか範囲を限定に絞ってもできる。
SELECT 列名… FROM テーブル名 ORDER BY 列名… OFFSET 先頭から除外する行数(数値で) ROWS (FETCH NEXT 1(取得行数) ROWS ONLY)
Railsで対応するメソッド③
limitメソッドやoffsetメソッドで任意で取り出し場所を指定できる。
Book.order(price: :desc).limit(3).offset(6)
この時の数字は、0が1番目で、2番目は1。3は4番目なので、順番数えるの間違えない様に。
【SQL】 WHERE句や演算子系まとめ
今回はSQLで条件を絞ってやり取りしたい時に使用する「WHERE句」と、それに付随する演算子などについての学びのアプトプットをします。
WHERE句とは?
SELECTやDELETE時に一緒に修飾語として、条件をさらに絞って自由自在に好きな様にDBを操作することができる。
WHERE句の基本
基本的にWHEREを使用しないと処理対象の行は全ての行が対象となってしまい変更や選択をしてしまうことになるので、かなりの頻度で使用する。
また、 WHEREはデータを追加する時以外で頻繁に使用され、更新、削除、選択時にしか使用できない。追加時(INSERT)にはWHEREは使用しない。
また、記述方法として、WHEREの後ろに条件式を書いて条件を絞り込む。
WHERE 条件式
条件式とは?
条件式とは、結果が必ず「真」or「偽」で返ってくるもののことを指す。
なので結果が「真偽」ではなく、ただの文字列だったりする場合は、WHEREは使用できない。
○この書き方はOK WHERE 出金額 < 1000 (出金額より1000大きいか?Yes or No なので真偽が帰ってくる) ×この書き方はNG WHERE 出金額 + 1000 (結果が、出金額+1000なので、真偽ではなく文字列が返ってくるのでこれはNG)
演算子
また、条件式を書く時は様々な演算子を用いて、いろんなパターンの条件でDBを操作する。
比較演算子
比較演算子 意味 = 左右の値が正しい < 左辺は右辺より小さい > 左辺は右辺より大きい <= 左辺は右辺の値以下 >= 左辺は右辺の値以上 <> 左右の値が等しく無い (<>これはRubyでは「!=」と同じ意味)
NULL
0ではなく、NULLという何も定義されていない未定義や空白を表すものがある。
NULLを判定するものがあり、NULLであることを判定するのが↓
条件式 IS NULL
NULLではないことを判定するのが↓
条件式 IS NOT NULL
LIKE演算子(部分一致検索)
LIKE演算子を使うことで、検索対象で部分的に一致しているものを選択することができる。 (〇〇を含む、この条件に当てはまるものを選択する。みたいな時に使える)
LIKE演算子では、「%」と「 _ 」を使用する。
「%」 は任意の0文字以内の文字列(何でもOK)を検索する。
なので、「%1月%」と書いてあれば、11月でも良いし、「1月1」というものでも当てはまって当てはまって選択して検索してくれる。
%%で囲っている部分に何もなくても検索してくれる。
SELECT * FROM 家計簿 WHERE メモ LIKE '%1月%'
さらに、「 _ 」は、任意の1文字以上があるもののみ検索してくれるので、'1月_'とあった場合は、1月の後ろに何かしらの文字があるもののみ検索する。
SELECT * FROM 家計簿 WHERE メモ LIKE '1月_'
BETWEEN演算子(範囲判定)
式 BETWEEN 値1 AND 値2
BETWEEN演算子では、値が「値1以上かつ値2以下」の場合に真が返ってくる。
これに当てはまらなければ偽が返ってくる。
・例(100円〜3000円に当てはまる出費のみ取得するSELECT文) ↓
SELECT * FROM 家計簿 WHERE 出金額 BETWEEN 100 AND 3000
IN演算子
IN演算子を使うことで、複数パターンと比較することができ、複数の中で当てはまるものがあった場合に「真」を返す。
式 IN (値1, 値2, 値3・・・)
なので、=演算子では、1つの値としか比較できないが、IN演算子を使うことで、「値1もしくは値2に当てはまるもの」みたいな感じで複数を一度に比較し判定してくれる。
SELECT * FROM 家計簿 WHERE 費目 IN ('食費', '交際費', '光熱費')
逆に( )で比較指定した値のどれにも当てはまらないものを抽出したいときは、「NOT IN演算子」を使用する。
SELECT * FROM 家計簿 WHERE 費目 NOT IN ('食費', '交際費', '光熱費')
ANY / ALL演算子
比較演算子に、プラスしてさらに条件を絞って判定したい時に使用する。
ANY演算子を使うと、比較演算子の結果と比較してANYの()内の値とどれか1つでも当てはまる場合のみ、「真」を返す判定をしてくれる。
式 比較演算子 ANY (値1, 値2, 値3)
対して、ALL演算子は、比較演算子の結果と比較して、ALLの()内の値全てと当てはまる時のみ「真」を返す。
式 比較演算子 ALL (値1, 値2, 値3)
論理演算子
論理演算子を使うことで、複数の条件を組み合わせてより複雑な検索、判定を行うことができる。
中でも論理演算子の代表的なものは「AND演算子」と「OR演算子」がある。
AND演算子
2つの条件式の両方ともが真の場合のみ、判定が真となる(AかつB)
条件式1 AND 条件式2
OR演算子
対して、OR演算子は、2つの条件式のどちらかが真の場合に、真となる(AまたはB)
条件式1 OR 条件式2
・2つの条件式を組み合わせたWHERE句の例
◎買い物リストテーブルの販売店Bにある卵パックの価格を6000円に変更したい UPDATE 買い物リスト SET 価格 = 6000 WHERE 食品 = '卵パック' AND 販売店 = 'B'
NOT演算子
真偽値を逆転させる「その条件以外のもの」を抽出
NOT 条件式
○販売店B以外の行を取り出す WHERE NOT 販売店 = 'B'
論理演算子の優先順位
論理演算子で判定する時に、判定順番に優先順位が決められていて、①「NOT」、②「AND」、③「NOT」の順番で判定される。
なので例えば、「販売店AかBで売っているゲームかDVD」を抽出したい場合は、
SELECT * FROM 太郎の買い物リスト WHERE (販売店 = 'A' OR 販売店 = 'B')AND(カテゴリー = 'ゲーム' OR カテゴリー = 'DVD')
の様に()かっこをして先に判定して欲しいものを区切って意図的に優先順位を決めて記述する。