#Ruby on Rails5速習実践ガイド chapter4
##4-2-1 データ型
データベースのカラムにはそれぞれデータ型というものが必要になってくる。
データ型というのはその中に入る値を特定の条件付きで指定するものである。
データ型 | 説明 |
---|---|
:boolean | 真偽値 |
:integer | 符号付きの整数 |
:float | 浮動小数点数 |
:string | 文字列(短い) |
:text | 文字列(長い) |
:date | 日付 |
:datetime | 日時 |
それぞれのカラムに上記のデータ型を付け加えることで中に入るデータを区別する
#4-2-2 NOT NULL制約
カラムは値のない場合NULLとして値を設定しテーブルとして保存するがNOT NULLを使うことによってカラムには必ず何らかの値が入ることを指定することができる。
・NOT NULL制約のかけかた
NOT NULL制約には2種類のかけ方がある。1つ目がテーブルを作成する時に制約をつける方法。2つ目がテーブルを作成し終わってから制約を付け足したい時の制約をつける方法である。
1.テーブルを作成する時に制約をつける方法
テーブルを作成するためのマイグレーションファイルにそのままNOTNULL制約をかけてしまってからdb:migrateしてテーブルを作成する方法
class CreateTasks < ActionRecord::Migration[5.2]
def change
create_table :tasks do |t|
t.string :name, null: false
t.text :description
...
nameの部分は何か値がないと困るのでnameの後ろにnullをつける。
その後ろにnull(空白)でもいいならtrue,空白が嫌(NOTNULL)ならfalseをつける
2.テーブルを作成し終わってから制約を付け足したい時の制約をつける方法
$ bin/rails g migration ChangeTaskNameNotNull
テーブルはもう作ってしまったのでマイグレーションファイルを変更しても意味がない。なので変更用のマイグレーションファイルを作りそこに変更用のコードを書く
class ChangeTaskNameNotNull < ActionRecord::Migration[5.2]
def change
change_column_null :tasks, :name, false
end
end
できた変更用マイグレーションファイルにchange_column_null(カラムをnullにchangeする)を使ってタスクを変更する。
change_column_null :タスク名, :カラム名, :trueかfalseか(trueはnull、falseはnotnull),
##文字列カラムの長さを指定する (changeメソッド と upメソッドdownメソッド)
文字列の長さを指定するには上記の説明である通り、テーブル作成のマイグレーションファイルに情報を追加するか、情報が決まってしまったテーブルに情報追加のためのマイグレーションファイルを作るかのどちらかである。今回は情報が決まってしまったテーブルに情報追加のためのマイグレーションファイルを作る方法を見てみる。
class ChangeTasksNameLimit30 < ActionRecord::Migration[5.2]
def up
change_column :tasks, :name, :string, :limit: 30
end
def down
change_column :tasks, name, :string
end
end
今回はchangeメソッドの代わりにupメソッドとdownメソッドが書かれているところに注目する。本来changeメソッドは上のupメソッドとdownメソッドを合わせて作られるメソッドである。なぜ今回changeメソッドを二つに分けたかというと、答えは[change_column]にある。change_columnをchangeメソッドで使うと何かあった時に元に戻せなくなるのだ。バージョンを戻す時には上げたバージョン(upやchange)を見ながらそれと逆の操作をしてバージョンを戻す。しかしchangeメソッドだとそれがうまくいかないのである。
##4-2-4 ユニークインデックスを作成する
ユニークインデックスも上記のようにテーブル作成のマイグレーションファイルか追加変更用のマイグレーションファイルに書かれる。
class AddNameIndexToTasks < ActiveRecord::Migration[5.2]
def change
add_index :task, :name, unique: true
end
end
ユニークインデックスをつけると値(この場合はname)の重複が無くなる(被らなくなる)
##4-3-6 オリジナルの検証コードを書く (自分でvalidatesを作る)
validate :validate_name_not_including_comma
まず自分で作ったvalidateの名前をvalidateに入れる。
private
def validate_name_not_including_comma
errors.add(:name,'にカンマを含めることはできません') if name&.include?(',')
end
自分の作ったvalidateの内容をprivateメソッドのなか(外部からいじられたらダメなので)で定義。
if name&.include?(',') もしカンマがついた名前があると普通に実行するがカンマがない名前になると結果はnilになりエラーが出てしまう。ぼっち演算子ならnilが出てエラーにならずにnilという言葉だけ出てくる。
##4-5-1 セッション
セッションがあることによってどんなページへ何回とんでも自分が操作しているという状態は保たれる(ログイン状態になる)
session[user_id] = @user.id
@user.id = session[user_id]
上がセッションに情報を入れる操作。下がセッションの値を取り出す操作になっている。
##4-5-2 Userモデルを作る(password-digest)
password-digestを使うとパスワードが暗号化されパスワード漏洩や不正アクセスの心配が無くなる
password-digestを対応させるにはhas_secure_passwordというコードを書くと使えるようになる。
class User < ApplicationRecord
has_secure_password
end
モデルにhas_secure_passwordをかく。そうするとpasswordカラムの下にpassword_confimationというカラムができる。これはパスワード確認のために2回入力させるためのやつ。この2つのパスワードがpassword-digestで暗号化された時に一致しているかどうかで判断する。
##4-5-8 ログイン情報の取得を簡単にする
User.find_by(id:session[user_id])
今セッションしているidを探すというコードのなのでログインをしていればこのコードでログインしているユーザーを特定できる。
これをApplicationControllerのなかでメソッドとして定義してやるとどこのアクション内でもUser.find_by(id:session[user_id])が働き、ログインしているという情報がついてくる。
なので新しくメソッドを作る
class ApplicationController < ActionController::Base
helper_method :current_user
private
def current_user
@current_user||=User.find_by(id:session[user_id]) if session[:user_id]
end
end
「もし@current_userが機能しているなら@current_userを、そうじゃない場合はもしuser_idがセッションしている状態ならそのセッションしているユーザーの情報を探して、それを変数@current_userとする」という動作をcurrent_userメソッドと定義する。このメソッドは外部から操作されてはいけないのでprivateメソッドのなかに入れる
##4-5-9 ログアウト機能を実装する
ログイン状態はreset_sessionでログアウト状態になる
def destroy
reset_session
redirect_to root_url, notice: 'ログアウトしました'
end
なのでreset_sessionはdestryコントローラーの中に定義する。
##4-5-11-1 データベース上でUserとTaskを紐付ける
UserとTaskを紐づける上で重要になってくるのは【1対多】という関係。User一人に対しTaskは複数存在するといった状況のことである。具体的な紐付けについて説明する。
1.まずtaskのユーザー情報のuserカラムがNOTNULLな状態にならなければいけない。
class AddUserIdToTasks < ActiveRecord::Migration[5.2]
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
remove_reference:カラムの中の制約を付けたい時にreferenceを使う。
remove_reference :tasks, :user, index: true←このデータを
add_reference :tasks, user, null: false, index: true←userがnull:falseのなるように制約を付け加えた。
execute 'DELETE FROM tasks;'←制約をつける前にuserがnull:falseだったデータがあるかもしれない、もしあったらエラーになる。なのでこのコードを使いテーブルにあるデータを全削除した。
2.1対多の関係を表すhas_many :tasksとbelongs_toを使って関係性を構築する。
has_many :tasks
end
belongs_to :user
end
データベース間の関係はモデルが行うのでモデルに記入。
user has many tasks
tasks belongs to user
の関係になるので
userモデルにhas_many :tasks
tasksモデルにbelongs_to :user
を加える。
##4-5-11-3 ログインしているユーザーのTaskデータの登録
ログインするためのcreateアクションは以下のようになっている
def create
@task = Task.new(task_params)
・
・
・
・
・
private
def task_params
params.require(:task).permit(:name, :descrition)
end
end
このままだと@taskで新規タスクは作られるがログイン中のユーザーのタスクだということがわからなくなる。なので上のコードを書き換える
def create
@task = current_user.task.new(task_params)
・
・
・
・
・
private
def task_params
params.require(:task).permit(:name, :descrition)
end
end
current_userがつくことによって「現在ログイン中の」という意味のtaskになる。
つまり先頭にcurrent_userをつけると何でも「ログイン中の」という意味がついてくる。
例えば
Task.find(params[:id])→指定したidのタスクを取ってくる
current_user.tasks.find(params[:id])→ログイン中の人のタスクの中から指定したタスクを取ってくる。
##4-8 scopeを活用する
order(created_at: :desc)などはscopeを使うともっとシンプルな名前に変えることができる。
scope :recent, -> {order(created_at: :desc)}
このコードでorder(created_at: :desc)はrecentに書き換えることができる。
##その他勉強になったこと
task.errors.full_messages
taskのエラー情報が表示される
task.persisted?
taskがデータベースへ登録済みかどうか確認できる。
if task.errors.present?
ul#errors_explanation
-task.errors.hull_messages.each do |message|
li= message
errors.present?でエラーがあるかないかを判断する。
if user&.authenticate(session_params[:password])
private
def session_params
params.require(:session).permit(:email, :password)
end
本来はuser(session_params[:password])だがpassword-digestで暗号化した場合は
user&.authenticate(session_params[:password])になる。
if current_user.admin?
adminでユーザー管理権限を持っているかを確認することができる。そこにcurrent_userが加わり、「ログインしている人はそのユーザーの管理権限を持っているのか?」というif文になる。
@task = current_user.task.order(created_at: :desc)
orderは指定した基準で一覧を並び替える操作
今回はcreated_at(作成日時)を基準で並ぶ順番が変わっている