ログイン機能の実装(モデルのアソシエーションまで)③
ログイン機能の実装②まではログイン情報を取得するまで進めた。このままではログインしたままになってしまうので、ここからはログアウト機能の実装をしていきたいと思う。
ログアウト機能の実装
ユーザーがログイン状態ではsession[:user_id]にユーザーのIDが入っていることだった。ログアウトの状態にするにはsession[:user_id]にnilが入っている状態に変えればいいということになる。
session.delete(:user_id)
セッションからuser_idの情報だけピンポイントで消すには上記のようにできる。
セッション内のすべてのデータを削除したい場合には下記のようにする。
reset_session
アクションの設定
ログアウト機能を実装するためにsessionsコントローラーのdestroyアクションを編集していく。
def destroy reset_session redirect_to root_url, notice: 'ログアウトしました' end
これでdestroyアクションはできた。
続いてログアウトのリンクを追加する。
ul.navbar-nav.ml-auto - if current_user li.nav-item= link_to 'タスク一覧', tasks_path, class: 'nav-link' li.nav-item= link_to 'ユーザー一覧', admin_users_path, class: 'nav-link' li.nav-item= link_to 'ログアウト', logout_path, method: :delete, class: 'nav-link' - else li.nav-item= link_to 'ログイン', login_path, class: 'nav-link'
ログインしているときはタスク一覧、ユーザー一覧、ログアウトの表示が見え、ログインしていないときはログインできるようにログインの表示がされるようになっています。
ログインしていない場合の制限
制限をかけていない今のままではログインしていなくても他の機能が使えたりしてあまり意味がないのでログインしていなければタスク管理を利用できなくするという制限をかけて行きたいと思う。
そのためには「フィルター」という機能を使う。
フィルターとは一般的な処理の流れに従ってメインの処理をアクションと考えると、フィルターとはすべてのアクションに対して、前処理・後処理を設定する役割を提供するものと言える。
つまり、前処理・後処理を必要とする場合だけ、フィルターを設定する。
before_actionでアクションの前処理として他の機能へのリダイレクトを実装するとリダイレクトが行われ、アクションには到達しない。
class ApplicationController < ActionController::Base helper_method :current_user before_action :login_required ....ruby def login_required redirect_to login_url unless current_user end
before_action :login_requiredでアクションを実行する前にlogin_requiredが呼び出される。login_requiredではログインしていなければログイン画面にリダイレクトするという機能を実装しているので、もしログインしていなければアクションは実行されない。
1つ問題があり、アプリケーションのどのURLをリクエストしてもリダイレクトが行われてしまう。
これを防ぐために特定のコントローラーにおいて親クラスなどですでに定義済みのフィルタを通らないようにするにはskip_before_actionを利用する。
sessions_controller.rbを編集し、skip_before_actionを利用する。
class SessionsController < ApplicationController skip_before_action :login_required ...
これでログインしていないユーザーに制限をかけることができた。
ログインしているユーザーのデータだけを扱えるようにする
ログインしているユーザーのデータだけを扱えるようにするには特定のユーザーに紐付いたTaskデータだけを扱うようにプログラムを変更しなければいけいない。
そのためには
- UserとTaskを紐付ける。具体的にはtasksテーブルにuser_idというカラムを追加してタスクを所有しているユーザーのidが格納されるようにする。
- UserとTaskの紐付けを簡単に扱えるよう、Railsの「関連」を定義する
- ログインしているユーザーに紐付いたTaskデータを登録できるようにする。
- 一覧、詳細、変更など既存のレコードを扱う機能ではログインしているユーザーに紐付くデータだけを扱うようにする。
とくことが必要である。
UserとTaskを紐付ける
1つのユーザーに対して複数のTaskが存在する1対多の関係になるので「多」にあたるTaskにuser_idを持たせる。
$ rails g migration AddUserIdToTasks
上記のコマンドでマイグレーションファイルを作成する。
class AddUserIdToTasks < ActiveRecord::Migration[5.2] def change def up execute 'DELETE FROM tasks;' add_reference :tasks, :user, null: false, index: true end def down remove_reference :tasks, :user, index: true end end end
execute 'DELETE FROM tasks;'
はSQL文で今まで作られたタスクをすべて消すというもの。
既存のタスクがある状態でタスクとユーザーの関係を表すカラム(user_id)を追加すると、既存のタスクに紐付くユーザーを決められず、NOT NULL制約に引っかかってしまいます。そのため既存のタスクをすべて削除してから、カラム追加を行うようにしている。
ということなので、これをマイグレーション実行する。
これでTaskとUserが紐付く。
Railsで「関連」という仕組みを利用するには、モデルクラス同時の紐付けを定義する。
UserとTaskは1対多の「関係」にあたるので、Userクラスにはhas_many :tasks、Taskクラスにはbelongs_to :userを定義する。
ここは一番理解しにくかったが、1対多の関係において、1のモデルクラスにはhas_many、多のモデルクラスにはbelongs_toを定義する。
日本語にしたらわかりやすいが、has_manyはいくつかのモデルを持っている(なのでモデルクラスの複数形)、belongs_toは1つのモデルに所属している(なのでモデルクラスの単数形)というふうに定義する。
このような定義をすることでUserクラスのインスタンスはuser.tasksといったメソッドで紐付いたTaskオブジェクトの一覧を得られるようになる。また、Taskクラスのインスタンスはtask.userといったメソッドで紐付いたUserオブジェクトを得られるようになる。
ログインしているユーザーのTaskデータの登録
現在の登録アクション(tasks_controller.rb) のcreateアクションでは@task = Task.new(task_params)
となっている。
これを@task = current_user.tasks.new(task_params)
とすることでログインしているユーザーのuser_idを入れた状態でTaskデータを登録することができる。
まとめ
- session[:user_id]からログアウトの状態にするにはnilが入っている状態に変えればいいのでピンポイントでuser_idの情報を消すには
session.delete(:user_id)
とする。またセッション内のすべてのデータを削除するのはreset_session
とする。 - フィルターとはすべてのアクションに対して、前処理・後処理を設定する役割を提供するもので、 他の機能へのリダイレクトを実装するとリダイレクトが行われ、アクションには到達しない。
- 親クラスなどですでに定義済みのフィルタを通らないようにするにはskip_before_actionを利用する。
- 1対多の関係において、1のモデルクラスにはhas_many、多のモデルクラスにはbelongs_toを定義する。
モデル同士の「関連(アソシエーション)」についてはもっと理解を深めていかないといけないと感じた。