iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 21
0

接續昨天的介紹
因為時間的關係(?)
還只停留在皮毛的階段
今天要繼續為各位講解有關路由的一些技巧與觀念

另外關於設定路由時常犯的新手錯誤
也可以參考前作新手常犯的十種錯 2內的第八點:
『找不到路由』
本篇就不再重複。

昨天介紹了resources的使用方法
事實上在專案裡,只有一層的情況反而少見
通常都會是複數嵌套的方式使用,例如:

resources :books do
  resources :pages
end

會產生如下的路由:

        Prefix Verb   URI Pattern                              Controller#Action
    book_pages GET    /books/:book_id/pages(.:format)          pages#index
               POST   /books/:book_id/pages(.:format)          pages#create
 new_book_page GET    /books/:book_id/pages/new(.:format)      pages#new
edit_book_page GET    /books/:book_id/pages/:id/edit(.:format) pages#edit
     book_page GET    /books/:book_id/pages/:id(.:format)      pages#show
               PATCH  /books/:book_id/pages/:id(.:format)      pages#update
               PUT    /books/:book_id/pages/:id(.:format)      pages#update
               DELETE /books/:book_id/pages/:id(.:format)      pages#destroy
         books GET    /books(.:format)                         books#index
               POST   /books(.:format)                         books#create
      new_book GET    /books/new(.:format)                     books#new
     edit_book GET    /books/:id/edit(.:format)                books#edit
          book GET    /books/:id(.:format)                     books#show
               PATCH  /books/:id(.:format)                     books#update
               PUT    /books/:id(.:format)                     books#update
               DELETE /books/:id(.:format)                     books#destroy

共16種。
如果不會全部用到,可以用到昨天所提的only方法
只產生需要的部分,可以避免過於雜亂充滿無用的路由

技術上可以超過三層或更多
但實務上非常不建議這麼做,因為不好理解,中間也需要準備許多不必要的參數
以上圖為例,pages的update需要page_id與book_id
但實務上有page_id就可以取得page的實體,裡面一定會包含book_id
所以傳入的book_id也可以說是多餘的
這個狀況在三層以後會更為明顯
為了避免這個情況當然也可以用限制方法手刻
但rails其實也提供了一種很便捷地整理方法:

resources :books do
  resources :pages, shallow: true
end

會產生如下(因為book的部分沒有變故略過)

      Prefix Verb   URI Pattern                         Controller#Action
   book_pages GET    /books/:book_id/pages(.:format)     pages#index
              POST   /books/:book_id/pages(.:format)     pages#create
new_book_page GET    /books/:book_id/pages/new(.:format) pages#new
    edit_page GET    /pages/:id/edit(.:format)           pages#edit
         page GET    /pages/:id(.:format)                pages#show
              PATCH  /pages/:id(.:format)                pages#update
              PUT    /pages/:id(.:format)                pages#update
              DELETE /pages/:id(.:format)                pages#destroy

很清楚地發現
除了index,create,new這三個方法因為無可避免地會用到book_id以外
其餘都只需要傳入page_id,整個路由都顯得相當清爽
這邊要特別注意
原本form如果update與create共用時
只需要傳入model本身就會自行判斷路由,在使用shallow後會因為URL所需的變數不同而導致錯誤
假設原本寫法如下:

<%= simple_form_for [@book, @page], method: method do |f| %>

新增時可以正常運作,但卻會因為update時已經不需要book_id而導致錯誤
解決方法是將url作為參數一起傳進去
無法繼續讓rails自行判斷

如果路由有重複使用的情況
可以使用concern來收納
例如

resources :messages do
  resources :comments
end
 
resources :articles do
  resources :comments
end

在messages與articles下面都有一模一樣的comments
可以整理如下:

concern :commentable do
  resources :comments
end

resources :messages, concerns: :commentable
resources :articles, concerns: :commentable

出來的結果是相同的

最後要提供一個觀念
router設計是以簡單易懂為原則
目標是貼近企業邏輯,讓開發者與使用者容易理解
而不是盡量貼近原本設計的資料庫階層或正規化
新手一開始設計路由時,常常會侷限於model的型態
而無法展開手腳
其實路由是可以很靈活的變化
只要讓開發者與使用者能夠更一目了然就可以了!!
不需要拘泥於資料結構

希望大家都能寫出更簡潔易懂的路由囉 :)


上一篇
路由 routes
下一篇
淺談callback -- 物件生命週期的掛鉤
系列文
Ruby礦工的Rails地圖30

尚未有邦友留言

立即登入留言