第零部分
在製作CRUD之前,你總要在index加動作吧
這部分算是CRUD的前置作業,所以稱第零部分
在 初試啼聲(上)時,已經建好index頁面了
/app/views/users/index.html.erb
<table>
<thead>
<tr>
<td>姓名</td>
<td>信箱</td>
<td>性別</td>
<td>年齡</td>
<td>動作</td>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.mail %></td>
<td><%= user.gender %></td>
<td><%= user.age %></td>
<td>
<%= link_to "檢視", user_path(user) %>
<%= link_to "編輯", edit_user_path(user) %>
<%= link_to "刪除", user_path(user), method: "delete", data: {confirm: "delete, r u sure?"} %>
</tr>
<% end %>
</tbody>
</table><%= link_to "新增使用者", new_user_path %>
在表外增加了動作: 新增使用者
表內則增加了動作: 檢視、編輯跟刪除
連結的方式是 <%= link_to “顯示的字”, 連結(通常用path即可) %>
比較要注意的是"刪除",因為刪除不是連結到其他頁面,
這時候可以查一下routes,就知道刪除的動作是藏在/user/:id的destroy
而只要寫 method: “delete”,瀏覽器就會用DELETE的方式送出請求
這時候伺服器會自動把網頁導至/user/:id,執行destroy功能
(我們還沒有在controller設定destroy功能,這最後會寫)
第一部分-Create
回顧下面這張圖,出現過在 初試啼聲(上)
就是我們在routes設定中使用"resources :users"
Rails自動產生的路徑及動作
這邊跟新增相關的單詞,你會看到new跟create
依照Rails慣例而言,new就是產生一個實例變數
create就是資料庫接收這個實例變數
所以你可以發現new是GET方法、create是POST方法
所以我們在create要做的事情如下
- controller寫new方法
- 新增一個新增使用者的view
- controller寫create方法,接收從new傳過來的變數進伺服器
- (額外,要求重要欄位需全填)
首先第一步,寫controller的new方法
/app/controllers/users_controller.rb
def new
@user=User.new
end
就這樣,因為new只是要產生新的實例變數而已
不用處理資料庫也不用驗證送出的資料
第二步,新增一個新增使用者的view(十分饒口)
/app/views/users/new.html.erb
<h1>新增使用者</h1>
<%= form_for(@user) do |f| %>
<%= f.label :name, "姓名" %>
<%= f.text_field :name %> <br/>
<%= f.label :mail, "信箱" %>
<%= f.text_field :mail %> <br/>
<%= f.label :gender, "性別" %>
<%= f.text_field :gender %> <br/>
<%= f.label :age, "年齡" %>
<%= f.text_field :age %> <br/> <%= f.submit %> <br/>
<% end %>
form_for方法之後的"do|f|"所產生的f就是FormBuilder物件
可以透過物件方法,來完成input動作
裡頭基本上就button、checkbox、text_field及submit較為常用
可以參考下面的官方文件說明
補充說明,因為在controller中def new的@user動作是.new
所以 submit上就會去找POST方法,不用額外設定送出的目的地
也不用設定submit上的文字顯示,他會顯示create user
(簡單來說,這就是遵循Rails慣例所帶來的方便)
第三步,controller寫create方法,接收從new傳過來的變數進伺服器
/app/controllers/users_controller.rb
def create
@user = User.new(user_params)
if @user.save
redirect_to user_path, notice: "succeed!"
else
render :new
end
end
這邊有兩個點需要討論
第一點,就是傳入給User.new的user_params是什麼?
因為Rials會避免沒有限制的資料進入Model
如果你沒有限制,這樣有心人士可以很容易覆寫/修改表單上沒有的值
所以很容易Raise ActiveModel::ForbiddenAttributesError
我們必須在/app/controllers/users_controller.rb最下面給一個private方法
也就是給一個方法,限制你輸入的資料只能包含特定欄位
private
def user_params
params.require(:user).permit(:name, :mail, :gender, :age)
end
第二點就是 if @user.save …else …end這段
意思就是如果你成功儲存,回到index,否則render :new
render比較常翻成渲染,這跟redirect_to 不一樣
redirect_to是重新導向某網頁,該網頁會重新讀取
我們希望的功能是
如果資料沒有填齊或格式錯誤導致儲存失敗
網頁不會刷掉使用者原本填好的資料
所以使用 render方法,指向:new的動作,回到新增的頁面
第四步,重要欄位要填
這邊因為篇幅關係先給提示就好、不細講
- 要在controller裡面給 “validates_presence_of”
重要的欄位名稱放後面,意思是這些欄位都要有東西才能存 - 無論是重要欄位沒填或是填的格式錯誤(譬如age填文字)
都會引起error,這時候可以利用erb的功能顯示在view上
看一下幾個stackoverflow的例子,可以直接參考
第二部分-Read
Read其實就是show的功能
看一下路由設定的路徑,就是/user/:id
(這個路徑下有四個HTTP指令,分別是GET、PUT、PATCH跟DELETE)
:id就是名為id的變數,用來傳遞使用者的號碼
所以我們必須要在controller中給一個方法,取得該id使用者的資料
/app/controllers/users_controller.rb
def show
@user=User.find_by(id: params[:id])
end
find_by 先傳入要找的欄位,再傳入瀏覽器給的id
也可以換成 @user=User.find(params[:id])
因為params傳進去的變數名稱就叫id,所以可以直接對應
這又是來自Rails慣例的方便了
Controller設定好使用者了,再來就是view
新增一個/app/views/users/show.html.erb
使用者姓名: <%= @user.name %>
使用者信箱: <%= @user.mail %>
使用者性別: <%= @user.gender %>
使用者年齡: <%= @user.age %><%= link_to 'Back to index', users_path %>
OK,搞定
第三部分,Edit
也就是edit功能
在這邊要使出Rails的魔法了
在controller中,複製show的函式並且名稱改成edit
也就是這樣
def edit #直接複製show,把函式名稱改成edit
@user=User.find_by(id params[:id])
end
在app/views/users/裡面,新增一個edit.html.erb
並且把new.html.erb的內容複製進去,把h1內容換成編輯
也就是這樣
<h1>編輯使用者</h1> <%# 這邊的內容跟"new.html.erb"一模一樣 %>
<%= from_for(@user) do |f| %>
<%= f.label :name, "姓名" %>
<%= f.text_field :name %> <br/>
<%= f.label :mail, "信箱" %>
<%= f.text_field :mail %> <br/>
<%= f.label :gender, "性別" %>
<%= f.text_field :gender %> <br/>
<%= f.label :age, "年齡" %>
<%= f.text_field :age %> <br/><%= f.submit %> <br/>
<% end %>
OK,搞定
又被Rails的慣例給照顧了一次
簡單來說,就是先傳使用者資料給edit
透過User.find找到指定的資料傳給view
所以form裡面的格子就會有現成的資料
接著submit會幫你找到edit動作對應的HTTP是PUT/PATCH
所以按鈕上就會出現update的字眼
至於PUT跟PATCH的討論可以看一下這篇文章
(記得寫Update的方法喔~我就不再補了...)
第四部份,Delete
這部分的view其實已經寫好了,就是在第零部分
我們在連結中已經指定好method,還有提示方框
<%= link_to "刪除", user_path(user), method: "delete", data: {confirm: "delete, r u sure?"} %>
所以這部份不用動,所以就在controller中間新增
def delete
@user = User.find(params[:id])
@user.destroy
redirect_to users_url
end
View有了、Controller有了
OK了,搞定
我們基本上完成了CRUD四個部位的功能
最後這邊補充一些比較常改善的結構寫法
第一個,整理controller的程式碼
我們在edit、show等功能,都需要查詢使用者
這時候就可以用before_action的功能
把查詢使用者寫在private的區塊
然後在最上面寫before_action
參考下面連結
簡單來說就是在def還沒作動之前
before_action會把函式"餵進去"函式
第二個,整理view程式碼
我們知道edit跟new的程式碼幾乎一模一樣
這時候就可以用 Partial Render方法
參考下面連結
簡單來說就是把同樣的部分獨立出來
只要在erb檔用<% render "指定檔名" %>呼叫
就可以傳入指定的程式碼,還可以傳遞變數
大概先降,繼續加油