Faker(5)--Provider 們不要起爭議

Faker 提供的 Provider 非常多,除了預設之外,還有不同語系實作。

不過我們先來解決昨天的疑惑:這些 Provider 到底是如何使用 Generator 的?

Magic 的中介層設計

搜尋了一下,會發現 Provider 大部分會使用 Generatorparse() 方法,而 Day 7 有提到,它的本質是 format()。換句話說,Provider 會經由 Generator 來存取其他 Provider。

這個設計有點類似 Mediator Pattern,它們的關係如下:

@startuml
Class Client
Class Base {
# generator: Generator
}
Class Provider1
Class Provider2
Class Generator
Client -> Generator
Base <|-- Provider1
Base <|-- Provider2
Generator <- Base : Midiator
Generator -> Provider1
Generator ---> Provider2
@enduml

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}}{{firstNameMale}}',
);

protected static $femaleNameFormats = array(
'{{lastName}}{{firstNameFemale}}',
);

口袋名單 $lastName$characterMale$characterFemale 等,當然也會覆寫:

protected static $lastName = array(
// ...
);

protected static $characterMale = array(
// ...
);

protected static $characterFemale = array(
// ...
);

產生名字的方法也覆寫了,因為台灣大部分是一個姓配兩個名:

public static function firstNameMale()
{
return static::randomName(static::$characterMale, mt_rand(1, 2));
}

public static function firstNameFemale()
{
return static::randomName(static::$characterFemale, mt_rand(1, 2));
}

Provider]\Personname() 方法並沒有被覆寫,原始碼如下:

public function name($gender = null)
{
if ($gender === static::GENDER_MALE) {
$format = static::randomElement(static::$maleNameFormats);
} elseif ($gender === static::GENDER_FEMALE) {
$format = static::randomElement(static::$femaleNameFormats);
} else {
$format = static::randomElement(array_merge(static::$maleNameFormats, static::$femaleNameFormats));
}

return $this->generator->parse($format);
}

因此,當我們使用 zh_TW 語系,在呼叫 $generator->name 的時候,事實上它會先跑 Provider\Personname() 方法,然後使用 $generator->parse() 把「覆寫的樣板」放進去,接著再把「覆寫的口袋名單」與呼叫「覆寫產生名的方法」,最後組合出回傳結果。

這種覆寫的方法也是 Laravel 常見擴充寫法。

了解它的設計後,後面要擴充自己的假資料清單就會非常容易了。


Faker 主框架差不多介紹完了,明天來試試自定義 Provider。

參考資料