Ruby on Rails 初試啼聲(中)

張凱喬
12 min readOct 31, 2017

--

這一篇要來完成CRUD設定
簡單來說
就是個別有一個Action(create、read、update、destroy)

其中三個(create、read、update)會有頁面
destroy則只有動作,沒有專屬的頁面

而上一部 初試啼聲(上)
已經把重要的概念都提出了
這一部會介紹更多語法
大致上做的就是把對的語法放在對的位置
至於改善結構以及外觀等細節,會另開系列文章

PS
後來發現Alphacamp的課程
要自己實作一個簡單的專案
只好把這個專案變成初試啼聲(下)了

第零部分

在製作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要做的事情如下

  1. controller寫new方法
  2. 新增一個新增使用者的view
  3. controller寫create方法,接收從new傳過來的變數進伺服器
  4. (額外,要求重要欄位需全填)

首先第一步,寫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的動作,回到新增的頁面

第四步,重要欄位要填
這邊因為篇幅關係先給提示就好、不細講

  1. 要在controller裡面給 “validates_presence_of”
    重要的欄位名稱放後面,意思是這些欄位都要有東西才能存
  2. 無論是重要欄位沒填或是填的格式錯誤(譬如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 "指定檔名" %>呼叫
就可以傳入指定的程式碼,還可以傳遞變數

大概先降,繼續加油

--

--

No responses yet