# 前言
紀錄 Laravel Scramble 的特殊用法
Laravel Scramble 是一款針對 Laravel 的 API 文件套件,主張 code as document,盡可能的減少 comment 的目標下,產出 API 文件
Scramble 的版本為 v0.8.6
# 情境
# 多份 API 文件
原先公司的產品是單純的 前端 + 後端,也就是 後端 的 API 只單純為產品的 前端 服務,在這個情境下,Laravel Scramble 官方文件上提供的用法已經可以很好的解決問題
後來需求調整,後端 除了要 繼續提供給前端的 API 之外,還要 提供 API 給外部服務 使用。簡單理解,Google 除了要提供 API 給其前端使用外,還需要提供 API 給外部服務使用,在這個情境下,有兩種選擇
- 整份 code 都切分開來,內部歸內部,外部歸外部,這樣自然可以一個專案一份文件。不過這樣做有其成本,這也不在本篇的討論範圍內
- 文件工具提供分開的 API 文件,可通過不同的 url 來存取不同的文件
# 多版本 API 文件
當產品內有 Mobile APP 時,就會遇到版本問題。原因是 Mobile APP 在版本更新時,是需要先上架一個版本讓 平台 (iOS / Android) 審核,審核通過後,才能上架
而在審核的過程中,舊版本需要持續地提供服務,而審核通過後,新版本需要立即提供服務,於此同時舊版本仍需繼續服務,直到所有使用者皆已升級到新版
所以這時便會需要 同時兩個版本 的 API 存在,自然也會需要 兩份 API 文件
全部都放在同一份文件不行嗎? 自然是不行的,試想,Google 提供的外部 API 如果跟他們自己內用的放在一起,是不是很奇怪?這裡頭也有資安上的考量。
# 問題點
如上所述,Laravel Scramble 並沒有提供多版本多文件的 solution
重新歸納一下問題 & 需求
- 可以在專案定義不同版本的文件
- 可以在專案定義不同的 URL,分別提供不同 scope 的 API 文件
# Solution
如上所述,Laravel Scramble 在文件上並沒有提供這樣的 solution,所以要嘛換一套工具,要嘛就只能追一下其 source code,看看能否在現有提供的 method 上,魔改一下,在最低魔改成本的目標下,達到我們的需求
# Source code 研究
原生用法,會通過 URL baseUrl/docs/api
來進到文件頁面,我們以此為入口點,既然有 URL,那勢必有定義相對應的 route
果不其然,在 Laravel Scramble 的 vendor dir path vendor/dedoc/scramble
,在 routes/web.php
中有兩支定義好的 route
GET docs/api.json
View docs/api
baseUrl/docs/api
是由 View docs/api
所提供,所以先從這裡進去看看。這一個 View 的 Blade file,定義了前端 UI,這邊可以看到兩個重點
- 定義在
config/scramble.php
當中的參數,有部分會影響到 UI 顯示 - 最重要的資料來源,來自
route('scramble.docs.index')
回到 vendor/dedoc/scramble
一看,route('scramble.docs.index')
不就是 GET docs/api.json
的 route name 嗎?
到這裡,其實已經獲得非常有用的資訊了。整個資料流如下圖
繼續追一下 GET docs/api.json
這支產生資料的 API。code 的細節這邊不贅述,但可以很容易地看到,Scramble 是基於 config/scramble.php
的參數來產生 JSON data
至此,已經有了要魔改的關鍵所需資訊了
- 既然 UI & Data 都是通過
config/scramble.php
而產生的,那只要在下面兩個時機點去 overwriteconfig/scramble.php
,那自然可以 改變 UI & 改變 JSON Data source - (1) 取得 View Blade 時
- (2) 取得 JSON data source 時
- 既然可以通過
View docs/api
來取得 API docs page,那也可以自行定義一個 web route 來 render 相同的 Blade
# 實作
首先修改取得 Docs data 的部分。概念上很簡單,如下:
- 定義一個新的 API route,這個 route 可以帶入 version & scope
- route 的實作 controller 當中,根據 version & scope 來 overwrite
config/scramble.php
,使用Config::set()
- 完成 config overwrite 之後,直接 return
Dedoc\Scramble\Generator
,他會去抓當前的config/scramble.php
的參數,產出相對應的 JSON
完成以上的實作後,我們就可以透過 帶入不同參數 的 API 來取得 render API docs 的 JSON data,接下來,就可以修改 render view 的部分
在 vendor/dedoc/scramble/routes/web.php
中可以知道,用來 render 的 Blade file 放在 vendor/dedoc/scramble/resources/views/docs.blade.php
。copy 這一個 file,然後放到 resources/views/scramble.blade.php
,這樣就可以自定義 Blade 裡頭的參數
只需要修改 apiDescriptionUrl 這一個參數,預設的話是讀 config/scramble.php
當中的 route('scramble.docs.index')
,但我們需要讓 render file 去讀我們另外定義的 URL,所以可以把這一個參數的值改成 帶入 parameter
<?php
// default
apiDescriptionUrl="{{ route('scramble.docs.index') }}"
// modified
apiDescriptionUrl="{{ $routeUrl }}"
然後我們只要先在外面的 controller 把 $routeUrl 產出,在 pass 給 Blade file 即可。產生 route url 的程式碼如下
<?php
$routeUrl = route('scramble.api.docs.json', $query);
scramble.api.docs.json
為上面定義,用來取得 API Docs data 的 API route name。通過帶入 query 的方式,我們可以取得指定 version or scope 的 API Docs data,再把這個 data 給 Blade file,最終 render 出正確的 API 文件,如下流程圖