整合 CI
從開始拿到程式後,花了五天在調整程式,目的就是為了今天!
Faker 提供的 Provider 非常多,除了預設之外,還有不同語系實作。
不過我們先來解決昨天的疑惑:這些 Provider 到底是如何使用 Generator
的?
搜尋了一下,會發現 Provider 大部分會使用 Generator
的 parse()
方法,而 Day 7 有提到,它的本質是 format()
。換句話說,Provider 會經由 Generator
來存取其他 Provider。
這個設計有點類似 Mediator Pattern,它們的關係如下:
@startuml |
Provider 要使用 Generator
當 Mediator 時,必須小心循環呼叫的問題,比方說 A Provider 呼叫 B Provider,而 B Provider 又要呼叫 A Provider。
Day 8 提到 Provider\Base
類別提供非常多基本亂數取樣方法,今天就派得上用場了!
Provider 最常用到的肯定是 randomElement()
,不同領域的 Provider 通常都會有自己的口袋名單,要從口袋名單裡隨便選一個,當然就是用它。numberBetween()
也是個常用到方法,因為不同領域的 Provider 值域都不大一樣。
如果有仔細觀察,會發現 Provider 非常多方法都有用到 Provider\Base
的亂數取樣方法。
Provider 語系擴充的設計是,繼承的時候覆寫對應的口袋名單、樣版或是產生的方法即可。
比方說 Provider\zh_TW\Person
類別,它繼承自 Provider\Person
,覆寫 $maleNameFormats
與 $femaleNameFormats
樣版,因為台灣名字的顯示慣例先姓後名:
protected static $maleNameFormats = array( |
口袋名單 $lastName
、$characterMale
與 $characterFemale
等,當然也會覆寫:
protected static $lastName = array( |
產生名字的方法也覆寫了,因為台灣大部分是一個姓配兩個名:
public static function firstNameMale() |
Provider]\Person
的 name()
方法並沒有被覆寫,原始碼如下:
public function name($gender = null) |
因此,當我們使用 zh_TW
語系,在呼叫 $generator->name
的時候,事實上它會先跑 Provider\Person
的 name()
方法,然後使用 $generator->parse()
把「覆寫的樣板」放進去,接著再把「覆寫的口袋名單」與呼叫「覆寫產生名的方法」,最後組合出回傳結果。
這種覆寫的方法也是 Laravel 常見擴充寫法。
了解它的設計後,後面要擴充自己的假資料清單就會非常容易了。
Faker 主框架差不多介紹完了,明天來試試自定義 Provider。
重構的過程中,最重要卻也最麻煩的流程,就是驗證。我們必須確保重構的過程不會把原本的功能改壞,只能靠不斷的測試,驗證功能沒壞,才能繼續下一步。
如果有認真看前兩天的文章,應該會發現一個很奇怪的事:
Factory
產生 Generator
物件時,會使用 addProvider()
把 Provider
加入 Generator
Provider\Base
建構子的依賴是 Generator
這造成了一個循環引用的關係
@startuml |
通常這樣的關係是不利於維護的,容易修改一個地方,而讓很多地方同時受影響。不過我們還是先一起來看看它到底在做什麼吧!
關於 Generator 使用 Provider 的部分,Day 7 已經有說明,不再贅述。
打開 IDE 搜尋一下,會發現 Provider\Base
有 3 個方法會用到 Generator
:
optional()
unique()
valid()
與其他產生假資料的方法不同的是:這三個方法會回傳 Generator
物件或是另外三種 Generator 物件:
先來了解這三個方法在做什麼:
optional()
方法需要給一個權重值(百分比)與一個預設值(default),它會依權重值來隨機決定要回傳 DefaultGenerator
還是正常的 Generator
。
先來看個簡單的範例:
$generator = Faker\Factory::create('en_US'); |
輸出效果:
預設值 |
其中權重 0% 指的是一定會回傳預設值,而 100% 則會回傳 Generator
,因此有產生隨機的名字,中間的機率則各是 50%。
DefaultGenerator
的設計是:先把預設值存下來之後,在由 __get()
,__call()
回傳回去。這樣使用回傳的 Generator 時,我們也不會發現它是 Generator
還是 DefaultGenerator
,蠻有趣的。
連續產生一百組假資料,有可能會出現一樣的資料。有時會不允許資料裡有重覆的值,比方說資料庫欄位被設定成 UNIQUE
,如果 insert 遇到重覆的值就會發生錯誤。
unique()
能解決這個問題,它會回傳 UniqueGenerator
。同一個 UniqueGenerator
產生的假資料,可以確保每次都會不一樣。
它的設計是 Generator
任務不變,而 UniqueGenerator
的任務是記錄已產生過的資料,如有重覆,會再重新跟 Generator
要資料。
從 UniqueGenerator
呼叫 Generator
的方式如下:
public function __get($attribute) |
一樣舉 name
屬性與 name()
方法為例,從上面 UniqueGenerator
的程式可以得知,最後呼叫 Generator
的結果如下:
$generator = Faker\Factory::create('en_US'); |
這裡先提示一下:$generator->name
與 $generator->name()
的結果是一樣的。Day 7 介紹 Generator
曾有翻過原始碼,知道 __get()
最終跟 __call()
一樣,是呼叫 format()
因此這個結果可以得知:雖然包裝有做處理,但 UniqueGenerator
的介面接到 Generator 的介面是一模一樣的。這正是標準的 Proxy Pattern。
@startuml |
如果昨天有翻程式碼的話應該也會發現另一件事:Provider 提供的方法如果有參數,幾乎都會有預設值,即使是看起來沒用處的
[a, b, c]
,目前也只有發現,還不清楚目的。
如果需要產出名字為 M
開頭的假資料的話,那麼使用 valid()
會是最好的選擇!
與 UniqueGenerator
在驗證假資料不重覆很類似,而 valid()
的驗證方法使用 Closure 自定義方法,做法可以更為廣泛。
也因為很類似,所以設計也很像 UniqueGenerator
,一樣透過 __get()
與 __call()
轉接到 Generator
,所以也是使用 Proxy Pattern。
Provider\Base
本身並沒有使用到 Generator
的功能,只是單純的把它包裝後再回傳,這樣比較不容易有改 A 壞 B 的情況發生。
事實上,用最頻繁的是其他的 Provider。我們明天再來看看這些 Provider 的細節吧!
昨天導入 Composer 主要是為了今天要整合 Laravel 前的準備。
昨天有提到 Generator
有 addProvider()
方法,可以把各式各樣的 Provider 加入 Generator。而也有提到 Generator 的屬性或方法,會轉接到 Provider 提供的方法。
雖然沒有硬性規定,不過它的 Provider 都有繼承自 Provider\Base
的基礎類別,這個類別定義了很多產生假資料的基本方法,讓我們一起來看看。
基本隨機取樣的方法
$generator = new Faker\Generator(); |
輸出效果如下:
2 |
這些是最基本的隨機取樣方法,後面提到的其他 Provider 會依賴這些方法來取得隨機樣本。
基本的洗亂方法:
$generator = new Faker\Generator(); |
輸出效果如下:
array ( |
特別的是,它有一些方法,可以把字串樣版裡的 wildcard 使用亂數取代,如:
$generator = new Faker\Generator(); |
輸出效果如下:
取代範例:833 554 ??? *** |
規則是:
#
會用 randomDigit()
的結果取代%
會用 randomDigitNot()
的結果取代?
會用 randomLetter()
的結果取代*
會用 randomAscii()
的結果取代不同的方法會取代不同的 wildcard,這是為了保留在不同場合使用適合方法的彈性。
numerify()
只會取代 #
和 %
lexify()
只會取代 ?
asciify()
只會取代 *
bothify()
會全取代這個產生器很有趣,通常我們會使用正則來檢驗 Email 是否是正常的,如:
preg_match('/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}/', '[email protected]') |
而 regexify()
這個方法是反過來,用正則來產生符合規則的假資料:
$regex = '/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}/'; |
輸出效果:
d%363@m.ncqs |
注意:官方 doc block 有提醒,這個方法會「非常地」慢。
今天先介紹 Provider 基本產假資料的方法,未來介紹其他 Provider 都會使用到這些方法來產生更多樣化的假資料。