Skip to content

Synthesizers

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

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

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

首先确保您了解 hydration

在使用 Synthesizers 之前,完全了解 Livewire 的 hydration 系统会很有帮助。您可以通过阅读 hydration 文档 了解更多。

理解 Synthesizers

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

假设您的应用程序包含以下 CreatePost 组件:

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

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

js
state: { title: '' },

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

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

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

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

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

Livewire 现在可以使用此元组在下一次请求时将 $title 属性水合回 stringable。

现在您已经看到了 Synthesizers 的外部效果,以下是 Livewire 内部 stringable synth 的实际源代码:

php
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 属性:

php
public static $key = 'str';

每个 synth 必须包含一个静态 $key 属性,Livewire 使用它将像 ['', { s: 'str' }] 这样的元数据元组转换回 stringable。您可能注意到,每个元数据元组都有一个引用此 key 的 s 键。

相反,当 Livewire 脱水一个属性时,它将使用 synth 的静态 match() 函数来识别这个特定的 Synthesizer 是否是脱水当前属性的好候选($target 是属性的当前值):

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

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

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

现在,在下一次请求开始时,在此 Synthesizer 通过元组中的 { s: 'str' } key 被匹配后,hydrate() 方法将被调用并传递属性的原始 JSON 表示,期望它返回完整的 PHP 兼容值以分配给属性。

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

注册自定义 Synthesizer

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

php
class UpdateProperty extends Component
{
    public Address $address;

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

以下是 Address 类的源代码:

php
namespace App\Dtos\Address;

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

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

php
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 方法从您的 service provider boot 方法中注册 synthesizer:

php
class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Livewire::propertySynthesizer(AddressSynth::class);
    }
}

支持数据绑定

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

php
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;
    }
}