本章的作業目標:
- 安裝 gem "devise"
- 在 navbar 安裝登入/登出按鈕
- 調整修改 devise views ( 註冊, 登入, 修改帳號頁面 ... etc)
- 利用 before_action :authenticate_user! 來做要求登入的設定
- 客製化 devise => 新增一個 "name" 的欄位
- 使用者功能 整合進 group / post 裡面 (作者機制)
- 只有作者才能有 group / post 的修改/刪除權限
安裝 gem "devise"
gemfile 插入 『 gem 'devise' 』
gem "devise", "~> 3.4.1"
bundle install
安裝新增的 gem
設定 Devise
rails g devise:install
devise 安裝
rails g devise user
建立 user 功能
rake db:migrate
由於需要一個資料庫來儲存使用者資料
devise 很聰明的幫我們把相關設定都做好了
所以只需要執行這行來建立 User 的資料庫
rails g devise:views
叫出(原本是隱藏的) devise views
未來可以客製化修改
別忘了重開 rails server
在 navbar 安裝登入/登出按鈕
...
...
+ <% if !current_user %>
+ <li> <%= link_to("註冊", new_user_registration_path) %> </li>
- <li> <%= link_to("登入", "#") %> </li>
+ <li> <%= link_to("登入", new_user_session_path) %> </li>
+ <% else %>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+ Hi!, <%= current_user.email %>
+ <b class="caret"></b>
+ </a>
+ <ul class="dropdown-menu">
+ <li> <%= link_to("登出", destroy_user_session_path, method: :delete) %> </li>
+ </ul>
+ </li>
+ <% end %>
...
...
...(略)
//= require turbolinks
+ //= require bootstrap/dropdown
//= require bootstrap/alert
//= require_tree .
...(略)
調整修改 devise views ( 註冊, 登入, 修改帳號頁面 ... etc)
因為導入 bootstrap ,所以跟原始的 CSS 設定有衝突導致版面炸掉
我們在舊版本是用手動修改的方式處理
現在可以直接用作者開發的新功能,自動化幫我們處理,讓 deivse views 可以支援 bootstrap 3 的 css 設定
打開 Gemfile
原本
...
...
gem "simple_form"
...
...
改成
...
...
gem "simple_form", "~> 3.1.0.rc2", github: "plataformatec/simple_form", branch: "master"
...
...
更新安裝 gem
bundle install
安裝支援 bootstrap 3 套件
$ rails generate simple_form:install --bootstrap
before
after
--
利用 before_action :authenticate_user! 來做要求登入的設定
在 groups_controller.rb 加入
before_action :authenticate_user!
這是 devise 內建的功能,只要把它放進 controller 裡面,
就會自動驗證使用者是否入
if yes => 繼續下面的程序
if no => 轉到登入畫面
我們可以把這一行放進前面做的二個 controller : groups 跟 posts 裡面
class GroupsController < ApplicationController
before_action :authenticate_user!
...
...
class PostsController < ApplicationController
before_action :authenticate_user!
...
...
new, create, edit, update, destroy 等 action 必須先登入才能操作
我們會發現,不是所有 action 都一定要登入才行
只有跟 新增 / 修改 / 刪除 有關的 action 才需要先登入
class GroupsController < ApplicationController
- before_action :authenticate_user!
+ before_action :authenticate_user!, only: [:new, :edit, :create, :update, :destroy]
...
...
posts_controller 由於只有以上五個 action, 就不需要再設定 only:
客製化 devise => 新增一個 "name" 的欄位
devise 內建的資料格式並沒有 name 這個欄位,所以我們要客製化一個出來
rails g migration add_name_to_user
新增一個資料庫異動設定, 名稱是 add_name_to_user ( user 表單新增一個 name 欄位)
打開新增的檔案, 位於 db/migrate/(一堆數字)_add_name_to_user.rb
class AddNameToUser < ActiveRecord::Migration
def change
+ add_column :users, :name, :string
end
end
接下來跑 rake db:migrate
這樣 user 資料庫就有 name 這個欄位了
客製化 devise 的 views => 註冊頁面新增 name 欄位
打開 app/views/devise/registrations/new.html.erb
...
...
<div class="form-inputs">
<%= f.input :email, required: true, autofocus: true %>
+ <%= f.input :name, required: true %>
<%= f.input :password, required: true, hint: ("#{@minimum_password_length} characters minimum" if @validatable) %>
<%= f.input :password_confirmation, required: true %>
</div>
...
現在還只是前端的部分完成,後端還要再加一個設定,才能讓 name 輸入的值真正存到資料庫裡面
(還記得前面說的 strong_params 嗎?)
加入 strong_parameters 與 devise 整合的 hack
打開 app/controller/application_controller
class ApplicationController < ActionController::Base
...
...
+ before_filter :configure_permitted_parameters, if: :devise_controller?
+ protected
+ def configure_permitted_parameters
+ devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
+ end
end
這樣註冊新會員功能就完成了, 請新建立一個測試用帳號 來測試功能
navbar 上的 hi! email 改成 hi! name
既然我們都能讓會員取名字了, navbar 上的 greeting 也該改成 name 吧?
打開 app/views/common/_navbar.html.erb
...
...
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
- Hi!, <%= current_user.email %>
+ Hi!, <%= current_user.name %>
<b class="caret"></b>
</a>
...
...
before
after
新增 "帳號設定" 功能,並能修改自己帳號的 name
我們在前面章節也有創造一個帳號,但是卻還沒命名
所以需要做一個 "使用者帳號設定" 頁面來把還沒命名的帳號命名
Devise 已經幫我們建好內建的功能
打開 app/views/common/_navbar.html.erb
...
...
<ul class="dropdown-menu">
+ <li> <%= link_to("帳號設定", edit_user_registration_path )%></li>
<li> <%= link_to("登出", destroy_user_session_path, method: :delete) %> </li>
</ul>
...
...
修改 edit_account 的 strong_parameters
...
...
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation) }
+ devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:name, :email, :password, :password_confirmation, :current_password) }
end
end
使用者功能 整合進 group / post 裡面 (作者機制)
group 資料表新增 user_id 欄位
rails g migration add_user_id_to_group
打開 db/migrate/(一堆數字)add_user_id_togroup.rb
class AddUserIdToGroup < ActiveRecord::Migration
def change
+ add_column :groups, :user_id, :integer
end
end
rake db:migrate
這樣 group 資料庫裡面,就多了 user_id 欄位
對 migration 想了解更多的 可參考 Rails 資料庫遷移(中文) / Rails 資料庫遷移(英文)
設定 user 跟 group 之間的資料庫關聯 ( database relationship )
打開 app/models/user.rb
class User < ActiveRecord::Base
...
...
+ has_many :groups
end
打開 app/models/group.rb
class Group < ActiveRecord::Base
validates :title, presence: true
has_many :posts
+ belongs_to :owner, class_name: "User", foreign_key: :user_id
end
延伸閱讀 : Rails Active Record 關聯(中文)
只有作者才能有 group / post 的修改/刪除權限
只有作者才能有 group 的修改/刪除權限
打開 app/controllers/groups_controller.rb
只要找出 edit, create, update, destroy 這四個 action
class GroupsController < ApplicationController
...
...
def edit
- @group = Group.find(params[:id])
+ @group = current_user.groups.find(params[:id])
end
def create
- @group = Group.new(group_params)
+ @group = current_user.groups.new(group_params)
...
...
end
def update
- @group = Group.find(params[:id])
+ @group = current_user.groups.find(params[:id])
...
...
end
def destroy
- @group = Group.find(params[:id])
+ @group = current_user.groups.find(params[:id])
...
...
end
...
...
解說
這段 code 簡單來說就是
把 Group 換成 current_user.groups (無誤!)
運作邏輯是這樣:
把 create, edit, update, destroy 這四個 action 的運作
從原本
只是單純呼叫 group 的資料庫來
找某筆資料( .find(params[:id]) )
or
建立一筆新資料 ( .new(group_params) )
改成
登入的使用者( current_user ) 所擁有的 " group資料 ( .groups ) " 來做
找某筆資料( .find(params[:id]) )
or
建立一筆新資料 ( .new(group_params) )
當運作 create 創建新資料時
就會把 登入的使用者 ( current_user ) 的 id
寫進 group 資料庫裡的 user_id 的欄位裡面
所以這個 group 資料裡的 『 user_id 的欄位裡的值 』 就會等於 『 使用者( user )的 id 』 => 自動弄出作者這功能
至於 edit, update, destroy 這三個會讓資料異動的功能
則會自動驗證 group 裡的 user_id 跟 current_user 的 id 是否一致
如果是 true => 執行後續的程序
如果是 false => 直接跳 error (開發模式) or 404 (產品模式)
修改 view => 只有作者才會出現 edit / delete 按鈕
打開 app/models/group.rb
...
...
belongs_to :owner, class_name: "User", foreign_key: :user_id
+ def editable_by?(user)
+ user && user == owner
+ end
...
...
打開 app/views/groups/index.html.erb
...
...
+ <% if group.editable_by?(current_user) %>
<%= link_to("Edit", edit_group_path(group), class: "btn btn-sm btn-default")%>
<%= link_to("Delete", group_path(group),class: "btn btn-sm btn-default", method: :delete, data: { confirm: "Are you sure?" } )%>
+ <% end %>
...
...
解說
這段 code 重點在於
# app/models/group.rb
def editable_by?(user)
user == owner
end
# app/views/groups/index.html.erb
<% if group.editable_by?(current_user) %>
會去驗證 "登入的使用者" 跟 "作者的 id" 是否一致
if true => 顯示 edit 跟 delete 按鈕
if false => 隱藏
只有作者才能有 post 的修改/刪除權限
跟上述差不多,就直接貼 code , bj4 (不解釋) 了 ... XD
rails g migration add_user_id_to_post user_id:integer
rake db:migrate
打開 app/models/user.rb
class User < ActiveRecord::Base
...
...
has_many :groups
+ has_many :posts
end
打開 app/models/post.rb
改成
class Post < ActiveRecord::Base
...
...
+ belongs_to :author, class_name: "User", foreign_key: :user_id
+
+ def editable_by?(user)
+ user && user == author
+ end
end
打開 app/controllers/posts_controller.rb
...
...
def edit
- @post = @group.posts.find(params[:id])
+ @post = current_user.posts.find(params[:id])
end
def create
- @post = @group.posts.new(post_params)
+ @post = @group.posts.build(post_params)
+ @post.author = current_user
...
...
end
def update
- @post = @group.posts.find(params[:id])
+ @post = current_user.posts.find(params[:id])
...
...
end
def destroy
- @post = @group.posts.find(params[:id])
+ @post = current_user.posts.find(params[:id])
...
...
end
...
...
打開 app/views/groups/show.html.erb
..
..
+ <% if post.editable_by?(current_user) %>
<%= link_to("Edit", edit_group_post_path(post.group, post), class: "btn btn-default btn-xs")%>
<%= link_to("Delete", group_post_path(post.group, post), class: "btn btn-default btn-xs ", method: :delete, data: { confirm: "Are you sure?" } )%>
+ <% end %>
...
..
完成圖