[Ruby on Rails5]Progate 学習メモ3


引き続きメモ。とっ散らかっている内容は後でまとめます。

▼DBに追加する場合

route.rbの記載は、
post "コントロール名/:id/アクション編集" => "コントロール名#アクション編集"

xxx_contoroller.rbの記載は、
def アクション編集
@変数 = テーブル名.find_by(id: params[:id])
@変数.編集するカラム名 = テーブル名.new(編集するカラム名: params[:編集するカラム名])
@変数.save
redirect_to("コントロール名/アクション一覧")
end

xxx.html.erbの記載は、
<%= form_tag("コントロール名/#{@変数.id}/アクション編集") do %>
<textarea name="カラム名"><%= @変数.content %></textarea>
<input name="カラム" value="初期値">
<input type="submit" value="ボタン表示名">
<% end %>

※inputタグの初期値が必要な場合はvalueに記述する。ボタンの場合のvalueは表示名になる。

inputタグの初期値にrubyを組み込むことも可能。
<input name="カラム" value="<%= @変数.カラム名 %>">

上記のようにすることで、テーブルから値を呼び出せる

▼テーブルにカラムを追加する
①マイグレーションファイルの作成
モデルとマイグレーションを作成(すべて揃える場合)
rails g model コントロール image_names:string

マイグレーションのみを作成
rails g migration ファイル名

db/migration配下にファイルが生成される。
中にはchengeメソッドで記載されており、これが「rails db:migrate」で反映される内容である。
ファイル名は、「日付時間_ファイル名 to テーブル名.rb」

通常であれば、自動生成されている内容のままでよいので、そのままコマンド実行することが多い。

カラムを追加する場合は下記のようにする。

日付_追加カラム.rbの記載は、
add_column :テーブル名, :カラム名, :データ型

②マイグレーションファイルの反映
rails db:migrate

▼DB追加時に空白箇所に初期値を設定する(プロフ画像など)
xxx_contoroller.rbの記載は、
def アクション名
@変数 = テーブル.new(
カラム: params[:カラム],
カラム: "画像ファイル名"
)
if @変数.save
...
end

▼ビューにrubyで画像を表示させる
xxx.html.erbに下記を記載する。

<img src="<%= "/.../... " %>">

public/user_imagesフォルダに保存されている想定だと、下記のようになる
<img src="<%= "/user_images/#{変数.画像URLカラム}" %>">

※[変数.画像URLカラム]がファイル名に該当する。画像URLカラムを指定することでファイル名が代入される。

▼ビューに画像ファイル選択ボタンを表示させて、保存する
xxx.html.erbに下記を記載する。

<input name="image"type="file">

画像の送信は特殊なので、form_tagに{multipart: true}を追加する必要があります。

<%= form_tag("...", {multipart: true}) do %>
...
<% end %>

アクションからDBにファイル名、/public/user_images配下に画像を保存します。

▼Rubyのコードでファイルを作成する
File.writeの場合は、デフォルトでpublicを指定しています。

File.write("public/ファイル名", "ファイルの中身")

sample.txtに「Hello world」と記載のあるテキストファイルを作成するには、下記のような感じになります。
File.write("public/sample.txt", "Hello world")

画像を保存するには、File.writeではなくFile.binwriteを用います。

File.binwrite("public/ファイル名", image.read)

テーブルから画像ファイルを呼び出す場合は、下記のように指定します。

File.binwrite("public/フォルダ名/#{@変数.カラム名}", image.read)

▼ファイル名をデータベースに保存する
実装するには、xxx.controller.rbに記載します。
下記の例は、id.拡張子という形式で保存します。

def アクション名
@変数 = テーブル名.find_by(id: params[:id])
@変数.カラム名 = params[:カラム名]
@変数.画像カラム名 = "#{@変数.id}.拡張子"
if @変数.save
...
end

▼画像が存在するか判定する
画像が送信された時にだけ画像を更新するように、画像が送信されたかを判定するif文を追加することで判定ができる。

実装するには、xxx.controller.rbに記載します。
def アクション名
...
if params[:image]
@変数.画像カラム名 = "#{@変数.id}.拡張子"
image = params[:image]
File.binwrite("...", image.read)
end
...
end


▼ログインフォームの作成
①ログイン画面の作成(ルーティング(共通からのリンクでget)、アクション、ビュー)
②ログイン画面のログイン情報(メールアドレス、パスワード)入力欄の作成
③パスワードカラムの作成、バリデーションの設定
④別のルーティング(個別の画面からのリンクでpost)を追加する
⑤フォームの送信先を指定(nameオプションで書き込むカラムを指定)
⑥ログインユーザを特定(成功失敗の分岐をする)
⑦セッションに記録する

▼パスワード入力フォームを伏字にする
パスワード入力フォームを伏字(マスク)するようにします。
xxx.html.erbに下記を記載する。

<input type="password">

▼ログインユーザを特定
ログインする情報(アカウント名やメールアドレスとパスワード)をparamsメソッドで取得します。一致ユーザを特定するためにfind_byを使います。

ログイン情報を取得し、if文で存在有無を判定します。
実装するには、xxx.controller.rbに記載します。
def アクション
@変数 = テーブル.find_by(カラム: params[:カラム], カラム: params[:カラム])
if @変数
flash[:notice] = "表示したい文字列"
redirect_to("成功パターンのURL")
else
render("失敗パターンのURL")
end

※ログイン画面であれば、成功の場合は投稿画面、失敗の場合はエラーメッセージを出し、ログイン画面に遷移するとよいでしょう

失敗時にエラーメッセージを表示させつつ、入力値を残したままにするには下記のように記載する。

def アクション
...
else
@エラー用変数 = "エラーメッセージ本文"
@変数 = params[:変数]
...
render("失敗パターンのURL")
end

xxx.html.erbには下記を記載
<% if @エラー用メッセージ %>
...
<% if @エラー用メッセージ %>
...
<% end %>
...
<input name="変数" value="<%= @変数 %>">

※失敗の場合は入力した値が変数に格納され、inputタグのvalueに初期値として代入される。

▼セッションを制御する
ログインしてページを移動してもユーザー情報を保持し続けるために、sessionという特殊な変数を用います。

実装するには、xxx.controller.rbに記載します。
session[:キー名] = 値

user_idをキーとして特定のユーザのカラム(id)を代入する場合は、
session[:user_id] = @変数.id

ログイン中のユーザIDを表示させるには、
xxx.html.erbに下記を記載する。

<%= session[:キー名] %>

ログアウトするには、ログイン時とは逆にセッションを削除する必要があります。nil(何もない)を代入し、セッションを上書きします。

実装するには、xxx.controller.rbに記載します。
session[:キー名] = nil

よく用いられるの下記の記述です。
session[:キー名] = nil
flash[:notice] = "文字列"
redirect_to("/...")

またroute.rbへの記載はpostを用いる。DBの操作はないが、セッションの操作でもpostを用いる必要があるため。
post "アクション" => "コントロール#アクション"

get → DBを変更しない場合
post → DB, sessionの値を変更する場合

▼ヘッダにログイン状態を表示する
ログインしている場合としていない場合に分岐して表示パターンを変える。
application.html.erbに下記のように記載する。

<%= if session[:キー名] %>
<li>
<%= session[:キー名] %>
</li>
<% else %>
<li>
<%= link_to("表示文字列", "リンク先のパス", {method: "変数"}) %>
</li>
<% end %>

ログイン状況によってヘッダに表示させる情報を制御できる。
※getで書かれているリンクを探してしまうので、postを探す書き方にする

▼ログイン時にログインユーザ名を表示させる
ログイン中のユーザをcurrent_userに代入する場合、
application.html.erbに下記のように記載する。

<%= current_user = User.find_by(id: session[:キー名]) %>
<li>
<%= link_to(current_user.カラム名 , "コントロール/#{current_user.id}") %>
</li>
...

▼アクション側で共通の変数を定義する
各アクションからapplication.html.erbのyieldタグに代入される。
application.html.erbでアクション側の変数を使おうとすると、@current_userを定義する必要があるので繰り返し記載する必要があります。

記載は、xxx.controller.rbにします。

def アクション
@current_user = テーブル.find_by(id: session[:カラム])
end

各コントローラの全アクションで共通する処理がある場合は下記のように記載します。
記載は、xxx.controller.rbにします。

before_action :メソッド名
def アクション
@current_user = テーブル.find_by(id: session[:カラム])
end

アクションを呼び出す前にset_current_userメソッドを呼び出すことができる。
application.html.erbには下記のように記載します。

before_action :set_current_user
def set_current_user
@current_user = テーブル.find_by(id: session[:カラム])
end

▼ログインユーザのみ閲覧可能=ログインしていない場合のアクセス制限
ログインしていない場合でも、URLを直接入力するとアクセスできてしまいます。ログインをしているユーザがいない場合にはログインページにリダイレクトするようにしましょう。
xxx.controller.rbには下記のように記載します。

before_action アクション
if @current_user == nil
flash[:notice] = "表示させる文章"
redirect_to("/リンク先")
end
・・・
end

application.html.erbには下記のように記載します。

def authenticate_user
if @current_user == nil
flash[:notice] = "表示させる文章"
redirect_to("/リンク先")
end
・・・
end

onlyを用いて各コントローラでbefore_actionを使うことで、指定したアクションでのみそのメソッドを実行することができます。
記載は、xxx.controller.rbにします。

before_action :authenticate_user, {only: [:アクション, ...]}

対象を限定しない場合は下記のように記載します。

before_action :authenticate_user

各コントローラは、applicationコントローラを継承しているので、継承元のメソッドを使うことができます。
application.html.erbには下記のように記載します。

def authenticate_user
...
end

xxx.controller.rbには下記のように記載します。

class UserController < ApplicationController
before_action :authenticate_user
end

@変数で定義した変数は同じクラスの異なるメソッド間で共通して使用することが可能です。
application.html.erbには下記のように記載します。

class UserController < ApplicationController
...
def set_current_user
@current_user = テーブル.find_by(id: session[:カラム])
end
def authenticate_user
if @current_user == nil
...
end
...
end

▼未ログインユーザのみ閲覧可能=ログインしている場合のアクセス制限
新規登録ページやログインページなどは未ログインユーザのみ閲覧可能とする。
「ログインユーザーを禁止する」という意味の、forbid_login_userメソッドを作成し、ログインユーザーが存在する場合、投稿一覧ページにリダイレクトするようにします。メソッドの実行にはbefore_actionを用い、onlyで適用したいアクションを指定しましょう。
application.html.erbには下記のように記載します。

def forbid_login_user
if @current_user
flash[:notice] = "表示させる文章"
redirect_to("/リンク先")
end
...
end

対象を限定する場合は、xxx.html.erbには下記のように記載します。

before_action :forbid_login_user, {only: [:アクション, ...]}

▼ユーザーの編集ページのアクセス制限
ユーザー詳細ページで、ログインしているユーザーではない場合には編集ページへのリンクを非表示にする。
コントローラ/アクション.html.erbに記載する。

<% if @user.id == @current_user.id %>
<%= link_to("表示文言", "users/#{@user.id}/edit") %>
<% end %>

アクション側でも同様の条件分岐を用いて制限する。
「正しいユーザーかを確かめる」という意味のensure_correct_userメソッドを用意し、ログイン中のユーザーのidと編集したいユーザーのidが等しいか判定します。
xxx.controller.rbには下記のように記載します。

before_action :ensure_correct_user, {only: [:アクション, ...]}
...
def ensure_correct_user
if @current_user.id != params[:id].to_i
flash[:notice] = "表示させる文章"
redirect_to("/リンク先")
end

▼文字列を数値に変換する
文字列を数値に変換するには、to_iメソッドを用いる。

1 == "1"
=> false
1 == "1".to_i
=> true

例えば、編集したいユーザーのidは文字列であるparams[:id]に格納されているため、数値に変える必要がある。

params[:id].to_i