本篇將介紹 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 專案幾乎相同,只多了 assets
跟 priv
兩個資料夾而己。
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
裡 就可以了。
又到了把手弄髒的時間了。在專案的目錄下,輸入底下這行指令:
mix phx.gen.html Blog Post posts title:string content:string draft:boolean
我們分段來看:
mix phx.gen.html
是用來生成完整 HTML CRUD 模版的指令。phx.gen
還可以生成很多不同的東西,如果有時間再來介紹。這個指令,需要四個參數。Blog
稱為 context,是用來分類應用程式中不同的功能區塊,稍後將會詳述。Post
稱為 schema name,一般來說是單數。這個 schema 與 Rails 的 schema.rb 完全不是同一回事,反而比較接近 Rails 的 model。這個參數會用來作為 schema 的模組名稱。posts
稱為 resource name,一般來說是 schema name 的複數型。會用來做為 URL routes 的 resource 名稱,及資料庫的 table 名。不像 Rails 只要輸入單數的 model 名稱,就會幫你找到英文的複數型,Phoenix 要你自己決定這個名稱應該是什麼。欄位名:資料庫型別
組成。按下 [Enter],就可以看到 Phoenix 又生了一堆檔案,並且提示你下一步要做什麼:
依 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
畫面會顯示資料庫新增了一張資料表。
用瀏覽器打開 http://localhost:4000/posts ,就可以新增 (Create)、查詢 (Read)、修改 (Update) 及刪除 (Delete) post 了,這四個功能合稱 CRUD。如果無法正常開啟或使用,先確定你的 mix phx.server
是否還在執行喔。
傳統 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
mix phx.gen.html
生成 CRUD 模版router.ex
、及執行資料庫遷移/lib/專案名稱_web
下/lib/專案名稱
下,並依 context 分類下一篇將討論 Endpoint 及 Plug。
Happy hacking! 明天見。
我有問題(舉手)!如果我的router有設定scope,例如:
scope "/admin", HelloPhxWeb do
# ...
end
那我在使用phx.gen.html
的時候,要怎麼讓controller與view的部分也產生在相對應的路徑下?
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
感謝!