さらなる高みへ -higher ground -

プログラミング学習の定着を狙いとしたアウトプット(独り言)をしてみるブログ

ツイートの削除機能をつける

ここではツイートの削除機能を追加する。
データの削除はRailsの基本機能の1つであるため、使いこなせるようにしたい。

ツイートの削除機能を追加する

現在の実装では一度投稿したツイートは削除することができない。
そこで、ツイートに削除ボタンを追加して、ツイートの削除を行えるようにする。

ツイートを削除する流れは、下記の図のような流れで実行される。

delete.png

作業手順はルーティングを設定することでビューに削除のパスを指定できるため、ルーティングの編集から始まる。

作業内容

1.ツイートを削除するためのルーティングを設定する

2.削除ボタンをビューに追加する

3.destroyアクションをコントローラに定義する

4.削除後のビューを追加する

1.ツイートを削除するためのルーティングを設定する

ツイート削除機能を実装するためにまずはルーティングを定義していく。
ツイートを削除する際にはツイートごとに固有のパスが発行されるので、ユーザーのマイページを作成したときと同様に少し特別なルーティングが必要になる。

また、ツイートの削除を行う際にはdeleteというHTTPメソッドを利用する。

deleteメソッド

HTTPリクエストにはいくつかの種類があり、これまでにgetメソッドとpostメソッドを学習した。

deleteメソッドは情報の削除を行う際に利用するHTTPメソッドである。
今回の場合、ツイートの削除を行うのでdeleteメソッドを利用する。
また、ツイートの削除を行う際には、tweetsコントローラのdestroyアクションを動かす。

config/routes.rbを以下のように編集する

config/routes.rb

Rails.application.routes.draw do
  devise_for :users
  root  'tweets#index'
  get   'tweets'      =>  'tweets#index'
  get   'tweets/new'  =>  'tweets#new'
  post  'tweets'      =>  'tweets#create'
  delete  'tweets/:id'  => 'tweets#destroy'
  get   'users/:id'  =>  'users#show'
end

リクエストする箇所(ビュー)では、パスの:idの部分に削除するツイートのidが入るように記述する。
すると、パラメーターがコントローラに渡る際paramsのキーにidが追加され、そのバリューはリクエストの際に:idの部分に記述された値になる。

つまり、:idの部分に記述された値はコントローラ上でparams[:id]とすることで取得できる。

2.削除ボタンをビューに追加する

ツイートを削除するためにビューファイルを編集して削除ボタンを追加する。
今回は、link_toメソッドを使用する。

削除ボタンはツイートの表示画面の右上に配置される。
また、自分が投稿したツイートにのみ表示されるようにする。

link_toメソッドを実装する際にはrake routesコマンドを使用して、パスを確認する必要がある。
必ずしもrake routesで確認する必要性はなく、指定するパスを忘れてしまった場合にコマンドを実行する。

rake routes

このコマンドは現在定義しているルーティングを表示してくれるコマンドである。
ターミナルで入力することで、config/routes.rbに記述したルーティングを表示してくれる。

【例】

ターミナル

  $ rake routes

出力結果

                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
                    root GET    /                              tweets#index
                  tweets GET    /tweets(.:format)              tweets#index
                         POST   /tweets(.:format)              tweets#create
               new_tweet GET    /tweets/new(.:format)          tweets#new
                   tweet DELETE /tweets/:id(.:format)          tweets#destroy
                    user GET    /users/:id(.:format)           users#show

 

rake routesコマンドを実行すると例えば上記のような出力結果が表示される。(完全に同様の出力結果でなくてもOK)

この出力結果は以下のような意味を持っている。

項目名 意味
Prefix 「Prefix」はルーティングの名前を表しています。「Prefix」は設定を行わなくても、自動的に割り当てられますが、自ら設定することもできます。
Verb 「Verb」はHTTPメソッドを表しています。右側に表示されているパスにどのHTTPメソッドでアクセスするかを示しています。
URI Pattern URI Pattern」はルーティングのパスを表しています。このパスにアクセスされた際に、指定のコントローラとアクションで処理が行われます。
Controller#Action 「Controller#Action」は指定したパスにアクセスした際に処理が行われるコントローラとアクションを表しています。#以前がコントローラ名、#以後がアクション名を示しています。

また、/tweets(.:format) の後ろについている(.:format)とは、formatオプションとよばれるもので、html形式やjson形式といった複数の形式で出力することができるというものである。
このカリキュラムでは、すべてhtml形式を使用しますので、現時点ではいろいろな形式があるのだな、程度の認識で問題ない。

問題1:削除ボタンを作成しましょう

作業ファイル:app/views/tweets/index.html.erb
ヒント1:下記の例をもとに、link_toメソッドを利用しましょう。
ヒント2:rake routesで確認した、tweetsコントローラのdestroyメソッドを動かすhttpメソッド/パスの組み合わせを書きましょう
ヒント3:Rubyタグの中の文字列内で変数を使いたい場合は式展開#{}を使用します。
ヒント4:削除したいツイートを投稿したユーザーのidは変数`tweet`を使用して取得します。
【例】
index.html.erb
    //省略
  <div class="content_post" style="background-image: url(<%= tweet.image %>);">
    <% if ユーザーがログインしているか? かつ current_user.id == 削除したいツイートを投稿したユーザーのid %>
      <div class="more">
        <span><%= image_tag 'arrow_top.png' %></span>
        <ul class="more_list">
          <li>
            <%= link_to '削除', "/tweets/削除したいツイートのid", method: :HTTPメソッドを指定 %>
         </li>
        </ul>
      </div>
    <% end %>
    <%= simple_format(tweet.text) %>
    <span class="name">
      <a href="/users/<%= tweet.user_id %>">
        <span>投稿者</span><%= tweet.user.nickname %>
      </a>
    </span>
  </div>
 //省略

 【 問題1の解答を確認する 】 

3.destroyアクションをコントローラに定義する

ツイートを削除する際にはtweetsコントローラのdestroyアクションを動かす。
そこで、このアクションをコントローラに定義していく。

destroyメソッド

ActiveRecordメソッドのうちの一つで、インスタンスに対してそのレコードを削除する際に使うことができる。
ターミナルで実行して確認してみる。
以下の指示に従い、ターミナルからコマンドを実行する。

ターミナル

$ rails c
[1] pry(main)> tweet = Tweet.find(tweetsテーブルを見て保存されているidを入力してください)
=> #<Tweet:0x007ff621357720 id: 1, name: "george", text: "いい景色だ", image: "http://photo1.jpg", created_at: Thu, 16 Apr 2015 05:41:20 UTC +00:00, updated_at: Thu, 16 Apr 2015 05:41:20 UTC +00:00, user_id: 1>]
[2] pry(main)> tweet.destroy

すると、以下のような結果になり、レコードが削除される。

ターミナル

   (0.2ms)  BEGIN
  SQL (1.6ms)  DELETE FROM `tweets` WHERE `tweets`.`id` = 1
   (0.3ms)  COMMIT
=> #<Tweet:0x007ff621357720 id: 1, name: "george", text: "いい景色だ", image: "http://photo1.jpg", created_at: Thu, 16 Apr 2015 05:41:20 UTC +00:00, updated_at: Thu, 16 Apr 2015 05:41:20 UTC +00:00, user_id: 1>]

tweets_controllerにdestroyアクションを実装していく。
ツイートを削除する際にはdestroyメソッドを使う。
また、どのツイートを削除するのかを特定する場合はparams[:id]を使用して、削除したいツイートの情報を取得する。
削除を行えるのは、そのツイートを投稿したユーザーのみである。
そのため、別のユーザーが削除をできないように条件をつける必要がある。

app/controllers/tweets_controller.rbを以下のように編集する

app/controllers/tweets_controller.rb

class TweetsController < ApplicationController

  before_action :move_to_index, except: :index

  def index
    @tweets = Tweet.includes(:user).page(params[:page]).per(5).order("created_at DESC")
  end

  def new
  end

  def create
    Tweet.create(image: tweet_params[:image], text: tweet_params[:text], user_id: current_user.id)
  end

  def destroy
    tweet = Tweet.find(params[:id])
    if tweet.user_id == current_user.id
      tweet.destroy
    end
  end

  private
  def tweet_params
    params.permit(:image, :text)
  end

  def move_to_index
    redirect_to action: :index unless user_signed_in?
  end
end
後置if

最後のendを省略してif文を処理の後方に配置する書き方である。

elsif, elseにあたる条件分岐が無く、かつ処理が一行で完結する場合に用いる。
コード量も少なくシンプルに記述できるので、今回のdestroyメソッドは以下のように書き直す。

app/controllers/tweets_controller.rb

  def destroy
    tweet = Tweet.find(params[:id])
    tweet.destroy if tweet.user_id == current_user.id
  end

4.削除後のビューを追加する

ルーティングとコントローラの編集が終わったため、続いてビューファイルの追加を行う。
このビューファイルはツイートの削除を行ったあとに呼び出されるビューファイルになる。

以下の指示に従い、app/views/tweets/destroy.html.erbを作成する

①ファイルの新規作成

app/views/tweetsディレクトリの中にdestroy.html.erbというファイルを作成する。

②ファイルの編集

作成したファイルを、以下のように編集する。

app/views/tweets/destroy.html.erb

<div class="contents row">
  <div class="success">
    <h3>
      削除が完了しました。
    </h3>
    <a class="btn" href="/">投稿一覧へ戻る</a>
  </div>
</div>

このソースコードは、打ち間違いを防ぐためにコピー&ペーストでOK

トップページを開き、削除機能を試してみる

① ログインする

② ログインしたユーザーでツイートの投稿する。一番上に表示されます。

https://tech-master.s3.amazonaws.com/uploads/curriculums//423f87cfd44c04ab84589d1516c1cf04.png

③ 削除ボタンをクリックする。

https://tech-master.s3.amazonaws.com/uploads/curriculums//1fedbbd46f7e2865cb49707d4e5d159d.png

https://tech-master.s3.amazonaws.com/uploads/curriculums//b8541b45e77d2d10d9d36a53fc71cdac.png

④ 削除完了画面が表示される。

https://tech-master.s3.amazonaws.com/uploads/curriculums//886227e7efd1a474543c369b4011fb65.png

⑤ トップページに戻り、②のツイートがなくなっていれば、削除機能は実装完了。

https://tech-master.s3.amazonaws.com/uploads/curriculums//b320831b79cf5f87ba79c712b93ac26a.png

要点チェック