コメントの削除機能
destroyアクションの作成
コメントコントローラーにdestroyアクションを記載していきます。
def destroy
comment = Comment.find(params[:id])
comment.delete
redirect_to comment.board, flash: {notice: 'コメントが削除されました' }
end
コメント削除ボタンの作成
コントローラーでアクションを定義したら、viewを作成します。
link_toの第二引数に渡すパスを調べるために、
http://localhost:3000/rails/info/routesを確認しますと、
deleteアクションのパスはcomment_pathとなっています。
よって下記のようにリンクを作成します。
<span><%= link_to '削除', comment, method: :delete, data: { confirm: '削除してもよろしいでしょうか?'} %></span>
# 「link_to 'リンク名', リンク先へのURL又はパス, method: :メソッド名」で指定したメソッドの指定したパスに飛べる'リンク'を作成します。
# methodオプションを指定しない場合、第二引数のパスはGETメソッドのものを参照します。
# data-confirm属性を設定すると、リンクを踏んだ際に確認のダイアログボックスを表示させます。
編集機能にもエラーメッセージを表示する。
まず前提として、updateアクションには下記のとおりフィルタリングされています
before_action :set_target_board, only: %i[show edit update destroy]
# 中略
private
def set_target_board
@board = Board.find(params[:id])
end
これによって、@boardには該当のidのパラメータが渡されております。
その上でupdateアクションを下記のようにします。
def update
if @board.update(board_params)
flash[:notice] = "「#{@board.title}」の掲示板を編集しました。"
redirect_to @board
else
redirect_to edit_board_path, flash: {
board: @board,
error_messages: @board.errors.full_messages
}
end
end
これによって、パラメータが保存できた場合は編集したことを示すメッセージが表示されて、掲示板のshowにリダイレクトします。
バリデーションエラーでboardが保存できなかった場合は、編集画面にリダイレクトしてエラーメッセージのリストを表示します。
又この時、リダイレクト後も掲示板のフォームには編集時に入力したパラメータは保持されます。
多対多のリレーションの考え方
例えば掲示板にタグをつけることを考えます。
ここでは簡略化するため、各テーブルはidとnameのカラムのみを持ちます。
boardsテーブル
id | name |
---|---|
1 | 1日目 |
2 | 2日目 |
3 | 3日目 |
tagsテーブル
id | name |
---|---|
1 | ruby |
2 | SQL |
3 | 初心者 |
このとき、例えば
1日目はruby, 初心者
2日目はSQL
3日目はruby, SQL, 初心者
とタグ付けしたかったりした場合、この関係を表そうとすると下記のようになります。
id | board | tag | tag | tag |
---|---|---|---|---|
1 | 1日目 | ruby | 初心者 | |
2 | 2日目 | SQL | ||
3 | 3日目 | ruby | SQL | 初心者 |
でもこれではカラムの数が事前に定められない他、カラムに空白が生まれてしまいます。
そこで**「中間テーブル」**を用いて、各id同士を繋げて表します。
board_id | tag_id |
---|---|
1 | 1 |
1 | 3 |
2 | 2 |
3 | 1 |
3 | 2 |
3 | 3 |
このように表現することで、例えば
board_id = 1 である「1日目」には、
tag_id = 1 である「ruby」と
tag_id = 2 である「初心者」がタグづけされていることがわかります。
では以下でこのアソシエーションをどのように実装するかを見ていきます。
多対多アソシエーションの実装許容しないようにマイグレーションファイルを書き換える。
まず最初に中間テーブルとtagsテーブルのモデルを作成します。
テーブルの作成
tagsテーブル
docker-compose exec web bundle exec rails g model tag name:string
中間テーブル
docker-compose exec web bundle exec rails g model board_tag_relation board:references tag:references
tagがnullであることを許可しない
t.string :name, null: false
書き換えたら、マイグレーションを実行します。
docker-compose exec web bundle exec rake db:migrate
モデルのアソシエーションの設定
####中間モデルの確認
中間モデルは最初からboardモデルとtagモデルにbelongs_toによってassociateされています。
これによって中間モデルはboard_idとtag_idをカラムに持つことになり、上記で示したような2つのidを結びつけることが可能になります。
class BoardTagRelation < ApplicationRecord
belongs_to :board
belongs_to :tag
end
tagモデルの修正
タグから掲示板を関連づける設定をする。
class Tag < ApplicationRecord
has_many :board_tag_relations
has_many :boards, through: :board_tag_relations # throughオプションによって中間テーブルを経由して複数のboardを持つことを示している。
end
boardsモデルの修正
掲示板からタグを関連づける設定をする。
class Board < ApplicationRecord
has_many :comments
has_many :tag_board_relations
has_many :tags, through: :board_tag_relations
validates :name, presence: true, length: { maximum: 10 }
validates :title, presence: true, length: { maximum: 30 }
validates :body, presence: true, length: { maximum: 1000 }
end
次回はアソシエーションのdependentオプションを作成していきます。
アソシエーションのdependentオプション
今のhas_manyの関係では、掲示板やタグを削除しても、掲示板とタグとの関係性や、掲示板についたコメントの情報は残ってしまう。
本来であれば掲示板やタグが削除されたら、そのコメントやタグ(掲示板)との関係性は削除されるべきであるから、これを実装する。
boardモデルの修正
class Board < ApplicationRecord
has_many :comments
has_many :tag_board_relations, dependent: :delete_all
# dependentオプションはdestroyメソッドによってデータを削除した場合に有効になる。deleteメソッドでは無効。
# delete_allとした場合、boardモデルのデータが削除された場合、コメントやtagとの関連付けが全て削除される。
# destroyオプションを使った場合、関連するモデルのオブジェクト一つ一つにdestroyメソッドを使用することになる。
# destroyオプションを使用すれば、関連の先の関連を削除する、依存するデータ全てを削除するなどの複雑なことができる。
# destroyの方が1つ1つやるので動作が重く、delete_allはまとめて削除するのでパフォーマンスが良い。
has_many :tag, through: :tag_board_relations
tagモデルの修正
class Tag < ApplicationRecord
has_many :board_tag_relations, dependent: :delete_all
has_many :boards, through: :board_tag_relations
end
削除機能のdestroyメソッドへの変更
def destroy
@board.destroy # deleteメソッドから変更。
redirect_to boards_path, flash: { notice: "「#{@board.title}の掲示板が削除されました。」"}
end