Rails ブックマークの実装part1
実装内容
- 掲示板の☆ボタンを押すと、その掲示板をブックマーク/解除出来る機能を作成。
- ブックマークすると★解除すると☆になるようにし、フラッシュメッセージも表示させる。
- 同じユーザが同じ掲示板を複数回ブックマークできないようにする。
- ブックマークの一覧ページを作成する。
実装の流れ
- 中間モデルの定義
- ルーティングを作成
- bookmarkコントローラの実装
- viewの実装
中間モデルの定義
userモデルとboardモデルの間にbookmarkモデルを作成する。
userモデル
id | user |
---|---|
1 | meo |
bookmarkモデル
←中間モデル
id | user | board |
---|---|---|
1 | meo | abcd |
boardモデル
id | board |
---|---|
1 | abcd |
user board bookmarkの関係
userが掲示板をブックマークするとその数だけ掲示板はブックマークを持っている。
つまり、userとboardはbookmarkを持っているので多対多
の関係であると言える。
中間モデルについて
多対多
である中間モデル(bookmark)には、外部キー制約(foreign_key)を付ける必要がある。
どのuserがどのboardをブックマークしたかuser_id
,board_id
が保存される。
bookmarkモデル
id | user | board |
---|---|---|
1 | user_id | board_id |
bookmarkモデルを作成
外部キーをつけたいのでreference型を使い作成する。
紐づくモデル名+「_id」で外部キーと呼ぶ。
今回はuser,boardと紐付けるので以下のコマンドを実行する。
$ rails g model Bookmark user:references board:references Running via Spring preloader in process 5265 invoke active_record create db/migrate/20210603064703_create_bookmarks.rb create app/models/bookmark.rb
同じユーザが同じ掲示板を重複してブックマークできないようにしたいので、
作成されたマイグレーションファイルへ以下の記述を追加する。
t.index [:user_id, :board_id], unique: true
unique:true
でテーブル内で重複するデータを禁止する一意性制約
をつける。
マイグレーションを実行。
$ rails db:migrate == 20210603064703 CreateBookmarks: migrating ================================== -- create_table(:bookmarks)
各モデルのアソシエーション
belongs_to :user belongs_to :board validates :user_id, uniqueness: { scope: :board_id }
データベース側に一意性制約
をつけたが、モデル側にも同様に記述する。
uniqueness
でオブジェクトが保存される前に属性の値が重複してないことを検証する。
has_many :boards, dependent: :destroy has_many :bookmarks, dependent: :destroy has_many :bookmark_boards, through: :bookmarks, source: :board
親(user)を削除したら、一緒に子(booksmark)も削除されるように
dependent: :destroy
を追加。
has_many :bookmark_boards, through: :bookmarks, source: :board
について
boards
は既にあるのでbookmark_boards
を定義し、ユーザがブックマークしている掲示板を取得する。
through: :bookmarks, source: :board
でbookmarksを通してboardモデルから掲示板を取得している。
has_many :bookmarks, dependent: :destroy belongs_to :user同じく、親(board)を削除したら、一緒に子(bookmarks)も削除されるように
dependent: :destroy
を追加。
userモデルにbookmarkしたか判定するメソッドを追加
コントローラにも記述できるが、モデルに書くのが一般的。
#ブックマーク追加 def bookmark(board) bookmark_boards << board end
<<
で引数で渡したboardレコードが先程has_many through
で定義したbookmark_boards
に保存される。
#ブックマーク外す def unbookmark(board) bookmark_boards.destroy(board) end
bookmark_boards
から引数のboardレコードを削除する。
#ブックマークしてるか判定するメソッド def bookmark?(board) bookmark_boards.include?(board) end
includes?
メソッドで引数のboardレコードが含まれてたらtrue、なければfalseを返す。part2へ