這篇文章是針對 Dan Harrin 在 laracasts 的影片進行的設計範例和理解筆記,其設計哲學貫穿整個 filament package,理解其原理可以為後續開發思路打下基礎。
Filament/livewire 設計概念是以 component 為基礎,透過對應設計達到 component based design,意思就是透過 component 完成功能。
設計 TextInput Component
namespace App\Components;
class TextInput
{
public function __construct(
protected string $name
) {
}
// 透過靜態類別方法來創建實體,避免要寫 new TextInput()
public static function make(string $name): self
{
// 由靜態類 new instance
return new self($name);
}
// render view component
public function render(): View
{
return view('components.text-input');
}
接下來創建其對應的 html component,components/text-input.blade.php
:
<input type="text" />
在主要 layout 當中使用其組件,這邊我的主 layout 為 demo.blade.php
:
{!! $input->render()->render() !!}
最後在呼叫主 layout 時注入 input:
Route::get('/', function () {
$input = \App\Components\TextInput::make('Email');
return view('demo', [
'input' => $input
]);
});
這樣子就是一個基礎組件設計,可以很方便的在其他地方使用,接下來進行語句優化,這邊會用到 Laravel 的一個特性 contract Htmlable
,這個介面一旦實現他後他會將其轉換為 html 格式的字串,這樣在呼叫時就不需要再寫一個 render()
函數。
class TextInput implements Htmlable
{
public function render(): View
{
return view('components.text-input');
}
public function toHtml(): string
{
// $this->render() <- 呼叫原有 render()
// ->render() <- 在將其渲染成 html
return $this->render()->render();
}
接著修改 demo.blade.php
並且在呼叫主 layout 時注入 input:
{{ $input }}
添加方法
在 filament 當中有很多方便的 API 可以進行調用,這邊以 label 為基礎進行添加:
protected string $label;
public function label(string $label): self
{
$this->label = $label;
return $this;
}
public function render(): View
{
return view('components.text-input', [
'label' => $this->label,
]);
}
//web.php
$input = \App\Components\TextInput::make('Email')
->label('Email Address');
這樣子運作沒問題,但 label
就會是必須項目,所以這邊當沒有呼叫 label
則以 $name 作為預設:
public function getLabel():string
{
return $this->label ?? str($this->name)->title();
}
public function render(): View
{
return view('components.text-input',[
'label' => $this->getLabel(),
]);
}
方法反射
當添加 `label 時我們就必須一直修改 render() 當中的 data passed,有沒有辦法可以讓他更加方便,這邊就會需要透過反射方法進行映射到 blade:
public function extractPublicMethods(): array
{
// 反射
$reflection = new \ReflectionClass($this);
$methods = [];
// 取得所有 public 方法
foreach ($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
// 取得方法名,並綁定方法
$methods[$method->getName()] = \Closure::fromCallable([$this, $method->getName()]);
}
return $methods;
}
public function render(): View
{
return view('components.text-input',$this->extractPublicMethods());
}
// text-input.blade.php
<label>
<span>
{{ $getLabel() }}
</span>
<input type="text" />
</label>
非常佩服 Dan Harrin 的設計,其組件和邏輯整合度非常高,並且很方便的以組件型態去調用,可以很方便的在其他地方使用。