上一篇筆記:Astro課程 0723-0731 - Rails (Day1-Day4)
書籍推薦:Everyday Rails Testing with RSpec
sign_up.html.erb
使用form_for(傳遞一個model)
<%= form_for(@user) do |form| %>
<div class="fields">
<%= form.label :account, "帳號" %>
<%= form.text_field :account %>
</div>
<div class="fields">
<%= form.label :password, "密碼" %>
<%= form.password_field :password %>
</div>
<div class="fields">
<%= form.label :email, "信箱" %>
<%= form.email_field :email %>
</div>
<%= form.submit "註冊帳號" %>
<% end %>
def sign_up
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirct_to root_path, notice: "會員註冊成功"
else
render :sign_up
end
end
http沒有狀態,不知道使用者有沒有登入
所以rails後台給瀏覽器一張session
的票
session[:user_token] = @user.id
:user_token
後面會塞一些使用者的獨特訊息(eg.各自用的瀏覽器id),以判斷不同使用者
Eg.發文的時候才會檢查是否有登入 => 檢查票是否存在
def create
@user = User.new(user_params)
if @user.save
session[:user_token] = @user.id
redirect_to home_users_path, notice: "會員註冊成功"
else
render :sign_up
end
end
<nav>
<ul>
<% if session[:user_token]%>
<li>account</li>
<li>登出</li>
<% else %>
<li>登入</li>
<li>註冊</li>
<% end %>
</ul>
</nav>
每一頁都需要用到的功能,放在application controller
沒有註冊的話,還是要show出頁面
before_action :find_user
private
def find_user
if seesion[:user_token]
@current_user = User.find(session[:user_token])
end
視圖小幫手
form_for
和 link_to
都是view helper
遇到同名的helper 後面的檔會蓋過前面(所以不需要的檔案就把它刪掉)
rails generator config: 自訂產生的預設檔案
rails template
module UsersHelper
# 檢查使用者有沒有登入
def user_signed_in?
session[:user_token]
end
# 登入的話把session找出來
def current_user
User.find(session[:user_token]) if user_signed_in?
end
end
_navbar就可以使用current_user
了!
<nav>
<%= hello %>
<ul>
<% if current_user %>
<li><%= current_user.account%></li>
<li><%= link_to '登出', sign_out_users_path, method: :delete %></li>
<% else %>
<li>登入</li>
<li>註冊</li>
<% end %>
</ul>
</nav>
board controller加上條件判斷
def new
if user_signed_in?
@board = Board.new
else
redirect_to sign_in_users_path, notice: "請先登入會員";
end
寫在application controller,然後匯出給view用
helper_method :user_signed_in?, :current_user
private
def not_found
render file: '/public/404.html', status: 404
end
def user_signed_in?
# session繼承ActionController, 可以在controller和view用
session[:user_token]
end
def current_user
User.find(session[:user_token]) if user_signed_in?
end
參考helper_method api,改成||
的寫法
def user_signed_in?
current_user != nil
#session[:user_token]
end
def current_user
@current_user ||= User.find_by(id: session[:user_token])
# User.find(session[:user_token]) if user_signed_in?
end
def sign_out
session[:user_token] = nil
redirect_to root_path, notice: "登出成功"
end
進一步:把session包成private方法
增加程式碼可讀性
private
def sign_in_user(u)
session[:user_token] = u.id
end
def sign_out_user
session[:user_token] = nil
end
def create
@user = User.new(user_params)
if @user.save
# 登入
sign_in_user(@user)
redirect_to root_path, notice: '會員註冊成功'
else
render :sign_up
end
end
def sign_out
sign_out_user
redirect_to root_path, notice: '登出成功'
end
我們想讓使用者輸入帳號
或email
都可以登入
sign_in.html.erb
<h1>登入會員</h1>
<%= form_for(@user, url: login_users_path) do |form| %>
<div class="fields">
<%= form.label :account, "帳號" %>
<%= form.text_field :account, placeholder: "請輸入帳號與密碼" %>
</div>
<div class="fields">
<%= form.label :password, "密碼" %>
<%= form.password_field :password %>
</div>
<%= form.submit "登入" %>
<% end %>
先看之前寫過的user.rb
def self.login(options)
if options[:account] && options[:password]
find_by(account: options[:account],
password: add_salt(options[:password])) # 已加密的密碼
# else => 要不然是true要不然是nil,所以可以註解掉
# return false
end
end
然後思考user controller
def login
# 寫params的話,用兩層 params[:user][:account]
if user_params[:account] && user_params[:password] #有填寫的話
# 確認有填寫之後要認證
# Duck Typing: 長得看起來像Hash,所以就當作Hash來用(跟之前寫的option類似)
user = User.login(user_params) # 兩種結果: user的資料或是nil
if user #有使用者的話
sign_in_user(user)
else #nil
redirect_to sign_in_user_path, notice: "請輸入正確帳號密碼"
end
else # 沒有填寫的話
redirect_to sign_in_user_path, notice: "請輸入正確帳號密碼"
end
簡化:
def login
user = User.login(user_params)
if user
sign_in_user(user)
redirect_to root_path, notice: "成功登入"
else
redirect_to sign_in_users_path, notice: "請輸入正確帳號密碼"
end
end
eg. 編輯、刪除看板
boards controller
before_action :authenticate_user!, except: [:index, :show]
把:authenticate_user!
放在application controller裡,讓其他的controller也可以用
def authenticate_user!
redirect_to root_path, notice: "請登入會員!" if not user_signed_in?
end
開新欄位:產生新欄位給board
rails g migration add_user_to_board user:belongs_to
但之前已建好的板user_id不存在
class AddUserToBoard < ActiveRecord::Migration[6.0]
def change
add_reference :boards, :user, null: false, foreign_key: true
# add_column :boards, :user_id, :integer
end
end
另一種方式:
rails g model BoardMaster user:belongs_to board:belongs_to
board.rb
has many users:
has_many :board_masters
has_many :users, through: :board_masters
user.rb
has many boards:
has_many :board_masters
has_many :boards, through: :board_masters
進去console查詢多對多
2.5.2 :002 > b1 = Board.first
Board Load (0.2ms) SELECT "boards".* FROM "boards" WHERE "boards"."deleted_at" IS NULL AND "boards"."deleted_at" IS NULL ORDER BY "boards"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Board id: 2, title: "PHP", intro: nil, deleted_at: nil, state: "normal", created_at: "2020-07-23 07:46:35", updated_at: "2020-07-23 07:46:35">
2.5.2 :003 > b1.users
User Load (0.3ms) SELECT "users".* FROM "users" INNER JOIN "board_masters" ON "users"."id" = "board_masters"."user_id" WHERE "board_masters"."board_id" = ? LIMIT ? [["board_id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.5.2 :001 > u1 = User.first
User Load (0.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, account: "bb", password: "cf53685c123ae36dcfe5ef73faaf9ea769f86c16", email: "bb", nickname: nil, gender: nil, state: "normal", deleted_at: nil, created_at: "2020-08-02 09:08:53", updated_at: "2020-08-02 09:08:53">
2.5.2 :002 > u1.boards
Board Load (0.8ms) SELECT "boards".* FROM "boards" INNER JOIN "board_masters" ON "boards"."id" = "board_masters"."board_id" WHERE "boards"."deleted_at" IS NULL AND "boards"."deleted_at" IS NULL AND "board_masters"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
徵求中
view
<div>
[版主:<%= display_bm(@board) %>]
</div>
建一個boards_helper.rb
module BoardsHelper
def display_bm(board)
if board.users.count == 0
"徵求中"
else
board.users.map { |user| user.account }.join("/")
end
end
end
b1.users << u1
2.5.2 :001 > u1 = User.first
User Load (0.7ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, account: "bb", password: "cf53685c123ae36dcfe5ef73faaf9ea769f86c16", email: "bb", nickname: nil, gender: nil, state: "normal", deleted_at: nil, created_at: "2020-08-02 09:08:53", updated_at: "2020-08-02 09:08:53">
2.5.2 :002 > u1.boards
Board Load (0.8ms) SELECT "boards".* FROM "boards" INNER JOIN "board_masters" ON "boards"."id" = "board_masters"."board_id" WHERE "boards"."deleted_at" IS NULL AND "boards"."deleted_at" IS NULL AND "board_masters"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.5.2 :003 > b1 = Board.first
Board Load (3.2ms) SELECT "boards".* FROM "boards" WHERE "boards"."deleted_at" IS NULL AND "boards"."deleted_at" IS NULL ORDER BY "boards"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Board id: 2, title: "PHP", intro: nil, deleted_at: nil, state: "normal", created_at: "2020-07-23 07:46:35", updated_at: "2020-07-23 07:46:35">
2.5.2 :004 > b1.users
User Load (1.6ms) SELECT "users".* FROM "users" INNER JOIN "board_masters" ON "users"."id" = "board_masters"."user_id" WHERE "board_masters"."board_id" = ? LIMIT ? [["board_id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.5.2 :005 > u1.boards
Board Load (0.3ms) SELECT "boards".* FROM "boards" INNER JOIN "board_masters" ON "boards"."id" = "board_masters"."board_id" WHERE "boards"."deleted_at" IS NULL AND "boards"."deleted_at" IS NULL AND "board_masters"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.5.2 :006 > b1.users << u1
(0.7ms) begin transaction
BoardMaster Create (2.6ms) INSERT INTO "board_masters" ("user_id", "board_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["user_id", 1], ["board_id", 2], ["created_at", "2020-08-03 09:16:55.601429"], ["updated_at", "2020-08-03 09:16:55.601429"]]
(1.2ms) commit transaction
User Load (0.2ms) SELECT "users".* FROM "users" INNER JOIN "board_masters" ON "users"."id" = "board_masters"."user_id" WHERE "board_masters"."board_id" = ? LIMIT ? [["board_id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, account: "bb", password: "cf53685c123ae36dcfe5ef73faaf9ea769f86c16", email: "bb", nickname: nil, gender: nil, state: "normal", deleted_at: nil, created_at: "2020-08-02 09:08:53", updated_at: "2020-08-02 09:08:53">]>