iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 17
0

本篇將介紹 Phoenix 的資料夾結構,及用於快速產生 CRUD 功能的模版生成指令。


接續上次的文章,我們來看看 hello_phx 資料夾裡目前有什麼東西,先看第一層:

hello_phx
├── _build
├── assets
├── config
├── deps
├── lib
│   ├── hello_phx
│   ├── hello_phx.ex
│   ├── hello_phx_web
│   └── hello_phx_web.ex
├── mix.exs
├── mix.lock
├── priv
└── test

你會發現這個資料夾結構,跟我們在第 15 天的 mix 專案,與使用其它模組裡的函式 裡的一般 elixir 專案幾乎相同,只多了 assetspriv 兩個資料夾而己。

lib :最核心的資料夾

用 Phoenix 開發網頁應用程式時,跟一般的 elixir 專案一樣,最主要的程式碼都會放在 lib 裡面。lib 底下,有兩個資料夾及兩個檔案。就像普通 elixir 專案,我們會把核心業務邏輯放在 lib/專案名稱.ex 檔案及 lib/專案名稱 資料夾中。

而帶有 web 字樣的,就是跟網頁相關的部份了。 熟悉 Rails (或其它 MVC) 框架的使用者,會覺得非常親切,但似乎多了什麼,也好像少了什麼,這裡先賣個關子,等到適當的時候再來討論。

lib
├── hello_phx
│   ├── application.ex
│   └── repo.ex
├── hello_phx.ex
├── hello_phx_web
│   ├── channels
│   ├── controllers
│   ├── endpoint.ex
│   ├── gettext.ex
│   ├── router.ex
│   ├── templates
│   └── views
└── hello_phx_web.ex

assets: 前端相關的檔案

這個 assets 資料夾也符合其它 MVC 框架的慣例,用來放圖檔、CSS 樣式表及 JavaScript 原始碼。Phoenix 用了一個純 JavaScript 的打包程式,叫做 Brunch。雖然社區裡有非常多的聲音希望能跟隨潮流換成 Webpack,但是核心團隊依然在觀望中,理由是如果用 WebPack 打包,要做出期望的分檔方式,設定部份會太過複雜。只要用 “Phoenix webpack” 關鍵字搜尋一下,就會找到很多如何改用 webpack 的教學與工具。當然還是希望 JavaScript 社群能儘快在打包工具上找到共識啦。已經好一陣子了,不要再一直發明新的了。

assets
├── brunch-config.js
├── css
├── js
├── node_modules
├── package-lock.json
├── package.json
├── static
└── vendor

priv:Erlang 的悠久傳統

priv 資料夾是從 Erlang 時期就開始的傳統。用來放置不是主要程式,但是應用程式會使用到的附加檔案,例如 C 語言的可執行檔等。而在 Phoenix 專案中,預設放了三種東西在裡面,分別是用來放多語系翻譯檔 (i18n) 的 gettext、放資料庫遷移定義檔案 (migration file) 的 repo。而圖檔、打包好的 JavaScript 及 CSS 則會放在 static 裡。

Note: 這個資料夾為什麼叫 priv 已不可考

priv
├── gettext
│   ├── en
│   └── errors.pot
├── repo
│   ├── migrations
│   └── seeds.exs
└── static
    ├── css
    ├── favicon.ico
    ├── images
    ├── js
    └── robots.txt

還沒提到的資料夾

其它的資料夾,依然按照 elixir 專案的慣例來歸類檔案:

  • config: 各種設定檔
  • deps:專案依賴的函式庫原始碼
  • test:測試相關檔案
  • _build:編譯後, Erlang VM 執行用的 .beam

中場小結

這些資料夾的用途都不需要強記,當遇到不知道這種檔案該放哪時,再回來查就好。而你只要記得兩件事:Phoenix 專案,就只是個一般的 elixir 專案,主要的程式都放 lib 以及 與 web 相關的檔案是放在 lib/專案名稱_web 就可以了。

快速生成 CRUD 模版

又到了把手弄髒的時間了。在專案的目錄下,輸入底下這行指令:

mix phx.gen.html Blog Post posts title:string content:string draft:boolean

我們分段來看:

  1. mix phx.gen.html 是用來生成完整 HTML CRUD 模版的指令。phx.gen 還可以生成很多不同的東西,如果有時間再來介紹。這個指令,需要四個參數。
  2. 第一個參數 Blog 稱為 context,是用來分類應用程式中不同的功能區塊,稍後將會詳述。
  3. 第二個參數 Post 稱為 schema name,一般來說是單數。這個 schema 與 Rails 的 schema.rb 完全不是同一回事,反而比較接近 Rails 的 model。這個參數會用來作為 schema 的模組名稱。
  4. 第三個參數 posts 稱為 resource name,一般來說是 schema name 的複數型。會用來做為 URL routes 的 resource 名稱,及資料庫的 table 名。不像 Rails 只要輸入單數的 model 名稱,就會幫你找到英文的複數型,Phoenix 要你自己決定這個名稱應該是什麼。
  5. 第四個參數是個 keyword list,由多個 欄位名:資料庫型別 組成。

按下 [Enter],就可以看到 Phoenix 又生了一堆檔案,並且提示你下一步要做什麼:

https://ithelp.ithome.com.tw/upload/images/20180106/20103390jpqB4lsZ2a.png

將 resource 加入 router 檔案

依 RESTful 的定義,每一組功能稱為一個 resource,現在我們要在路由檔案中,加入這組功能的進入點。用編輯器打開 lib/hello_phx_web/router.exs (這是跟網路相關的部份,所以在 hello_phx_web 裡),加入剛剛畫面中的 “resource...” 那行,像是這樣:

#...blah

  scope "/", HelloPhxWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    resources "/posts", PostController # <=== 加進這行
  end

# ...blah

遷移資料庫

我們的功能需要新增資料庫的 table 來存放資料。雖然剛剛 phx.gen.html 會幫我們生成一張告訴資料庫要增加哪些東西的定義檔,稱為 migration file,但是我們還是要下指令執行變更資料庫這個動作。

回到 shell,輸入:

mix ecto.migrate

畫面會顯示資料庫新增了一張資料表。

https://ithelp.ithome.com.tw/upload/images/20180106/20103390ldgs4ZU52D.png

試試看新功能吧

用瀏覽器打開 http://localhost:4000/posts ,就可以新增 (Create)、查詢 (Read)、修改 (Update) 及刪除 (Delete) post 了,這四個功能合稱 CRUD。如果無法正常開啟或使用,先確定你的 mix phx.server 是否還在執行喔。

https://ithelp.ithome.com.tw/upload/images/20180106/20103390wzp0L0PlIj.png

那個 Context 是怎麼一回事

傳統 MVC 框架的慣例,是用 View、Controll 及 Model 資料夾來依角色分類檔案。但 Java 圈的名人 Uncle Bob 在 2012 年就寫了一篇文章叫 Screaming Architecture,大義是這樣:

應用程式的資料夾結構,應該用於強調其領域知識,該是大聲的告訴讀程式的人說:我是一個做訂閱影音的網站 (會有 /Video/Subscription)、我是一個做庫存管理的網站 (會有 /Products/Department)。

而不是單純的說:我是 ASP 做出來的網站我是 Rails 做出來的網站

Phoenix 自 1.3 版起實作了這個概念。把與 web 相關的部份,自 endpoint 到 controller,放在 lib/專案名稱_web 下。而與商業邏輯相關的部份,則是把各個 schema 放在 lib/專案名稱 的 context 資料夾下。每個 context,應該包含多個與該功能區塊緊密相關的 schema。

lib
├── hello_phx
│   ├── application.ex
│   ├── blog
│   │   ├── blog.ex
│   │   └── post.ex
│   └── repo.ex
├── hello_phx.ex
├── hello_phx_web
│   ├── channels
│   ├── controllers
│   │   ├── page_controller.ex
│   │   └── post_controller.ex
│   ├── endpoint.ex
│   ├── gettext.ex
│   ├── router.ex
│   ├── templates
│   └── views
└── hello_phx_web.ex

重點回顧

  • Phoenix 專案就是一般的 elixir 專案
  • mix phx.gen.html 生成 CRUD 模版
  • 生成後,要加 resource 進 router.ex、及執行資料庫遷移
  • 與網頁相關的程式,會放在 /lib/專案名稱_web
  • 商業邏輯則會放在 /lib/專案名稱 下,並依 context 分類

下一篇將討論 Endpoint 及 Plug。
Happy hacking! 明天見。


上一篇
那種會從灰燼裡復活的鳥: Phoenix
下一篇
Plug 及 Endpoint
系列文
函數式編程: 從 Elixir & Phoenix 入門。31

1 則留言

0
Bater
iT邦新手 5 級 ‧ 2018-01-11 16:09:36

我有問題(舉手)!如果我的router有設定scope,例如:

  scope "/admin", HelloPhxWeb do
    # ...
  end

那我在使用phx.gen.html的時候,要怎麼讓controller與view的部分也產生在相對應的路徑下?

taiansu iT邦新手 5 級‧ 2018-01-11 18:01:56 檢舉

mix phx.gen.html Admin User users --web Admin

https://github.com/phoenixframework/phoenix/blob/v1.3.0/lib/mix/tasks/phx.gen.html.ex#L54

其實這有寫但是還沒發 XD

Bater iT邦新手 5 級‧ 2018-01-11 20:34:49 檢舉

感謝!

我要留言

立即登入留言