diff --git a/src/Casts/MoneyCast.php b/src/Casts/MoneyCast.php new file mode 100644 index 0000000..e807f09 --- /dev/null +++ b/src/Casts/MoneyCast.php @@ -0,0 +1,31 @@ + $attributes + */ + public function get(Model $model, string $key, mixed $value, array $attributes): Money + { + return new Money($value, new Currency('USD')); + } + + /** + * Prepare the given value for storage. + * + * @param array $attributes + */ + public function set(Model $model, string $key, mixed $value, array $attributes): mixed + { + return $value; + } +} diff --git a/src/Casts/MoneySynth.php b/src/Casts/MoneySynth.php new file mode 100644 index 0000000..ee10026 --- /dev/null +++ b/src/Casts/MoneySynth.php @@ -0,0 +1,37 @@ + $target->getAmount(), + 'currency' => $target->getCurrency(), + ], []]; + } + + public function hydrate($value) + { + if ($value === null) { + return null; + } + + return new Money( + $value['amount'], + new Currency($value['currency']) + ); + } +} diff --git a/src/FilamentMoneyFieldServiceProvider.php b/src/FilamentMoneyFieldServiceProvider.php index 611321e..96c8482 100644 --- a/src/FilamentMoneyFieldServiceProvider.php +++ b/src/FilamentMoneyFieldServiceProvider.php @@ -2,6 +2,8 @@ namespace Pelmered\FilamentMoneyField; +use Livewire\Livewire; +use Pelmered\FilamentMoneyField\Casts\MoneySynth; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -20,5 +22,7 @@ public function boot(): void $this->publishes([ __DIR__.'/../config/filament-money-field.php' => config_path('filament-money-field.php'), ], 'config'); + + Livewire::propertySynthesizer(MoneySynth::class); } } diff --git a/src/Forms/Components/MoneyInput.php b/src/Forms/Components/MoneyInput.php index dea45cd..e65cb45 100644 --- a/src/Forms/Components/MoneyInput.php +++ b/src/Forms/Components/MoneyInput.php @@ -4,6 +4,7 @@ use Filament\Forms\Components\TextInput; use Filament\Support\RawJs; +use Money\Money; use Pelmered\FilamentMoneyField\Concerns\HasMoneyAttributes; use Pelmered\FilamentMoneyField\Forms\Rules\MaxValueRule; use Pelmered\FilamentMoneyField\Forms\Rules\MinValueRule; @@ -40,7 +41,12 @@ protected function setUp(): void $this->dehydrateStateUsing(function (MoneyInput $component, $state): ?string { $currency = $component->getCurrency(); - $state = MoneyFormatter::parseDecimal($state, $currency, $component->getLocale(), $this->getDecimals()); + + if ($state instanceof Money) { + return MoneyFormatter::parseDecimal($state->getAmount(), $state->getCurrency(), $component->getLocale(), $this->getDecimals()); + } + + $state = MoneyFormatter::parseDecimal($state, $currency, $component->getLocale(), $this->getDecimals()); if (! is_numeric($state)) { return null; diff --git a/src/MoneyFormatter/MoneyFormatter.php b/src/MoneyFormatter/MoneyFormatter.php index 3a9b752..9c13d20 100644 --- a/src/MoneyFormatter/MoneyFormatter.php +++ b/src/MoneyFormatter/MoneyFormatter.php @@ -33,11 +33,16 @@ public static function format( } public static function formatAsDecimal( - null|int|string $value, + null|int|string|Money $value, Currency $currency, string $locale, int $decimals = 2, ): string { + if ($value instanceof Money) { + $currency = $value->getCurrency(); + $value = $value->getAmount(); + } + return static::format($value, $currency, $locale, NumberFormatter::DECIMAL, $decimals); } diff --git a/tests/Components/FormTestComponent.php b/tests/Components/FormTestComponent.php index bef6154..256a532 100644 --- a/tests/Components/FormTestComponent.php +++ b/tests/Components/FormTestComponent.php @@ -28,6 +28,9 @@ public function form(Form $form): Form MoneyInput::make('price') ->minValue(100) ->maxValue(1000), + MoneyInput::make('price_cast') + ->minValue(100) + ->maxValue(1000), ]); } diff --git a/tests/Database/Factories/PostFactory.php b/tests/Database/Factories/PostFactory.php index 1700d13..3cc0e51 100644 --- a/tests/Database/Factories/PostFactory.php +++ b/tests/Database/Factories/PostFactory.php @@ -19,6 +19,7 @@ public function definition(): array 'title' => $this->faker->sentence(), 'rating' => $this->faker->numberBetween(1, 10), 'price' => $this->faker->numberBetween(100, 10000), + 'price_cast' => $this->faker->numberBetween(100, 10000), ]; } } diff --git a/tests/FormInputTest.php b/tests/FormInputTest.php index da5c436..2d980f1 100644 --- a/tests/FormInputTest.php +++ b/tests/FormInputTest.php @@ -211,3 +211,13 @@ ->fill([$field->getName() => 2345345]); expect($component->getState()['price'])->toEqual('2345345'); }); + +it('accepts form input money with money cast', function () { + $component = ComponentContainer::make(FormTestComponent::make()) + ->statePath('data') + ->components([MoneyInput::make('price_cast')]) + ->fill(['price_cast' => 123456]); + + dd($component->getState()); + expect($component->getState()['price_cast'])->toEqual('123456'); +}); diff --git a/tests/Models/Post.php b/tests/Models/Post.php index 4033c6c..ccdd458 100644 --- a/tests/Models/Post.php +++ b/tests/Models/Post.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\SoftDeletes; +use Pelmered\FilamentMoneyField\Casts\MoneyCast; use Pelmered\FilamentMoneyField\Tests\Database\Factories\PostFactory; class Post extends Model @@ -16,6 +17,7 @@ class Post extends Model protected $casts = [ 'is_published' => 'boolean', 'tags' => 'array', + 'price_cast' => MoneyCast::class, ]; protected $guarded = []; diff --git a/tests/MoneyFormatterTest.php b/tests/MoneyFormatterTest.php index 54553e1..b2982b6 100644 --- a/tests/MoneyFormatterTest.php +++ b/tests/MoneyFormatterTest.php @@ -3,9 +3,7 @@ namespace Pelmered\FilamentMoneyField\Tests; use Money\Currency; -use Pelmered\FilamentMoneyField\Tests\TestCase; use Pelmered\FilamentMoneyField\MoneyFormatter\MoneyFormatter; -use PHPUnit\Framework\Attributes\DataProvider; uses(TestCase::class);