1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【50日目】コメントの削除機能、多対多のアソシエーションの設定

Last updated at Posted at 2019-10-09

コメントの削除機能

destroyアクションの作成

コメントコントローラーにdestroyアクションを記載していきます。

comments_controller.rb
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となっています。
よって下記のようにリンクを作成します。

_comments.html.erb
<span><%= link_to '削除', comment, method: :delete, data: { confirm: '削除してもよろしいでしょうか?'} %></span>
# 「link_to 'リンク名', リンク先へのURL又はパス, method: :メソッド名」で指定したメソッドの指定したパスに飛べる'リンク'を作成します。
# methodオプションを指定しない場合、第二引数のパスはGETメソッドのものを参照します。
# data-confirm属性を設定すると、リンクを踏んだ際に確認のダイアログボックスを表示させます。

編集機能にもエラーメッセージを表示する。

まず前提として、updateアクションには下記のとおりフィルタリングされています

boards_controller.rb
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アクションを下記のようにします。

boards_controller.rb
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であることを許可しない

db/migrate/xxxxxx_create_tags.rb
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を結びつけることが可能になります。

app/model/board_tag_relations.rb
class BoardTagRelation < ApplicationRecord
  belongs_to :board
  belongs_to :tag
end

tagモデルの修正

タグから掲示板を関連づける設定をする。

tag.rb
class Tag < ApplicationRecord
  has_many :board_tag_relations
  has_many :boards, through: :board_tag_relations # throughオプションによって中間テーブルを経由して複数のboardを持つことを示している。
end

boardsモデルの修正

掲示板からタグを関連づける設定をする。

board.rb
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モデルの修正

board.rb
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モデルの修正

tag.rb
class Tag < ApplicationRecord
  has_many :board_tag_relations, dependent: :delete_all
  has_many :boards, through: :board_tag_relations
end

削除機能のdestroyメソッドへの変更

board_controller.rb
def destroy
  @board.destroy # deleteメソッドから変更。
  redirect_to boards_path, flash: { notice: "「#{@board.title}の掲示板が削除されました。」"}
end
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?