跳到主要内容

Synthesizers

由于 Livewire 组件在请求之间会被脱水(序列化)为 JSON,然后水合(反序列化)回 PHP 组件,它们的属性需要是 JSON 可序列化的。

原生 PHP 可以轻松地将大多数原始值序列化为 JSON。然而,为了让 Livewire 组件支持更复杂的属性类型(如模型、集合、Carbon 实例和 Stringable),需要一个更强大的系统。

因此,Livewire 提供了一个称为 "Synthesizers" 的扩展点,允许用户支持他们想要的任何自定义属性类型。

确保先理解水合概念

在使用 Synthesizer 之前,完全理解 Livewire 的水合系统会很有帮助。你可以通过阅读水合文档了解更多。

理解 Synthesizer

在探索创建自定义 Synthesizer 之前,让我们先看看 Livewire 用于支持 Laravel Stringable 的内部 Synthesizer。

假设你的应用包含以下 CreatePost 组件:

class CreatePost extends Component
{
public $title = '';
}

在请求之间,Livewire 可能会将此组件的状态序列化为如下 JSON 对象:

state: { title: '' },

现在,考虑一个更高级的示例,其中 $title 属性值是一个 Stringable 而不是普通字符串:

class CreatePost extends Component
{
public $title = '';

public function mount()
{
$this->title = str($this->title);
}
}

表示此组件状态的脱水 JSON 现在包含一个元数据元组,而不是普通的空字符串:

state: { title: ['', { s: 'str' }] },

Livewire 现在可以使用这个元组在下一个请求中将 $title 属性水合回 Stringable。

现在你已经从外部看到了 Synthesizer 的效果,以下是 Livewire 内部 Stringable Synth 的实际源代码:

use Illuminate\Support\Stringable;

class StringableSynth extends Synth
{
public static $key = 'str';

public static function match($target)
{
return $target instanceof Stringable;
}

public function dehydrate($target)
{
return [$target->__toString(), []];
}

public function hydrate($value)
{
return str($value);
}
}

让我们逐一解释。

首先是 $key 属性:

public static $key = 'str';

每个 Synth 必须包含一个静态的 $key 属性,Livewire 用来将元数据元组(如 ['', { s: 'str' }])转换回 Stringable。你可能已经注意到,每个元数据元组都有一个 s 键引用这个 key。

反过来,当 Livewire 正在脱水一个属性时,它将使用 Synth 的静态 match() 函数来识别此特定的 Synthesizer 是否是脱水当前属性的合适候选($target 是属性的当前值):

public static function match($target)
{
return $target instanceof Stringable;
}

如果 match() 返回 true,dehydrate() 方法将被调用,接收属性的 PHP 值作为输入,并返回 JSON 可序列化的元数据元组:

public function dehydrate($target)
{
return [$target->__toString(), []];
}

现在,在下一次请求的开始,当此 Synthesizer 被元组中的 { s: 'str' } 键匹配后,hydrate() 方法将被调用,接收属性的原始 JSON 表示形式,并期望返回分配给属性的完整 PHP 兼容值。

public function hydrate($value)
{
return str($value);
}

注册自定义 Synthesizer

为了演示你如何编写自己的 Synthesizer 来支持自定义属性,我们将以下面的 UpdateProperty 组件为例:

class UpdateProperty extends Component
{
public Address $address;

public function mount()
{
$this->address = new Address();
}
}

以下是 Address 类的源代码:

namespace App\Dtos\Address;

class Address
{
public $street = '';
public $city = '';
public $state = '';
public $zip = '';
}

要支持 Address 类型的属性,我们可以使用以下 Synthesizer:

use App\Dtos\Address;

class AddressSynth extends Synth
{
public static $key = 'address';

public static function match($target)
{
return $target instanceof Address;
}

public function dehydrate($target)
{
return [[
'street' => $target->street,
'city' => $target->city,
'state' => $target->state,
'zip' => $target->zip,
], []];
}

public function hydrate($value)
{
$instance = new Address;

$instance->street = $value['street'];
$instance->city = $value['city'];
$instance->state = $value['state'];
$instance->zip = $value['zip'];

return $instance;
}
}

要使其在应用中全局可用,你可以使用 Livewire 的 propertySynthesizer 方法从服务提供者的 boot 方法中注册 Synthesizer:

class AppServiceProvider extends ServiceProvider
{
/**
* 引导任何应用服务。
*/
public function boot(): void
{
Livewire::propertySynthesizer(AddressSynth::class);
}
}

支持数据绑定

使用上述 UpdateProperty 示例,你可能希望支持 wire:model 直接绑定到 Address 对象的属性。Synthesizer 允许你使用 get()set() 方法支持此功能:

use App\Dtos\Address;

class AddressSynth extends Synth
{
public static $key = 'address';

public static function match($target)
{
return $target instanceof Address;
}

public function dehydrate($target)
{
return [[
'street' => $target->street,
'city' => $target->city,
'state' => $target->state,
'zip' => $target->zip,
], []];
}

public function hydrate($value)
{
$instance = new Address;

$instance->street = $value['street'];
$instance->city = $value['city'];
$instance->state = $value['state'];
$instance->zip = $value['zip'];

return $instance;
}

public function get(&$target, $key) // [tl! highlight:8]
{
return $target->{$key};
}

public function set(&$target, $key, $value)
{
$target->{$key} = $value;
}
}