Edited at

Progate rails道場コースⅢ 攻略メモ

ProgateのRails道場コースをやっていて、個人的に復習が必要だなーって感じたところをまとめていきます。


3/14 初期画像を設定しよう


新規登録時に初期画像を表示

コントローラー

def create

@user = User.new(
name: params[:name],
email: params[:email],
#画像用のカラム名: 値
image_name: "default_user.jpg")
...........

ビュー

 <img src="<%= "/user_images/#{@user.image_name}" %>">

<h2><%= @user.name %></h2>
<p><%= @user.email %></p>

imgタグを使って画像を表示する。

画像名は変数展開を使って@userのimage_nameから取得。


4/14 画像編集機能を作ろう

コントローラーのコード解説

if params[:image]

@user.image_name = "#{@user.id}.jpg"
image = params[:image]
File.binwrite("public/user_images/#{@user.id}.jpg", image.read)
end

1行目

if params[:image]

if文を使って、画像が送信されているかを判定。

ユーザー編集画面のname="image"を与えたimgタグからファイルが送信されている場合のみ実行されます。

2行目

  @user.image_name = "#{@user.id}.jpg"

この時点ではまだ画像ファイルを読み取ってません。

@userのimage_name属性に、1.jpgといった形でuserのIDを付与したファイル名を与えてるだけ。

後に出てくる@user.saveでDBに保存されます。

3・4行目

ここからが実際に画像ファイルを読み取るコードです。

image = params[:image]

File.binwrite("public/user_images/#{@user.id}.jpg", image.read)

image変数に、編集ページのimgタグから送られてきた画像の情報を入れた後、次の行でFileクラスを使って画像を保存してます。

File.binwriteの引数にファイルの場所、名前を指定した後、前の行の変数imageに対してreadメソッドを使うことで画像データを取得で保存完了。


5/14 パスワードカラムを追加しよう

コンソールからデータを追加する方法を忘れてたので再確認。

[1] pry(main)> user = User.find_by(id: 1)

[2] pry(main)> user.password = "password"
=> "password"
[3] pry(main)> user.save

[1]DB上のユーザー情報をfind_byで取得し、適当な変数へ入れる。

[2]データを更新

[3]データを保存


7/14 ログイン処理を追加しよう

ビュー、ルーティングは省略

コントローラー解説

  def login

@user = User.find_by(email: params[:email],
password: params[:password])
if @user
session[:user_id] = @user.id
flash[:notice] = "ログインしました"
redirect_to("/posts/index")
else
render("users/login_form")
end
end

各コード解説

    @user = User.find_by(email: params[:email],

password: params[:password])

ログインフォームからemail、password属性で送られてきたデータを受け取り、find_byメソッドを使ってDBから探してくるというコード。

もし一致したデータがあればインスタンス変数@userに格納します。

if @user 

session[:user_id] = @user.id
flash[:notice] = "ログインしました"
redirect_to("/posts/index")
else
render("users/login_form")
end

@userにデータがある場合、sessionという変数に@userのidを代入します。

これによりログインユーザーの情報が保持され続けます。


8/14 ユーザーが存在しない場合の処理

loginアクション内の、ユーザーを特定できなかった場合の処理(elseの場合)をいじっていきます。

コントローラー

if @user 

session[:user_id] = @user.id
flash[:notice] = "ログインしました"
redirect_to("/posts/index")
else
@error_message = "メールアドレスまたはパスワードが間違っています"
@email = params[:email]
@password = params[:password]
render("users/login_form")
end

else以下のコードが対象です。

ログインに失敗した場合、まずは@error_messageというインスタンス変数を作成し、エラーメッセージを入れてやります。

その後ビュー側で、@error_messageが存在する場合(ログインに失敗し、上記のelse以降の処理が行われた時)のみ、それを表示するコードを書きます。


<% if @error_message %>
<div class="form-error">
<%= @error_message %>
</div>
<% end %>

後はrenderでログインフォームに返した際、一度書いたアドレスとパスワードを保持するためのコードを書いていきます。

@email = params[:email]

@password = params[:password]

アクション内でparamsを使って、ログイン失敗時に入力された値を受け取ります。

ビュー側でのinputタグのname属性と合わせてやりましょう。

<p>メールアドレス</p>

<input name="email" value=" <%= @email %> ">
<p>パスワード</p>
<input type="password" name="password" value=" <%= @password %> ">

inputタグのvalue属性がテキストフィールドの初期値になります。

そこに先程paramsで受け取った変数を入れてやれば完成です。


ログアウト機能を作ろう


ヘッダーにログアウトボタンを実装

sessionに値がある=ログイン状態

sessionに値がない=非ログイン状態

であるため、押すとsessionの値を空にするlogoutボタンを実装していきましょう。

まずはビュー側にボタンを作ります。

 <%= link_to("ログアウト", "/logout", {method: :post}) %>

{method: :post}を指定するのを忘れずに。これがないとgetメソッドでリクエストが送信されます。

送信先URLはProgateの指定通り/logoutで。これに合わせてルーティングも作成。

post "logout" => "users#logout"

ルーティングが出来たので、logoutアクションを作りましょう。

def logout

session[:user_id] = nil
flash[:notice] = "ログアウトしました"
redirect_to("/login")
end

user_idを格納していた変数sessionにnilを代入することで、ログアウト状態になります。


ログイン・非ログイン状態で表示するボタンを変更する

if sessionに値が入っているか

 ログイン時のメニュー
else
 非ログイン時のメニュー
end

といった風に、ビューにif文を埋め込んでいきます。

erbで書くとこんな感じ。

<% if session[:user_id] %>

<%= link_to("ログアウト", "/logout", {method: :post}) %>
<% else %>
<%= link_to("ログイン", "/login") %>
<% end %>


11/14 ヘッダーにユーザー名を表示しよう

ヘッダーにログイン中のユーザー名を表示し、クリックするとそのユーザーの詳細ページにアクセスする機能を実装します。

まずはユーザー名を取得し、ビューで表示するためのインスタンス変数を作ります。

この機能は全てのビューで表示するため、application_controllerにアクションを記述し共通化しましょう。これでいちいち全てのアクションで変数を作る必要が無くなります!

application_controller.rb

class ApplicationController < ActionController::Base

before_action :set_current_user
def set_current_user
@current_user = User.find_by(id: session[:user_id])
end
end

application_controller内で定義したメソッドをbefore_actionで指定することで、全てのアクションを呼び出す前にそのメソッドが実行されるようになります。

上記の場合はログイン中のユーザーデータをsession_idを元に特定し、@current_userという変数に代入してます。

これでどの画面でも@current_user.nameなどでログイン中のユーザーデータを取得することができるようになりました!

この機能を使って、ユーザー名を表示し、押すと詳細画面へ飛ぶボタンを実装していきます。

<%= link_to("#{@current_user.name}", "/users/#{@current_user.id}") %>

変数展開で埋め込んで完成。


12/14 ログインしていない場合のアクセス制限

ログインしていない場合は、特定の機能を使えないようにします。

ログイン状態かどうかを確認し、そうでない場合はログインフォームへリダイレクトする、といったアクションを作っていきましょう。

こちらも多数のページで共通して使う処理なので、application_controllerに書いていきます。

def authenticate_user

if @current_user == nil
flash[:notice] = "ログインが必要です"
redirect_to("/login")
end
end

1行目で前回定義した@current_user(ユーザーがログイン済みかどうかを調べるメソッド)を使い、中身がnilだった場合はリダイレクト処理、というコードです。

後は前回のようにbefore_actionでアクション前にこのメソッドが実行されるように記述するだけなのですが、usersコントローラではindex, show, edit, updateアクションのみ実行という指定があります。

なので、before_actionでonlyというオプションを使って、指定したアクションでのみ実行されるようにしていきましょう。

before_action :authenticate_user, {only: [:index, :show, :edit, :update]}

このように書いてやればオッケーです!


13/14 ログインしている場合のアクセス制限

前回の逆です。

指定のURLが多くて混乱しそうですが、ルーティングを見ながら、どのURLでどのアクションが実行されるか一つ一つ確認していけば大丈夫です。

例えば、get /signupだと、ルーティングは

get "signup" => "users#new"

となっているので、usersコントローラのnewアクションを指定してあげれば大丈夫です。


14/14 自分の情報のみ編集できるようにしよう

ラストです!

まずはユーザー詳細ページの編集リンクを、ログイン中の本人のみ表示されるようにしていきます!

show.html.erb


<% if @user.id == @current_user.id %>
<%= link_to("編集", "/users/#{@user.id}/edit") %>
<% end %>

if文を使い、表示中のユーザーページのID(@user.id)と、ログイン中のユーザーのID(@current_user.id)が一致するかどうかを判断します。

変数@userは"users/:id"の:idの部分(ユーザーID)が入ってます。各ユーザーの詳細ページのURLですね。

これと現在ログイン中のユーザーのsessionが入っている@current_userが一致することで、詳細ページを見ているのが本人かどうか判断します。

次はコントローラです。

def ensure_correct_user

if params[:id].to_i != @current_user.id
flash[:notice] = "権限がありません"
redirect_to("/posts/index")
end
end

params[:id].to_i != @current_user.id

params[:id].to_iでユーザー詳細ページのIDを取得し、to_iメソッドで文字列を数値に変換した後、ログイン中のユーザーと一致しないかどうかを!=を使って調べます、

一致しない場合、フラッシュメッセージを表示して、リダイレクトして終了です!

これで道場コースは残り一つです!ラストも頑張っていきましょう!!