From 71c665f513ec9fff724512098e4901d70c9d645e Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 21:06:25 +0100 Subject: [PATCH 01/34] Refactored to use brick/money --- DependencyInjection/Padam87MoneyExtension.php | 32 +-------- .../Mapping/Driver/MoneyEmbeddedDriver.php | 4 +- Doctrine/Type/CurrencyType.php | 26 +++++-- Doctrine/Type/DecimalObjectType.php | 54 ++++++++++++++ Entity/Embeddables/NullableMoney.php | 47 ++++++++++++ Entity/ExchangeRateInterface.php | 4 +- Form/CurrencyType.php | 20 +++--- Form/DecimalType.php | 66 +++++++++++++++++ Form/MoneyType.php | 58 +++++++++------ Padam87MoneyBundle.php | 8 --- README.md | 5 +- .../ExchangeRateRepositoryInterface.php | 3 +- Resources/config/services.yaml | 45 ++++-------- Service/DatabaseExchangeRateProvider.php | 41 +++++++++++ Service/MoneyFormatter.php | 72 +++++++++++++++++++ Twig/Extension/MoneyExtension.php | 60 ++++++++++++++++ composer.json | 2 +- 17 files changed, 430 insertions(+), 117 deletions(-) create mode 100644 Doctrine/Type/DecimalObjectType.php create mode 100644 Entity/Embeddables/NullableMoney.php create mode 100644 Form/DecimalType.php create mode 100644 Service/DatabaseExchangeRateProvider.php create mode 100644 Service/MoneyFormatter.php create mode 100644 Twig/Extension/MoneyExtension.php diff --git a/DependencyInjection/Padam87MoneyExtension.php b/DependencyInjection/Padam87MoneyExtension.php index 98ddb4d..dafb572 100644 --- a/DependencyInjection/Padam87MoneyExtension.php +++ b/DependencyInjection/Padam87MoneyExtension.php @@ -2,14 +2,11 @@ namespace Padam87\MoneyBundle\DependencyInjection; -use Money\Currencies; -use Money\Currencies\CurrencyList; use Padam87\MoneyBundle\Doctrine\Type\CurrencyType; -use Padam87\MoneyBundle\Doctrine\Type\MoneyAmountType; +use Padam87\MoneyBundle\Doctrine\Type\DecimalObjectType; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader; @@ -40,7 +37,7 @@ public function prepend(ContainerBuilder $container) [ 'dbal' => [ 'types' => [ - 'money_amount' => MoneyAmountType::class, + 'decimal_object' => DecimalObjectType::class, 'currency' => CurrencyType::class, ] ], @@ -53,37 +50,14 @@ public function prepend(ContainerBuilder $container) */ public function process(ContainerBuilder $container) { - $config = $container->getParameter('padam87_money.config'); - $driver = $container->getDefinition('doctrine.orm.default_metadata_driver'); - $driver->addMethodCall( 'addDriver', [ $container->getDefinition('Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver'), - 'Money\Money' + 'Brick\Money\Money' ] ); - - $driver->addMethodCall( - 'addDriver', - [ - $container->getDefinition('Padam87\MoneyBundle\Doctrine\Mapping\Driver\CurrencyPairEmbeddedDriver'), - 'Money\CurrencyPair' - ] - ); - - $container->setDefinition( - CurrencyList::class, - new Definition( - CurrencyList::class, - [ - array_fill_keys($config['currencies'], $config['scale']) - ] - ) - ); - - $container->setAlias(Currencies::class, CurrencyList::class); } } diff --git a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php index 02b89df..6644c21 100644 --- a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php +++ b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php @@ -2,9 +2,9 @@ namespace Padam87\MoneyBundle\Doctrine\Mapping\Driver; +use Brick\Money\Money; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver; -use Money\Money; class MoneyEmbeddedDriver implements MappingDriver { @@ -27,7 +27,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) $metadata->mapField( [ 'fieldName' => 'amount', - 'type' => 'money_amount', + 'type' => 'decimal_object', 'precision' => $this->config['precision'], 'scale' => $this->config['scale'], ] diff --git a/Doctrine/Type/CurrencyType.php b/Doctrine/Type/CurrencyType.php index 2f6ed10..a999f31 100644 --- a/Doctrine/Type/CurrencyType.php +++ b/Doctrine/Type/CurrencyType.php @@ -2,9 +2,9 @@ namespace Padam87\MoneyBundle\Doctrine\Type; +use Brick\Money\Currency; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; -use Money\Currency; class CurrencyType extends Type { @@ -19,11 +19,19 @@ public function getName() /** * {@inheritdoc} */ - public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform) { - $fieldDeclaration['length'] = 3; + $column['length'] = $this->getDefaultLength($platform); // enforce column length even if specified - return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + return $platform->getVarcharTypeDeclarationSQL($column); + } + + /** + * {@inheritdoc} + */ + public function getDefaultLength(AbstractPlatform $platform) + { + return 3; } /** @@ -43,7 +51,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return null; } - return new Currency($value); + return Currency::of($value); } /** @@ -51,10 +59,14 @@ public function convertToPHPValue($value, AbstractPlatform $platform) */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { + if ($value === null) { + return null; + } + if (!$value instanceof Currency) { - throw new \LogicException(); + throw new \LogicException(sprintf('Only instances of "%s" can be persisted as currency', Currency::class)); } - return $value->getCode(); + return (string) $value->getCurrencyCode(); } } diff --git a/Doctrine/Type/DecimalObjectType.php b/Doctrine/Type/DecimalObjectType.php new file mode 100644 index 0000000..1cc0929 --- /dev/null +++ b/Doctrine/Type/DecimalObjectType.php @@ -0,0 +1,54 @@ +amount = $money ? $money->getAmount() : null; + $this->currency = $money ? $money->getCurrency() : null; + } + + public function __invoke(): ?Money + { + if ($this->amount === null || $this->currency === null) { + return null; + } + + return Money::of($this->amount, $this->currency); + } +} diff --git a/Entity/ExchangeRateInterface.php b/Entity/ExchangeRateInterface.php index 6f08fac..fe43af4 100644 --- a/Entity/ExchangeRateInterface.php +++ b/Entity/ExchangeRateInterface.php @@ -2,9 +2,9 @@ namespace Padam87\MoneyBundle\Entity; -use Money\CurrencyPair; +use Brick\Math\BigNumber; interface ExchangeRateInterface { - public function getCurrencyPair(): CurrencyPair; + public function getConversionRatio(): ?BigNumber; } diff --git a/Form/CurrencyType.php b/Form/CurrencyType.php index aa8c59a..cc91022 100644 --- a/Form/CurrencyType.php +++ b/Form/CurrencyType.php @@ -2,19 +2,16 @@ namespace Padam87\MoneyBundle\Form; -use Money\Currency; -use Money\Money; -use Padam87\MoneyBundle\Service\MoneyHelper; +use Brick\Money\Currency; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class CurrencyType extends AbstractType { - private $config; + private array $config; public function __construct(array $config) { @@ -29,11 +26,15 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder ->addModelTransformer( new CallbackTransformer( - function (Currency $model = null) { - return $model ? $model->getCode() : null; + function (?Currency $modelData = null) { + return $modelData ? $modelData->getCurrencyCode() : null; }, - function ($form) { - return new Currency($form); + function ($formData) { + if ($formData === null) { + return null; + } + + return Currency::of($formData); } ) ) @@ -45,6 +46,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver ->setDefaults( [ + 'data_class' => Currency::class, 'choices' => array_combine($this->config['currencies'], $this->config['currencies']), ] ) diff --git a/Form/DecimalType.php b/Form/DecimalType.php new file mode 100644 index 0000000..36753ef --- /dev/null +++ b/Form/DecimalType.php @@ -0,0 +1,66 @@ +addModelTransformer( + new CallbackTransformer( + function (?BigDecimal $modelData = null) use ($options) { + if ($modelData === null) { + return null; + } + + if ($options['integer_only']) { + return $modelData->getIntegralPart(); + } + + return (string) $modelData; + }, + function (?string $formData) { + if ($formData === null) { + return null; + } + + return BigDecimal::ofUnscaledValue(preg_replace('/\s+/', '', $formData)); + } + ) + ) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults( + [ + 'data_class' => BigDecimal::class, + 'integer_only' => false, + ] + ) + ; + } + + public function getParent() + { + return TextType::class; + } + + public function getBlockPrefix() + { + return 'decimal'; + } +} diff --git a/Form/MoneyType.php b/Form/MoneyType.php index 410f1a3..8085727 100644 --- a/Form/MoneyType.php +++ b/Form/MoneyType.php @@ -2,26 +2,17 @@ namespace Padam87\MoneyBundle\Form; -use Money\Currency; -use Money\Money; -use Padam87\MoneyBundle\Service\MoneyHelper; +use Brick\Money\Currency; +use Brick\Money\Money; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; -use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class MoneyType extends AbstractType { - private $moneyHelper; - private $config; - - public function __construct(MoneyHelper $moneyHelper, array $config) - { - $this->moneyHelper = $moneyHelper; - $this->config = $config; - } - /** * {@inheritdoc} */ @@ -31,18 +22,32 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('amount', $options['amount_type'], $options['amount_options']) ->addModelTransformer( new CallbackTransformer( - function (Money $model = null) use ($options) { - if ($model === null) { - $model = new Money(0, new Currency($options['default_currency_code'])); + function (?Money $modelData = null) { + if ($modelData === null) { + return null; } return [ - 'amount' => $this->moneyHelper->getAmount($model), - 'currency' => $model->getCurrency(), + 'amount' => $modelData->getAmount(), + 'currency' => $modelData->getCurrency(), ]; }, - function ($form) { - return $this->moneyHelper->createMoney($form['amount'], $form['currency']); + function (?array $formData = null) use ($options) { + if ($formData === null) { + return null; + } + + if (null === $amount = $formData['amount']) { + return null; + } + + if (array_key_exists('currency', $formData)) { + $currency = $formData['currency']; + } else { + $currency = Currency::of($options['default_currency_code']); + } + + return Money::of($amount, $currency); } ) ) @@ -53,21 +58,28 @@ function ($form) { } } + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['currency_enabled'] = $options['currency_enabled']; + $view->vars['default_currency_code'] = $options['default_currency_code']; + } + public function configureOptions(OptionsResolver $resolver) { $resolver ->setDefaults( [ - 'amount_type' => TextType::class, + 'amount_type' => DecimalType::class, 'amount_options' => [ 'label' => false, ], - 'default_currency_code' => $this->config['default_currency'], + 'default_currency_code' => 'HUF', 'currency_enabled' => false, 'currency_type' => CurrencyType::class, 'currency_options' => [ 'label' => false, ], + 'addon_text' => null, ] ) ; @@ -75,6 +87,6 @@ public function configureOptions(OptionsResolver $resolver) public function getBlockPrefix() { - return 'moneyphp_money'; + return 'money_object'; } } diff --git a/Padam87MoneyBundle.php b/Padam87MoneyBundle.php index 903d030..f6309ba 100644 --- a/Padam87MoneyBundle.php +++ b/Padam87MoneyBundle.php @@ -2,16 +2,8 @@ namespace Padam87\MoneyBundle; -use Padam87\MoneyBundle\Doctrine\Type\MoneyAmountType; use Symfony\Component\HttpKernel\Bundle\Bundle; class Padam87MoneyBundle extends Bundle { - public function boot() - { - $config = $this->container->getParameter('padam87_money.config'); - - MoneyAmountType::$precision = $config['precision']; - MoneyAmountType::$scale = $config['scale']; - } } diff --git a/README.md b/README.md index b6a870b..e9f7284 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MoneyBundle -Symfony bundle for https://github.com/moneyphp/money +Symfony bundle for https://github.com/brick/money As an **opinionated** bundle, this money bundle uses the following principles as it's main guide: - Storage is just as important as calculation. @@ -13,9 +13,6 @@ To achieve these, the following restrictions apply: - precision and scale are mandatory (but have default values) - amounts are mapped as DECIMAL (changable, but not recommended to change) - ext-bcmath is mandatory. -(DECIMAL database values are converted to string by PDO. -This bundle uses bcmath to multiple these values by ˙pow(10, $scale)˙, and pass integer values to the `Money` object. -https://github.com/Padam87/MoneyBundle/blob/master/Doctrine/Type/MoneyAmountType.php#L45) ## Installation diff --git a/Repository/ExchangeRateRepositoryInterface.php b/Repository/ExchangeRateRepositoryInterface.php index ef8c67d..26e6a3d 100644 --- a/Repository/ExchangeRateRepositoryInterface.php +++ b/Repository/ExchangeRateRepositoryInterface.php @@ -2,10 +2,9 @@ namespace Padam87\MoneyBundle\Repository; -use Money\Currency; use Padam87\MoneyBundle\Entity\ExchangeRateInterface; interface ExchangeRateRepositoryInterface { - public function getExchangeRate(Currency $baseCurrency, Currency $counterCurrency): ?ExchangeRateInterface; + public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode): ?ExchangeRateInterface; } diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml index 4db17e1..0400ef2 100644 --- a/Resources/config/services.yaml +++ b/Resources/config/services.yaml @@ -1,50 +1,35 @@ services: _defaults: public: false - autowire: true - autoconfigure: true Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver: arguments: $config: '%padam87_money.config%' - Padam87\MoneyBundle\Doctrine\Mapping\Driver\CurrencyPairEmbeddedDriver: ~ - - Padam87\MoneyBundle\Money\Formatter\NumberFormatterFactory: ~ - - padam87_money.number_formatter: - class: \NumberFormatter - factory: 'Padam87\MoneyBundle\Money\Formatter\NumberFormatterFactory:createNumberFormatter' - arguments: ['@request_stack', '%locale%'] - - Money\Formatter\IntlMoneyFormatter: + Padam87\MoneyBundle\Service\MoneyFormatter: arguments: - - '@padam87_money.number_formatter' - - '@Money\Currencies\CurrencyList' + $requestStack: '@request_stack' + $defaultLocale: '%kernel.default_locale%' - Padam87\MoneyBundle\Service\MoneyHelper: - public: true + Padam87\MoneyBundle\Form\CurrencyType: arguments: $config: '%padam87_money.config%' - Padam87\MoneyBundle\Form\MoneyType: - arguments: - $config: '%padam87_money.config%' + Padam87\MoneyBundle\Form\DecimalType: ~ - Padam87\MoneyBundle\Form\CurrencyType: - arguments: - $config: '%padam87_money.config%' + Padam87\MoneyBundle\Form\MoneyType: ~ - Padam87\MoneyBundle\Exchange\DatabaseExchange: + Padam87\MoneyBundle\Twig\Extension\MoneyExtension: arguments: - - '@Doctrine\Persistence\ManagerRegistry' + $formatter: '@Padam87\MoneyBundle\Service\MoneyFormatter' + $converter: '@Brick\Money\CurrencyConverter' + tags: + - 'twig.extension' - Money\Exchange\ReversedCurrenciesExchange: + Padam87\MoneyBundle\Service\DatabaseExchangeRateProvider: arguments: - - '@Padam87\MoneyBundle\Exchange\DatabaseExchange' + $doctrine: '@doctrine' - Money\Converter: - class: Money\Converter + Brick\Money\CurrencyConverter: arguments: - - '@Money\Currencies\CurrencyList' - - '@Money\Exchange\ReversedCurrenciesExchange' + $exchangeRateProvider: '@Padam87\MoneyBundle\Service\DatabaseExchangeRateProvider' diff --git a/Service/DatabaseExchangeRateProvider.php b/Service/DatabaseExchangeRateProvider.php new file mode 100644 index 0000000..93728dc --- /dev/null +++ b/Service/DatabaseExchangeRateProvider.php @@ -0,0 +1,41 @@ +doctrine = $doctrine; + } + + public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode) + { + if ($sourceCurrencyCode === $targetCurrencyCode) { + return 1; + } + + $repo = $this->doctrine->getRepository(ExchangeRateInterface::class); + + if (!$repo instanceof ExchangeRateRepositoryInterface) { + throw new \LogicException( + sprintf('"%s" must implement %s', $repo->getClassName(), ExchangeRateRepositoryInterface::class) + ); + } + + if (null === $exchangeRate = $repo->getExchangeRate($sourceCurrencyCode, $targetCurrencyCode)) { + throw new \LogicException( + sprintf('%s - >%s ratio not found in the database.', $sourceCurrencyCode, $sourceCurrencyCode) + ); + } + + return $exchangeRate->getConversionRatio(); + } +} diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php new file mode 100644 index 0000000..547317e --- /dev/null +++ b/Service/MoneyFormatter.php @@ -0,0 +1,72 @@ +requestStack = $requestStack; + $this->defaultLocale = $defaultLocale; + } + + public function format(?Money $money, ?int $digits = null): ?string + { + if ($money === null) { + return null; + } + + return $money->formatWith($this->createFormatter($digits)); + } + + public function amount(?Money $money, ?int $digits = null): ?string + { + if ($money === null) { + return null; + } + + return $this->createFormatter($digits, \NumberFormatter::DECIMAL)->format((string) $money->getAmount()); + } + + public function currency(?Money $money): ?string + { + if ($money === null) { + return null; + } + + return Currencies::getSymbol($money->getCurrency()->getCurrencyCode()); + } + + private function createFormatter(?int $digits = null, int $formatStyle = \NumberFormatter::CURRENCY): \NumberFormatter + { + $locale = $this->getLocale(); + $formatter = new \NumberFormatter($locale, $formatStyle); + $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, \NumberFormatter::ROUND_CEILING); + + if ($digits) { + $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $digits); + $formatter->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $digits); + $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $digits); + $formatter->setAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN, $digits); + } + + return $formatter; + } + + private function getLocale() + { + if (null === $request = $this->requestStack->getMasterRequest()) { + return $this->defaultLocale; + } + + return $request->getLocale(); + } +} diff --git a/Twig/Extension/MoneyExtension.php b/Twig/Extension/MoneyExtension.php new file mode 100644 index 0000000..aeb146a --- /dev/null +++ b/Twig/Extension/MoneyExtension.php @@ -0,0 +1,60 @@ +formatter = $formatter; + $this->converter = $converter; + } + + public function getFilters() + { + return [ + new TwigFilter('money', [$this->formatter, 'format']), + new TwigFilter('money_amount', [$this->formatter, 'amount']), + new TwigFilter('money_currency', [$this->formatter, 'currency']), + new TwigFilter('money_convert', [$this->converter, 'convert']), + new TwigFilter( + 'currency', + function ($currency) { + if ($currency === null) { + return null; + } + + if (!$currency instanceof Currency) { + $currency = Currency::of($currency); + } + + return Currencies::getSymbol($currency->getCurrencyCode()); + } + ), + ]; + } + + public function getFunctions() + { + return [ + new TwigFunction('money', function ($amount, $currency) { + return Money::of($amount, $currency); + }), + new TwigFunction('currency', function ($code) { + return Currency::of($code); + }), + ]; + } +} diff --git a/composer.json b/composer.json index f467218..4cce9ab 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "php": "^7.4 || ^8.0", "ext-bcmath": "*", "symfony/framework-bundle": "^4.0 || ^5.0 || ^6.0", - "moneyphp/money": "^3.1", + "brick/money": "^0.5.1", "doctrine/orm": "^2.5", "doctrine/doctrine-bundle": "^1.5 || ^2.0" }, From 6dd8b336d12602e5e97dc00a959cb099a4434070 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 21:20:09 +0100 Subject: [PATCH 02/34] Removed form data_class restriction --- Form/CurrencyType.php | 1 - Form/DecimalType.php | 1 - 2 files changed, 2 deletions(-) diff --git a/Form/CurrencyType.php b/Form/CurrencyType.php index cc91022..0f2f068 100644 --- a/Form/CurrencyType.php +++ b/Form/CurrencyType.php @@ -46,7 +46,6 @@ public function configureOptions(OptionsResolver $resolver) $resolver ->setDefaults( [ - 'data_class' => Currency::class, 'choices' => array_combine($this->config['currencies'], $this->config['currencies']), ] ) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index 36753ef..4fff0a3 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -47,7 +47,6 @@ public function configureOptions(OptionsResolver $resolver) $resolver ->setDefaults( [ - 'data_class' => BigDecimal::class, 'integer_only' => false, ] ) From 4f1d58828ca95ef5959b8f08e0a5443a661dc907 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 21:44:24 +0100 Subject: [PATCH 03/34] Fixed decimal form type --- Form/DecimalType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index 4fff0a3..e63e40d 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -35,7 +35,7 @@ function (?string $formData) { return null; } - return BigDecimal::ofUnscaledValue(preg_replace('/\s+/', '', $formData)); + return BigDecimal::of(preg_replace('/\s+/', '', $formData)); } ) ) From 05bdb435750e5d6358d205c3878b4f7b71beae71 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 22:09:10 +0100 Subject: [PATCH 04/34] Decimal input sanitazation --- Form/DecimalType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index e63e40d..e6bea57 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -35,7 +35,7 @@ function (?string $formData) { return null; } - return BigDecimal::of(preg_replace('/\s+/', '', $formData)); + return BigDecimal::of(str_replace([' ', ','], ['', '.'], $formData)); } ) ) From 12ca2976de69dbc571c9f1bfec54345a35adc498 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 22:09:29 +0100 Subject: [PATCH 05/34] Fixed money formatter digits check --- Service/MoneyFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php index 547317e..a633b4f 100644 --- a/Service/MoneyFormatter.php +++ b/Service/MoneyFormatter.php @@ -51,7 +51,7 @@ private function createFormatter(?int $digits = null, int $formatStyle = \Number $formatter = new \NumberFormatter($locale, $formatStyle); $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, \NumberFormatter::ROUND_CEILING); - if ($digits) { + if ($digits !== null) { $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $digits); $formatter->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $digits); $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $digits); From 126984a78fca1ced373be0f58668c080bb33c355 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sun, 21 Feb 2021 23:20:36 +0100 Subject: [PATCH 06/34] Added missing form type tags --- Resources/config/services.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml index 0400ef2..5a1c681 100644 --- a/Resources/config/services.yaml +++ b/Resources/config/services.yaml @@ -14,10 +14,16 @@ services: Padam87\MoneyBundle\Form\CurrencyType: arguments: $config: '%padam87_money.config%' + tags: + - 'form.type' - Padam87\MoneyBundle\Form\DecimalType: ~ + Padam87\MoneyBundle\Form\DecimalType: + tags: + - 'form.type' - Padam87\MoneyBundle\Form\MoneyType: ~ + Padam87\MoneyBundle\Form\MoneyType: + tags: + - 'form.type' Padam87\MoneyBundle\Twig\Extension\MoneyExtension: arguments: From 453703f2400518c7ccc453684aa17a28f744a712 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 27 May 2021 23:19:45 +0200 Subject: [PATCH 07/34] Overridable currency digits for money formatter --- DependencyInjection/Configuration.php | 3 +++ Resources/config/services.yaml | 1 + Service/MoneyFormatter.php | 22 +++++++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 23b6018..c3af7c3 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -31,6 +31,9 @@ public function getConfigTreeBuilder() ->scalarPrototype()->end() ->defaultValue(['EUR']) ->end() + ->arrayNode('currency_digits') + ->scalarPrototype()->end() + ->end() ->end() ; diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml index 5a1c681..c66a28f 100644 --- a/Resources/config/services.yaml +++ b/Resources/config/services.yaml @@ -10,6 +10,7 @@ services: arguments: $requestStack: '@request_stack' $defaultLocale: '%kernel.default_locale%' + $config: '%padam87_money.config%' Padam87\MoneyBundle\Form\CurrencyType: arguments: diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php index a633b4f..f4f8372 100644 --- a/Service/MoneyFormatter.php +++ b/Service/MoneyFormatter.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\Service; +use Brick\Money\Currency; use Brick\Money\Money; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -11,11 +12,13 @@ class MoneyFormatter { private RequestStack $requestStack; private string $defaultLocale; + private array $config; - public function __construct(RequestStack $requestStack, string $defaultLocale) + public function __construct(RequestStack $requestStack, string $defaultLocale, array $config) { $this->requestStack = $requestStack; $this->defaultLocale = $defaultLocale; + $this->config = $config; } public function format(?Money $money, ?int $digits = null): ?string @@ -24,6 +27,10 @@ public function format(?Money $money, ?int $digits = null): ?string return null; } + if ($digits === null) { + $digits = $this->getCurrencyDigits($money->getCurrency()); + } + return $money->formatWith($this->createFormatter($digits)); } @@ -33,6 +40,10 @@ public function amount(?Money $money, ?int $digits = null): ?string return null; } + if ($digits === null) { + $digits = $this->getCurrencyDigits($money->getCurrency()); + } + return $this->createFormatter($digits, \NumberFormatter::DECIMAL)->format((string) $money->getAmount()); } @@ -61,6 +72,15 @@ private function createFormatter(?int $digits = null, int $formatStyle = \Number return $formatter; } + private function getCurrencyDigits(Currency $currency): ?int + { + if (array_key_exists($currency->getCurrencyCode(), $this->config['currency_digits'])) { + return (int) $this->config['currency_digits'][$currency->getCurrencyCode()]; + } + + return null; + } + private function getLocale() { if (null === $request = $this->requestStack->getMasterRequest()) { From 3926888459d106745f6611c727e65884436b8a78 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 12 Mar 2022 01:53:26 +0100 Subject: [PATCH 08/34] Removed moneyphp remnants --- .../Driver/CurrencyPairEmbeddedDriver.php | 59 ------------------- Doctrine/Type/MoneyAmountType.php | 54 ----------------- Money/Formatter/NumberFormatterFactory.php | 19 ------ Service/MoneyHelper.php | 29 --------- 4 files changed, 161 deletions(-) delete mode 100644 Doctrine/Mapping/Driver/CurrencyPairEmbeddedDriver.php delete mode 100644 Doctrine/Type/MoneyAmountType.php delete mode 100644 Money/Formatter/NumberFormatterFactory.php delete mode 100644 Service/MoneyHelper.php diff --git a/Doctrine/Mapping/Driver/CurrencyPairEmbeddedDriver.php b/Doctrine/Mapping/Driver/CurrencyPairEmbeddedDriver.php deleted file mode 100644 index 7c495c3..0000000 --- a/Doctrine/Mapping/Driver/CurrencyPairEmbeddedDriver.php +++ /dev/null @@ -1,59 +0,0 @@ -isEmbeddedClass = true; - - $metadata->mapField( - [ - 'fieldName' => 'baseCurrency', - 'type' => 'currency', - ] - ); - - $metadata->mapField( - [ - 'fieldName' => 'counterCurrency', - 'type' => 'currency', - ] - ); - - $metadata->mapField( - [ - 'fieldName' => 'conversionRatio', - 'type' => 'float', - ] - ); - } - - /** - * {@inheritdoc} - */ - public function getAllClassNames() - { - return [ - CurrencyPair::class - ]; - } - - /** - * {@inheritdoc} - */ - public function isTransient($className) - { - return false; - } -} diff --git a/Doctrine/Type/MoneyAmountType.php b/Doctrine/Type/MoneyAmountType.php deleted file mode 100644 index 733cf6a..0000000 --- a/Doctrine/Type/MoneyAmountType.php +++ /dev/null @@ -1,54 +0,0 @@ -getCurrentRequest()) { - $locale = $defaultLocale; - } else { - $locale = $requestStack->getCurrentRequest()->getLocale(); - } - - return new \NumberFormatter($locale, \NumberFormatter::CURRENCY); - } -} diff --git a/Service/MoneyHelper.php b/Service/MoneyHelper.php deleted file mode 100644 index 7b54993..0000000 --- a/Service/MoneyHelper.php +++ /dev/null @@ -1,29 +0,0 @@ -config = $config; - } - - public function createMoney($amount, Currency $currency): Money - { - return new Money(bcmul($amount, pow(10, $this->config['scale']), 0), $currency); - } - - public function getAmount(Money $money, ?int $scale = null): string - { - return bcdiv( - $money->getAmount(), - pow(10, $this->config['scale']), $scale === null ? $this->config['scale'] : $scale - ); - } -} From 7bf851377af0e32ec9348e610dc6c429e5d03585 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 12 Mar 2022 01:54:08 +0100 Subject: [PATCH 09/34] Fixed symfony 6 deprecation of master request --- Service/MoneyFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php index f4f8372..ae8c0f3 100644 --- a/Service/MoneyFormatter.php +++ b/Service/MoneyFormatter.php @@ -83,7 +83,7 @@ private function getCurrencyDigits(Currency $currency): ?int private function getLocale() { - if (null === $request = $this->requestStack->getMasterRequest()) { + if (null === $request = $this->requestStack->getMainRequest()) { return $this->defaultLocale; } From 89ad5eeafc751127ab31d7eb8afe9b03cbcf91ff Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 12 Mar 2022 01:54:54 +0100 Subject: [PATCH 10/34] Use the proper exception for database exchange rate provider --- Service/DatabaseExchangeRateProvider.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Service/DatabaseExchangeRateProvider.php b/Service/DatabaseExchangeRateProvider.php index 93728dc..fa00974 100644 --- a/Service/DatabaseExchangeRateProvider.php +++ b/Service/DatabaseExchangeRateProvider.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\Service; +use Brick\Money\Exception\CurrencyConversionException; use Brick\Money\ExchangeRateProvider; use Doctrine\Persistence\ManagerRegistry; use Padam87\MoneyBundle\Entity\ExchangeRateInterface; @@ -31,9 +32,7 @@ public function getExchangeRate(string $sourceCurrencyCode, string $targetCurren } if (null === $exchangeRate = $repo->getExchangeRate($sourceCurrencyCode, $targetCurrencyCode)) { - throw new \LogicException( - sprintf('%s - >%s ratio not found in the database.', $sourceCurrencyCode, $sourceCurrencyCode) - ); + throw CurrencyConversionException::exchangeRateNotAvailable($sourceCurrencyCode, $targetCurrencyCode); } return $exchangeRate->getConversionRatio(); From 55a667495cf1ffa73c3b563edf1428b552cd6add Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 12 Mar 2022 01:55:39 +0100 Subject: [PATCH 11/34] Remove old moneyphp exchange --- Exchange/DatabaseExchange.php | 48 ----------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 Exchange/DatabaseExchange.php diff --git a/Exchange/DatabaseExchange.php b/Exchange/DatabaseExchange.php deleted file mode 100644 index bff2545..0000000 --- a/Exchange/DatabaseExchange.php +++ /dev/null @@ -1,48 +0,0 @@ -doctrine = $doctrine; - } - - /** - * {@inheritdoc} - */ - public function quote(Currency $baseCurrency, Currency $counterCurrency) - { - if ($baseCurrency->getCode() === $counterCurrency->getCode()) { - return new CurrencyPair($baseCurrency, $counterCurrency, 1); - } - - $repo = $this->doctrine->getRepository(ExchangeRateInterface::class); - - if (!$repo instanceof ExchangeRateRepositoryInterface) { - throw new \LogicException( - sprintf('"%s" must implement %s', $repo->getClassName(), ExchangeRateRepositoryInterface::class) - ); - } - - if (null === $exchangeRate = $repo->getExchangeRate($baseCurrency, $counterCurrency)) { - throw new UnresolvableCurrencyPairException( - sprintf('%s - >%s ratio not found in the database.', $baseCurrency->getCode(), $counterCurrency->getCode()) - ); - } - - return $exchangeRate->getCurrencyPair(); - } -} From c1334d59ea1911c13e047a92d7750cf3851d133f Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 12 Mar 2022 01:57:22 +0100 Subject: [PATCH 12/34] Refactor embeddables to avoid context issues --- DependencyInjection/Padam87MoneyExtension.php | 14 +++++- .../Mapping/Driver/MoneyEmbeddedDriver.php | 8 +++- Money/Context/BundleContext.php | 47 +++++++++++++++++++ Money/EmbeddedMoney.php | 44 +++++++++++++++++ .../Embeddables => Money}/NullableMoney.php | 15 ++---- Padam87MoneyBundle.php | 7 +++ Resources/config/services.yaml | 4 ++ 7 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 Money/Context/BundleContext.php create mode 100644 Money/EmbeddedMoney.php rename {Entity/Embeddables => Money}/NullableMoney.php (72%) diff --git a/DependencyInjection/Padam87MoneyExtension.php b/DependencyInjection/Padam87MoneyExtension.php index dafb572..38cf9d6 100644 --- a/DependencyInjection/Padam87MoneyExtension.php +++ b/DependencyInjection/Padam87MoneyExtension.php @@ -2,8 +2,11 @@ namespace Padam87\MoneyBundle\DependencyInjection; +use Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver; use Padam87\MoneyBundle\Doctrine\Type\CurrencyType; use Padam87\MoneyBundle\Doctrine\Type\DecimalObjectType; +use Padam87\MoneyBundle\Money\EmbeddedMoney; +use Padam87\MoneyBundle\Money\NullableMoney; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; @@ -54,8 +57,15 @@ public function process(ContainerBuilder $container) $driver->addMethodCall( 'addDriver', [ - $container->getDefinition('Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver'), - 'Brick\Money\Money' + $container->getDefinition(MoneyEmbeddedDriver::class), + EmbeddedMoney::class + ] + ); + $driver->addMethodCall( + 'addDriver', + [ + $container->getDefinition(MoneyEmbeddedDriver::class), + NullableMoney::class ] ); } diff --git a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php index 6644c21..c5e4a27 100644 --- a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php +++ b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php @@ -2,9 +2,10 @@ namespace Padam87\MoneyBundle\Doctrine\Mapping\Driver; -use Brick\Money\Money; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver; +use Padam87\MoneyBundle\Money\EmbeddedMoney; +use Padam87\MoneyBundle\Money\NullableMoney; class MoneyEmbeddedDriver implements MappingDriver { @@ -30,6 +31,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) 'type' => 'decimal_object', 'precision' => $this->config['precision'], 'scale' => $this->config['scale'], + 'nullable' => $className === NullableMoney::class, ] ); @@ -37,6 +39,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) [ 'fieldName' => 'currency', 'type' => 'currency', + 'nullable' => $className === NullableMoney::class, ] ); } @@ -47,7 +50,8 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) public function getAllClassNames() { return [ - Money::class + EmbeddedMoney::class, + NullableMoney::class, ]; } diff --git a/Money/Context/BundleContext.php b/Money/Context/BundleContext.php new file mode 100644 index 0000000..0d29ac0 --- /dev/null +++ b/Money/Context/BundleContext.php @@ -0,0 +1,47 @@ +toScale(self::$scale, $roundingMode); + } + + /** + * {@inheritdoc} + */ + public function getStep() : int + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function isFixedScale() : bool + { + return true; + } + + public static function getScale(): int + { + return self::$scale; + } + + public static function setScale($scale): void + { + self::$scale = $scale; + } +} diff --git a/Money/EmbeddedMoney.php b/Money/EmbeddedMoney.php new file mode 100644 index 0000000..5b38a05 --- /dev/null +++ b/Money/EmbeddedMoney.php @@ -0,0 +1,44 @@ +amount = $money->getAmount(); + $this->currency = $money->getCurrency(); + } + + public static function of($amount, Currency $currency): self + { + return new EmbeddedMoney(Money::of($amount, $currency, new BundleContext())); + } + + public static function zero(Currency $currency): self + { + return new EmbeddedMoney(Money::zero($currency, new BundleContext())); + } + + public function __invoke(): Money + { + return Money::of($this->amount, $this->currency, new BundleContext()); + } +} diff --git a/Entity/Embeddables/NullableMoney.php b/Money/NullableMoney.php similarity index 72% rename from Entity/Embeddables/NullableMoney.php rename to Money/NullableMoney.php index 795d04e..6610ddb 100644 --- a/Entity/Embeddables/NullableMoney.php +++ b/Money/NullableMoney.php @@ -1,11 +1,12 @@ amount, $this->currency); + return Money::of($this->amount, $this->currency, new BundleContext()); } } diff --git a/Padam87MoneyBundle.php b/Padam87MoneyBundle.php index f6309ba..a58186f 100644 --- a/Padam87MoneyBundle.php +++ b/Padam87MoneyBundle.php @@ -2,8 +2,15 @@ namespace Padam87\MoneyBundle; +use Padam87\MoneyBundle\Money\Context\BundleContext; use Symfony\Component\HttpKernel\Bundle\Bundle; class Padam87MoneyBundle extends Bundle { + public function boot() + { + $config = $this->container->getParameter('padam87_money.config'); + + BundleContext::setScale($config['scale']); + } } diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml index c66a28f..e90e35e 100644 --- a/Resources/config/services.yaml +++ b/Resources/config/services.yaml @@ -33,6 +33,9 @@ services: tags: - 'twig.extension' + Padam87\MoneyBundle\Money\Context\BundleContext: + shared: false + Padam87\MoneyBundle\Service\DatabaseExchangeRateProvider: arguments: $doctrine: '@doctrine' @@ -40,3 +43,4 @@ services: Brick\Money\CurrencyConverter: arguments: $exchangeRateProvider: '@Padam87\MoneyBundle\Service\DatabaseExchangeRateProvider' + $context: '@Padam87\MoneyBundle\Money\Context\BundleContext' From edec024469549b62d9e157eda2879887875ee738 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Wed, 13 Apr 2022 23:52:30 +0200 Subject: [PATCH 13/34] Context and rounding mode to form type --- Form/MoneyType.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Form/MoneyType.php b/Form/MoneyType.php index 8085727..b22a305 100644 --- a/Form/MoneyType.php +++ b/Form/MoneyType.php @@ -2,8 +2,10 @@ namespace Padam87\MoneyBundle\Form; +use Brick\Math\RoundingMode; use Brick\Money\Currency; use Brick\Money\Money; +use Padam87\MoneyBundle\Money\Context\BundleContext; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\FormBuilderInterface; @@ -47,7 +49,7 @@ function (?array $formData = null) use ($options) { $currency = Currency::of($options['default_currency_code']); } - return Money::of($amount, $currency); + return Money::of($amount, $currency, $options['context'], $options['rounding_mode']); } ) ) @@ -80,6 +82,8 @@ public function configureOptions(OptionsResolver $resolver) 'label' => false, ], 'addon_text' => null, + 'context' => new BundleContext(), + 'rounding_mode' => RoundingMode::UNNECESSARY, ] ) ; From 1e19ef8c690e0e384cf907c1926a0da76a6b3ea4 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 4 May 2024 23:10:18 +0200 Subject: [PATCH 14/34] Allow Symfony 7 --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 4cce9ab..7742b96 100644 --- a/composer.json +++ b/composer.json @@ -3,12 +3,12 @@ "description": "Symfony bundle for https://github.com/moneyphp/money", "type": "symfony-bundle", "require": { - "php": "^7.4 || ^8.0", + "php": "^8.0", "ext-bcmath": "*", - "symfony/framework-bundle": "^4.0 || ^5.0 || ^6.0", - "brick/money": "^0.5.1", - "doctrine/orm": "^2.5", - "doctrine/doctrine-bundle": "^1.5 || ^2.0" + "symfony/framework-bundle": "^6.0 || ^7.0", + "brick/money": ">=0.5.1 <0.9", + "doctrine/orm": "^2.5 || ^3.0", + "doctrine/doctrine-bundle": "^2.0" }, "require-dev": { "phpunit/phpunit": "^7.2" From d6e699149769ded8f01804d5091989588734b6a2 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 17:38:24 +0200 Subject: [PATCH 15/34] Code improvements --- DependencyInjection/Configuration.php | 2 +- DependencyInjection/Padam87MoneyExtension.php | 6 +++--- .../Mapping/Driver/MoneyEmbeddedDriver.php | 8 ++++---- Doctrine/Type/CurrencyType.php | 10 +++++----- Doctrine/Type/DecimalObjectType.php | 8 ++++---- Form/CurrencyType.php | 14 +++++++------- Form/DecimalType.php | 12 ++++++------ Form/MoneyType.php | 12 ++++++------ Money/Context/BundleContext.php | 2 +- Money/EmbeddedMoney.php | 3 ++- Money/NullableMoney.php | 4 ++-- Padam87MoneyBundle.php | 2 +- Service/MoneyFormatter.php | 2 +- Twig/Extension/MoneyExtension.php | 18 +++++++++--------- 14 files changed, 52 insertions(+), 51 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index c3af7c3..042d09b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -11,7 +11,7 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('padam87_money'); diff --git a/DependencyInjection/Padam87MoneyExtension.php b/DependencyInjection/Padam87MoneyExtension.php index 38cf9d6..c47b430 100644 --- a/DependencyInjection/Padam87MoneyExtension.php +++ b/DependencyInjection/Padam87MoneyExtension.php @@ -19,7 +19,7 @@ class Padam87MoneyExtension extends Extension implements PrependExtensionInterfa /** * {@inheritdoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); @@ -33,7 +33,7 @@ public function load(array $configs, ContainerBuilder $container) /** * {@inheritdoc} */ - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $container): void { $container->prependExtensionConfig( 'doctrine', @@ -51,7 +51,7 @@ public function prepend(ContainerBuilder $container) /** * {@inheritdoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $driver = $container->getDefinition('doctrine.orm.default_metadata_driver'); $driver->addMethodCall( diff --git a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php index c5e4a27..dc49295 100644 --- a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php +++ b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php @@ -9,7 +9,7 @@ class MoneyEmbeddedDriver implements MappingDriver { - private $config; + private array $config; public function __construct(array $config) { @@ -19,7 +19,7 @@ public function __construct(array $config) /** * {@inheritdoc} */ - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadata $metadata): void { /* @var \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata */ @@ -47,7 +47,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) /** * {@inheritdoc} */ - public function getAllClassNames() + public function getAllClassNames(): array { return [ EmbeddedMoney::class, @@ -58,7 +58,7 @@ public function getAllClassNames() /** * {@inheritdoc} */ - public function isTransient($className) + public function isTransient($className): bool { return false; } diff --git a/Doctrine/Type/CurrencyType.php b/Doctrine/Type/CurrencyType.php index a999f31..a2941b9 100644 --- a/Doctrine/Type/CurrencyType.php +++ b/Doctrine/Type/CurrencyType.php @@ -11,7 +11,7 @@ class CurrencyType extends Type /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return 'currency'; } @@ -29,7 +29,7 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform) /** * {@inheritdoc} */ - public function getDefaultLength(AbstractPlatform $platform) + public function getDefaultLength(AbstractPlatform $platform): int { return 3; } @@ -37,7 +37,7 @@ public function getDefaultLength(AbstractPlatform $platform) /** * {@inheritdoc} */ - public function requiresSQLCommentHint(AbstractPlatform $platform) + public function requiresSQLCommentHint(AbstractPlatform $platform): bool { return true; } @@ -45,7 +45,7 @@ public function requiresSQLCommentHint(AbstractPlatform $platform) /** * {@inheritdoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): ?Currency { if (null === $value) { return null; @@ -57,7 +57,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritdoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { if ($value === null) { return null; diff --git a/Doctrine/Type/DecimalObjectType.php b/Doctrine/Type/DecimalObjectType.php index 1cc0929..2755041 100644 --- a/Doctrine/Type/DecimalObjectType.php +++ b/Doctrine/Type/DecimalObjectType.php @@ -11,7 +11,7 @@ class DecimalObjectType extends DecimalType /** * {@inheritdoc} */ - public function getName() + public function getName(): string { return 'decimal_object'; } @@ -19,7 +19,7 @@ public function getName() /** * {@inheritdoc} */ - public function requiresSQLCommentHint(AbstractPlatform $platform) + public function requiresSQLCommentHint(AbstractPlatform $platform): bool { return true; } @@ -27,7 +27,7 @@ public function requiresSQLCommentHint(AbstractPlatform $platform) /** * {@inheritdoc} */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue($value, AbstractPlatform $platform): ?BigDecimal { if (null === $value) { return null; @@ -39,7 +39,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * {@inheritdoc} */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string { if ($value === null) { return null; diff --git a/Form/CurrencyType.php b/Form/CurrencyType.php index 0f2f068..5e128d9 100644 --- a/Form/CurrencyType.php +++ b/Form/CurrencyType.php @@ -21,15 +21,15 @@ public function __construct(array $config) /** * {@inheritdoc} */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->addModelTransformer( new CallbackTransformer( - function (?Currency $modelData = null) { - return $modelData ? $modelData->getCurrencyCode() : null; + function (?Currency $modelData = null): ?string { + return $modelData !== null ? $modelData->getCurrencyCode() : null; }, - function ($formData) { + function ($formData): ?Currency { if ($formData === null) { return null; } @@ -41,7 +41,7 @@ function ($formData) { ; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults( @@ -52,12 +52,12 @@ public function configureOptions(OptionsResolver $resolver) ; } - public function getParent() + public function getParent(): ?string { return ChoiceType::class; } - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'moneyphp_currency'; } diff --git a/Form/DecimalType.php b/Form/DecimalType.php index e6bea57..cc49d3d 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -14,12 +14,12 @@ class DecimalType extends AbstractType /** * {@inheritdoc} */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->addModelTransformer( new CallbackTransformer( - function (?BigDecimal $modelData = null) use ($options) { + function (?BigDecimal $modelData = null) use ($options): ?string { if ($modelData === null) { return null; } @@ -30,7 +30,7 @@ function (?BigDecimal $modelData = null) use ($options) { return (string) $modelData; }, - function (?string $formData) { + function (?string $formData): ?BigDecimal { if ($formData === null) { return null; } @@ -42,7 +42,7 @@ function (?string $formData) { ; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults( @@ -53,12 +53,12 @@ public function configureOptions(OptionsResolver $resolver) ; } - public function getParent() + public function getParent(): ?string { return TextType::class; } - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'decimal'; } diff --git a/Form/MoneyType.php b/Form/MoneyType.php index b22a305..09d5258 100644 --- a/Form/MoneyType.php +++ b/Form/MoneyType.php @@ -18,13 +18,13 @@ class MoneyType extends AbstractType /** * {@inheritdoc} */ - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder ->add('amount', $options['amount_type'], $options['amount_options']) ->addModelTransformer( new CallbackTransformer( - function (?Money $modelData = null) { + function (?Money $modelData = null): ?array { if ($modelData === null) { return null; } @@ -34,7 +34,7 @@ function (?Money $modelData = null) { 'currency' => $modelData->getCurrency(), ]; }, - function (?array $formData = null) use ($options) { + function (?array $formData = null) use ($options): ?Money { if ($formData === null) { return null; } @@ -60,13 +60,13 @@ function (?array $formData = null) use ($options) { } } - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { $view->vars['currency_enabled'] = $options['currency_enabled']; $view->vars['default_currency_code'] = $options['default_currency_code']; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults( @@ -89,7 +89,7 @@ public function configureOptions(OptionsResolver $resolver) ; } - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'money_object'; } diff --git a/Money/Context/BundleContext.php b/Money/Context/BundleContext.php index 0d29ac0..998f9b3 100644 --- a/Money/Context/BundleContext.php +++ b/Money/Context/BundleContext.php @@ -40,7 +40,7 @@ public static function getScale(): int return self::$scale; } - public static function setScale($scale): void + public static function setScale(int $scale): void { self::$scale = $scale; } diff --git a/Money/EmbeddedMoney.php b/Money/EmbeddedMoney.php index 5b38a05..1aabcf4 100644 --- a/Money/EmbeddedMoney.php +++ b/Money/EmbeddedMoney.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\Money; +use Brick\Math\BigNumber; use Brick\Math\BigDecimal; use Brick\Money\Currency; use Brick\Money\Money; @@ -27,7 +28,7 @@ public function __construct(Money $money) $this->currency = $money->getCurrency(); } - public static function of($amount, Currency $currency): self + public static function of(BigNumber|int|float|string $amount, Currency $currency): self { return new EmbeddedMoney(Money::of($amount, $currency, new BundleContext())); } diff --git a/Money/NullableMoney.php b/Money/NullableMoney.php index 6610ddb..56d9815 100644 --- a/Money/NullableMoney.php +++ b/Money/NullableMoney.php @@ -25,8 +25,8 @@ class NullableMoney public function __construct(?Money $money = null) { - $this->amount = $money ? $money->getAmount() : null; - $this->currency = $money ? $money->getCurrency() : null; + $this->amount = $money !== null ? $money->getAmount() : null; + $this->currency = $money !== null ? $money->getCurrency() : null; } public function __invoke(): ?Money diff --git a/Padam87MoneyBundle.php b/Padam87MoneyBundle.php index a58186f..176fb13 100644 --- a/Padam87MoneyBundle.php +++ b/Padam87MoneyBundle.php @@ -7,7 +7,7 @@ class Padam87MoneyBundle extends Bundle { - public function boot() + public function boot(): void { $config = $this->container->getParameter('padam87_money.config'); diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php index ae8c0f3..6705c99 100644 --- a/Service/MoneyFormatter.php +++ b/Service/MoneyFormatter.php @@ -81,7 +81,7 @@ private function getCurrencyDigits(Currency $currency): ?int return null; } - private function getLocale() + private function getLocale(): string { if (null === $request = $this->requestStack->getMainRequest()) { return $this->defaultLocale; diff --git a/Twig/Extension/MoneyExtension.php b/Twig/Extension/MoneyExtension.php index aeb146a..309f29e 100644 --- a/Twig/Extension/MoneyExtension.php +++ b/Twig/Extension/MoneyExtension.php @@ -22,16 +22,16 @@ public function __construct(MoneyFormatter $formatter, CurrencyConverter $conver $this->converter = $converter; } - public function getFilters() + public function getFilters(): array { return [ - new TwigFilter('money', [$this->formatter, 'format']), - new TwigFilter('money_amount', [$this->formatter, 'amount']), - new TwigFilter('money_currency', [$this->formatter, 'currency']), - new TwigFilter('money_convert', [$this->converter, 'convert']), + new TwigFilter('money', $this->formatter->format(...)), + new TwigFilter('money_amount', $this->formatter->amount(...)), + new TwigFilter('money_currency', $this->formatter->currency(...)), + new TwigFilter('money_convert', $this->converter->convert(...)), new TwigFilter( 'currency', - function ($currency) { + function ($currency): ?string { if ($currency === null) { return null; } @@ -46,13 +46,13 @@ function ($currency) { ]; } - public function getFunctions() + public function getFunctions(): array { return [ - new TwigFunction('money', function ($amount, $currency) { + new TwigFunction('money', function ($amount, $currency): Money { return Money::of($amount, $currency); }), - new TwigFunction('currency', function ($code) { + new TwigFunction('currency', function ($code): Currency { return Currency::of($code); }), ]; From f28b7b7fb2e7655b7fd246652ffd3b6b7050c908 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 17:43:51 +0200 Subject: [PATCH 16/34] Fix exchange rate provider --- Service/DatabaseExchangeRateProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Service/DatabaseExchangeRateProvider.php b/Service/DatabaseExchangeRateProvider.php index fa00974..bb78ba9 100644 --- a/Service/DatabaseExchangeRateProvider.php +++ b/Service/DatabaseExchangeRateProvider.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\Service; +use Brick\Math\BigNumber; use Brick\Money\Exception\CurrencyConversionException; use Brick\Money\ExchangeRateProvider; use Doctrine\Persistence\ManagerRegistry; @@ -17,7 +18,7 @@ public function __construct(ManagerRegistry $doctrine) $this->doctrine = $doctrine; } - public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode) + public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode): BigNumber|int|float|string { if ($sourceCurrencyCode === $targetCurrencyCode) { return 1; From d7dd3db43d3ba9753d09927bb1355f07cfaad82f Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 19:48:03 +0200 Subject: [PATCH 17/34] Upgrade brick money --- Money/Context/BundleContext.php | 3 ++- composer.json | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Money/Context/BundleContext.php b/Money/Context/BundleContext.php index 998f9b3..c2bd1fc 100644 --- a/Money/Context/BundleContext.php +++ b/Money/Context/BundleContext.php @@ -4,6 +4,7 @@ use Brick\Math\BigDecimal; use Brick\Math\BigNumber; +use Brick\Math\RoundingMode; use Brick\Money\Context; use Brick\Money\Currency; @@ -14,7 +15,7 @@ class BundleContext implements Context /** * @inheritdoc */ - public function applyTo(BigNumber $amount, Currency $currency, int $roundingMode) : BigDecimal + public function applyTo(BigNumber $amount, Currency $currency, RoundingMode $roundingMode) : BigDecimal { return $amount->toScale(self::$scale, $roundingMode); } diff --git a/composer.json b/composer.json index 7742b96..874b0de 100644 --- a/composer.json +++ b/composer.json @@ -3,10 +3,10 @@ "description": "Symfony bundle for https://github.com/moneyphp/money", "type": "symfony-bundle", "require": { - "php": "^8.0", + "php": "^8.1", "ext-bcmath": "*", "symfony/framework-bundle": "^6.0 || ^7.0", - "brick/money": ">=0.5.1 <0.9", + "brick/money": "^0.9", "doctrine/orm": "^2.5 || ^3.0", "doctrine/doctrine-bundle": "^2.0" }, From 147092772e6a668c772be52a49c16b4f7af96fb5 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 20:21:13 +0200 Subject: [PATCH 18/34] Fix documentation --- Money/NullableMoney.php | 1 - README.md | 32 +++++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Money/NullableMoney.php b/Money/NullableMoney.php index 56d9815..c9daac7 100644 --- a/Money/NullableMoney.php +++ b/Money/NullableMoney.php @@ -3,7 +3,6 @@ namespace Padam87\MoneyBundle\Money; use Brick\Math\BigDecimal; -use Brick\Money\Context; use Brick\Money\Currency; use Brick\Money\Money; use Padam87\MoneyBundle\Money\Context\BundleContext; diff --git a/README.md b/README.md index e9f7284..dda1f75 100644 --- a/README.md +++ b/README.md @@ -34,19 +34,33 @@ padam87_money: ### Doctrine +#### A) Using the embedded money type + ```php - /** - * @var Money - * - * @ORM\Embedded(class="Money\Money") - */ - private $price; + #[ORM\Embedded(class: EmbeddedMoney::class)] + protected EmbeddedMoney $netPrice; + + public function getNetPrice(): ?Money + { + return ($this->netPrice)(); + } + + public function setNetPrice(?Money $netPrice): self + { + $this->netPrice = new EmbeddedMoney($netPrice); + + return $this; + } +``` + ``` ### Formatting -The bundle adds 2 services. +#### Twig + +`{{ netPrice|money }}` -> €100 -`padam87_money.number_formatter` - A simple `\NumberFormatter` object, with the current request's locale, and currency style. +`{{ netPrice|money_amount }}` -> 100 -`Money\Formatter\IntlMoneyFormatter` - Intl money formatter +`{{ netPrice|money_currency }}` -> € From c16e7e025f5798cb9d054baa9f8e9d5f2361355c Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 20:21:41 +0200 Subject: [PATCH 19/34] Add support for separate mapping --- Entity/Traits/MoneyFromDecimalTrait.php | 32 +++++++++++++++++ README.md | 47 +++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Entity/Traits/MoneyFromDecimalTrait.php diff --git a/Entity/Traits/MoneyFromDecimalTrait.php b/Entity/Traits/MoneyFromDecimalTrait.php new file mode 100644 index 0000000..22d65c8 --- /dev/null +++ b/Entity/Traits/MoneyFromDecimalTrait.php @@ -0,0 +1,32 @@ +getCurrency(), new BundleContext()) : null; + } + + private function setMoney(?BigDecimal &$amount, ?Money $value, bool $strict = true): self + { + if ($strict && !$this->getCurrency()->is($value->getCurrency())) { + throw new \LogicException( + sprintf('Class "%s" has currency of "%s", tried to set "%s"', __CLASS__, $this->getCurrency(), $value->getCurrency()) + ); + } + + $amount = $value->getAmount(); + + return $this; + } +} diff --git a/README.md b/README.md index dda1f75..36867cc 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,53 @@ padam87_money: } ``` +#### B) Using separate fields for amount and currency + +_This is recommended when multiple amounts share the same currency_ + +```php + use MoneyFromDecimalTrait; + + #[ORM\Column(type: 'currency')] + private Currency $currency; + + #[ORM\Column(type: 'decimal_object')] + private ?BigDecimal $netPrice = null; + + #[ORM\Column(type: 'decimal_object')] + private ?BigDecimal $grossPrice = null; + + + public function getCurrency(): Currency + { + return $this->currency; + } + + + public function getNetPrice(): Money + { + return $this->getMoney($this->netPrice); + } + + protected function setNetPrice(Money $netPrice): self + { + $this->setMoney($this->netPrice, $netPrice); + + return $this; + } + + + public function getGrossPrice(): Money + { + return $this->getMoney($this->grossPrice); + } + + protected function setGrossPrice(Money $grossPrice): self + { + $this->setMoney($this->grossPrice, $grossPrice); + + return $this; + } ``` ### Formatting From 887198d526fa4b1a7a1ddb7d12f8d7434ec928ce Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 21:04:42 +0200 Subject: [PATCH 20/34] Handle null currency in trait --- Entity/Traits/MoneyFromDecimalTrait.php | 11 +++++++---- README.md | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Entity/Traits/MoneyFromDecimalTrait.php b/Entity/Traits/MoneyFromDecimalTrait.php index 22d65c8..1559b89 100644 --- a/Entity/Traits/MoneyFromDecimalTrait.php +++ b/Entity/Traits/MoneyFromDecimalTrait.php @@ -5,21 +5,24 @@ use Brick\Math\BigDecimal; use Brick\Money\Currency; use Brick\Money\Money; -use Doctrine\ORM\Mapping as ORM; use Padam87\MoneyBundle\Money\Context\BundleContext; trait MoneyFromDecimalTrait { - abstract public function getCurrency(): Currency; + abstract public function getCurrency(): ?Currency; private function getMoney(?BigDecimal $amount): ?Money { - return $amount ? Money::of($amount, $this->getCurrency(), new BundleContext()) : null; + if ($amount === null || $this->getCurrency() === null) { + return null; + } + + return Money::of($amount, $this->getCurrency(), new BundleContext()); } private function setMoney(?BigDecimal &$amount, ?Money $value, bool $strict = true): self { - if ($strict && !$this->getCurrency()->is($value->getCurrency())) { + if ($strict && $this->getCurrency() !== null && !$this->getCurrency()->is($value->getCurrency())) { throw new \LogicException( sprintf('Class "%s" has currency of "%s", tried to set "%s"', __CLASS__, $this->getCurrency(), $value->getCurrency()) ); diff --git a/README.md b/README.md index 36867cc..c22ec3d 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ _This is recommended when multiple amounts share the same currency_ use MoneyFromDecimalTrait; #[ORM\Column(type: 'currency')] - private Currency $currency; + private ?Currency $currency = null; #[ORM\Column(type: 'decimal_object')] private ?BigDecimal $netPrice = null; @@ -70,7 +70,7 @@ _This is recommended when multiple amounts share the same currency_ private ?BigDecimal $grossPrice = null; - public function getCurrency(): Currency + public function getCurrency(): ?Currency { return $this->currency; } From 099a24734dc50e3c8d1a6bc12502f108c39d8830 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 6 May 2024 22:48:14 +0200 Subject: [PATCH 21/34] Fix currency converter definition --- Resources/config/services.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/config/services.yaml b/Resources/config/services.yaml index e90e35e..679068d 100644 --- a/Resources/config/services.yaml +++ b/Resources/config/services.yaml @@ -43,4 +43,3 @@ services: Brick\Money\CurrencyConverter: arguments: $exchangeRateProvider: '@Padam87\MoneyBundle\Service\DatabaseExchangeRateProvider' - $context: '@Padam87\MoneyBundle\Money\Context\BundleContext' From b6c93af527860eb9ce9f534ffefbe49a5be76a70 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 7 May 2024 00:54:45 +0200 Subject: [PATCH 22/34] Update type declarations --- Doctrine/Type/CurrencyType.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doctrine/Type/CurrencyType.php b/Doctrine/Type/CurrencyType.php index a2941b9..e451c6d 100644 --- a/Doctrine/Type/CurrencyType.php +++ b/Doctrine/Type/CurrencyType.php @@ -19,11 +19,11 @@ public function getName(): string /** * {@inheritdoc} */ - public function getSQLDeclaration(array $column, AbstractPlatform $platform) + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { $column['length'] = $this->getDefaultLength($platform); // enforce column length even if specified - return $platform->getVarcharTypeDeclarationSQL($column); + return $platform->getStringTypeDeclarationSQL($column); } /** From d9347d1084281d9dd26bdbc1c3e97779635e18da Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Fri, 7 Feb 2025 20:48:03 +0100 Subject: [PATCH 23/34] Use number type as basis for decimal --- Form/DecimalType.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index cc49d3d..c7c42c6 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -5,7 +5,7 @@ use Brick\Math\BigDecimal; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; -use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\NumberType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -48,6 +48,7 @@ public function configureOptions(OptionsResolver $resolver): void ->setDefaults( [ 'integer_only' => false, + 'html5' => true, ] ) ; @@ -55,7 +56,7 @@ public function configureOptions(OptionsResolver $resolver): void public function getParent(): ?string { - return TextType::class; + return NumberType::class; } public function getBlockPrefix(): string From 989b1cb65acfd4b4af0b6a2606fc47adc5ea9376 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Mon, 10 Feb 2025 03:49:17 +0100 Subject: [PATCH 24/34] Use configured scale for decimal input --- Form/DecimalType.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index c7c42c6..94e69f8 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -3,6 +3,7 @@ namespace Padam87\MoneyBundle\Form; use Brick\Math\BigDecimal; +use Padam87\MoneyBundle\Money\Context\BundleContext; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\NumberType; @@ -49,6 +50,7 @@ public function configureOptions(OptionsResolver $resolver): void [ 'integer_only' => false, 'html5' => true, + 'scale' => BundleContext::getScale(), ] ) ; From c99316bd46912be677ec1d067aff4e0168a9fd65 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 1 Apr 2025 00:57:27 +0200 Subject: [PATCH 25/34] Use default currency when not set --- Form/MoneyType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Form/MoneyType.php b/Form/MoneyType.php index 09d5258..0fa0e7d 100644 --- a/Form/MoneyType.php +++ b/Form/MoneyType.php @@ -43,7 +43,7 @@ function (?array $formData = null) use ($options): ?Money { return null; } - if (array_key_exists('currency', $formData)) { + if (array_key_exists('currency', $formData) && null !== $formData['currency']) { $currency = $formData['currency']; } else { $currency = Currency::of($options['default_currency_code']); From e51d7751e7c6dd111838343a2625914571a4b5fe Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Fri, 7 Nov 2025 00:01:26 +0100 Subject: [PATCH 26/34] Improve decimal type formatting --- Form/DecimalType.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Form/DecimalType.php b/Form/DecimalType.php index 94e69f8..913931c 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -29,6 +29,10 @@ function (?BigDecimal $modelData = null) use ($options): ?string { return $modelData->getIntegralPart(); } + if ($options['strip_trailing_zeros']) { + $modelData = $modelData->stripTrailingZeros(); + } + return (string) $modelData; }, function (?string $formData): ?BigDecimal { @@ -49,8 +53,8 @@ public function configureOptions(OptionsResolver $resolver): void ->setDefaults( [ 'integer_only' => false, + 'strip_trailing_zeros' => true, 'html5' => true, - 'scale' => BundleContext::getScale(), ] ) ; From f1016978bf96816d7447c22fa513368abb120dea Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Dec 2025 03:51:07 +0100 Subject: [PATCH 27/34] Allow doctrine bundle v3 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 874b0de..cc8cc50 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "symfony/framework-bundle": "^6.0 || ^7.0", "brick/money": "^0.9", "doctrine/orm": "^2.5 || ^3.0", - "doctrine/doctrine-bundle": "^2.0" + "doctrine/doctrine-bundle": "^2.0 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^7.2" From 36cc31d247084736705851eb23895a6ba18bc6dd Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Dec 2025 04:19:32 +0100 Subject: [PATCH 28/34] Add conflict with doctrine persistance BC break https://github.com/doctrine/persistence/pull/448 --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index cc8cc50..02ebc71 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,9 @@ "doctrine/orm": "^2.5 || ^3.0", "doctrine/doctrine-bundle": "^2.0 || ^3.0" }, + "conflict": { + "doctrine/persistence": "3.4.2 || 4.1.1" + }, "require-dev": { "phpunit/phpunit": "^7.2" }, From d3343a672ae6193b91ee4c503979aa126653f137 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Dec 2025 04:24:52 +0100 Subject: [PATCH 29/34] Update decimal object type for compatibility with doctrine 3 --- Doctrine/Type/DecimalObjectType.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Doctrine/Type/DecimalObjectType.php b/Doctrine/Type/DecimalObjectType.php index 2755041..a7780af 100644 --- a/Doctrine/Type/DecimalObjectType.php +++ b/Doctrine/Type/DecimalObjectType.php @@ -4,9 +4,9 @@ use Brick\Math\BigDecimal; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\DecimalType; +use Doctrine\DBAL\Types\Type; -class DecimalObjectType extends DecimalType +class DecimalObjectType extends Type { /** * {@inheritdoc} @@ -24,6 +24,14 @@ public function requiresSQLCommentHint(AbstractPlatform $platform): bool return true; } + /** + * {@inheritdoc} + */ + public function getSQLDeclaration(array $column, AbstractPlatform $platform): string + { + return $platform->getDecimalTypeDeclarationSQL($column); + } + /** * {@inheritdoc} */ From f065ba9dba4cffa1f86f5a27f2d5c51bfb074e33 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Dec 2025 06:51:06 +0100 Subject: [PATCH 30/34] Upgrade to doctrine DBAL 4 --- DependencyInjection/Padam87MoneyExtension.php | 23 +------------------ Doctrine/Type/DecimalObjectType.php | 19 +++++++++------ Padam87MoneyBundle.php | 7 ++++++ composer.json | 5 ++-- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/DependencyInjection/Padam87MoneyExtension.php b/DependencyInjection/Padam87MoneyExtension.php index c47b430..119a413 100644 --- a/DependencyInjection/Padam87MoneyExtension.php +++ b/DependencyInjection/Padam87MoneyExtension.php @@ -3,18 +3,15 @@ namespace Padam87\MoneyBundle\DependencyInjection; use Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver; -use Padam87\MoneyBundle\Doctrine\Type\CurrencyType; -use Padam87\MoneyBundle\Doctrine\Type\DecimalObjectType; use Padam87\MoneyBundle\Money\EmbeddedMoney; use Padam87\MoneyBundle\Money\NullableMoney; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader; -class Padam87MoneyExtension extends Extension implements PrependExtensionInterface, CompilerPassInterface +class Padam87MoneyExtension extends Extension implements CompilerPassInterface { /** * {@inheritdoc} @@ -30,24 +27,6 @@ public function load(array $configs, ContainerBuilder $container): void $container->setParameter('padam87_money.config', $config); } - /** - * {@inheritdoc} - */ - public function prepend(ContainerBuilder $container): void - { - $container->prependExtensionConfig( - 'doctrine', - [ - 'dbal' => [ - 'types' => [ - 'decimal_object' => DecimalObjectType::class, - 'currency' => CurrencyType::class, - ] - ], - ] - ); - } - /** * {@inheritdoc} */ diff --git a/Doctrine/Type/DecimalObjectType.php b/Doctrine/Type/DecimalObjectType.php index a7780af..e56e45b 100644 --- a/Doctrine/Type/DecimalObjectType.php +++ b/Doctrine/Type/DecimalObjectType.php @@ -8,20 +8,17 @@ class DecimalObjectType extends Type { - /** - * {@inheritdoc} - */ - public function getName(): string + public function __construct(private array $config = []) { - return 'decimal_object'; + } /** * {@inheritdoc} */ - public function requiresSQLCommentHint(AbstractPlatform $platform): bool + public function getName(): string { - return true; + return 'decimal_object'; } /** @@ -29,6 +26,14 @@ public function requiresSQLCommentHint(AbstractPlatform $platform): bool */ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { + if (!isset($column['precision'])) { + $column['precision'] = $this->config['precision']; + } + + if (!isset($column['scale'])) { + $column['scale'] = $this->config['scale']; + } + return $platform->getDecimalTypeDeclarationSQL($column); } diff --git a/Padam87MoneyBundle.php b/Padam87MoneyBundle.php index 176fb13..3051721 100644 --- a/Padam87MoneyBundle.php +++ b/Padam87MoneyBundle.php @@ -2,6 +2,9 @@ namespace Padam87\MoneyBundle; +use Doctrine\DBAL\Types\Type; +use Padam87\MoneyBundle\Doctrine\Type\CurrencyType; +use Padam87\MoneyBundle\Doctrine\Type\DecimalObjectType; use Padam87\MoneyBundle\Money\Context\BundleContext; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -12,5 +15,9 @@ public function boot(): void $config = $this->container->getParameter('padam87_money.config'); BundleContext::setScale($config['scale']); + + // @TODO: Keep an eye on https://github.com/doctrine/DoctrineBundle/issues/1867 for a better way to do this. + Type::addType('decimal_object', new DecimalObjectType($config)); + Type::addType('currency', CurrencyType::class); } } diff --git a/composer.json b/composer.json index 02ebc71..367fbde 100644 --- a/composer.json +++ b/composer.json @@ -7,8 +7,9 @@ "ext-bcmath": "*", "symfony/framework-bundle": "^6.0 || ^7.0", "brick/money": "^0.9", - "doctrine/orm": "^2.5 || ^3.0", - "doctrine/doctrine-bundle": "^2.0 || ^3.0" + "doctrine/orm": "^3.0", + "doctrine/doctrine-bundle": "^3.0", + "doctrine/dbal": "^^4.0" }, "conflict": { "doctrine/persistence": "3.4.2 || 4.1.1" From ce0d3f68f8a5ade59bbd94a990f69e753a9d1d51 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Thu, 4 Dec 2025 07:05:35 +0100 Subject: [PATCH 31/34] Fix bad composer version constraint --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 367fbde..e2125c9 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "brick/money": "^0.9", "doctrine/orm": "^3.0", "doctrine/doctrine-bundle": "^3.0", - "doctrine/dbal": "^^4.0" + "doctrine/dbal": "^4.0" }, "conflict": { "doctrine/persistence": "3.4.2 || 4.1.1" From b72b51691fe14aa0b59d4313cc3abfc48ad9c5af Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Tue, 9 Dec 2025 00:53:47 +0100 Subject: [PATCH 32/34] Check if type already has decimal and currency --- Padam87MoneyBundle.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Padam87MoneyBundle.php b/Padam87MoneyBundle.php index 3051721..2c926b9 100644 --- a/Padam87MoneyBundle.php +++ b/Padam87MoneyBundle.php @@ -17,7 +17,12 @@ public function boot(): void BundleContext::setScale($config['scale']); // @TODO: Keep an eye on https://github.com/doctrine/DoctrineBundle/issues/1867 for a better way to do this. - Type::addType('decimal_object', new DecimalObjectType($config)); - Type::addType('currency', CurrencyType::class); + if (!Type::hasType('decimal_object')) { + Type::addType('decimal_object', new DecimalObjectType($config)); + } + + if (!Type::hasType('currency')) { + Type::addType('currency', CurrencyType::class); + } } } From 8f8a77b566b0ebc15f4ffb731350f65fa71f4080 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Fri, 30 Jan 2026 22:22:49 +0100 Subject: [PATCH 33/34] Update composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e2125c9..341e1c2 100644 --- a/composer.json +++ b/composer.json @@ -3,9 +3,9 @@ "description": "Symfony bundle for https://github.com/moneyphp/money", "type": "symfony-bundle", "require": { - "php": "^8.1", + "php": "^8.4", "ext-bcmath": "*", - "symfony/framework-bundle": "^6.0 || ^7.0", + "symfony/framework-bundle": "^6.0 || ^7.0 || ^8.0", "brick/money": "^0.9", "doctrine/orm": "^3.0", "doctrine/doctrine-bundle": "^3.0", From 39eef442544e7f269ba17a4072e9f6d3b1a80aa6 Mon Sep 17 00:00:00 2001 From: Adam Prager Date: Sat, 31 Jan 2026 02:19:27 +0100 Subject: [PATCH 34/34] Code upgrade PHP 84 --- DependencyInjection/Configuration.php | 1 - DependencyInjection/Padam87MoneyExtension.php | 4 ++-- Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php | 5 +---- Entity/Traits/MoneyFromDecimalTrait.php | 2 +- Form/CurrencyType.php | 9 ++------- Form/DecimalType.php | 1 - Service/DatabaseExchangeRateProvider.php | 5 +---- Service/MoneyFormatter.php | 10 +--------- Twig/Extension/MoneyExtension.php | 16 ++++------------ 9 files changed, 12 insertions(+), 41 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 042d09b..49eac9b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2,7 +2,6 @@ namespace Padam87\MoneyBundle\DependencyInjection; -use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; diff --git a/DependencyInjection/Padam87MoneyExtension.php b/DependencyInjection/Padam87MoneyExtension.php index 119a413..744cebc 100644 --- a/DependencyInjection/Padam87MoneyExtension.php +++ b/DependencyInjection/Padam87MoneyExtension.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\DependencyInjection; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Padam87\MoneyBundle\Doctrine\Mapping\Driver\MoneyEmbeddedDriver; use Padam87\MoneyBundle\Money\EmbeddedMoney; use Padam87\MoneyBundle\Money\NullableMoney; @@ -9,7 +10,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; -use Symfony\Component\DependencyInjection\Loader; class Padam87MoneyExtension extends Extension implements CompilerPassInterface { @@ -21,7 +21,7 @@ public function load(array $configs, ContainerBuilder $container): void $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yaml'); $container->setParameter('padam87_money.config', $config); diff --git a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php index dc49295..feb96da 100644 --- a/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php +++ b/Doctrine/Mapping/Driver/MoneyEmbeddedDriver.php @@ -9,11 +9,8 @@ class MoneyEmbeddedDriver implements MappingDriver { - private array $config; - - public function __construct(array $config) + public function __construct(private array $config) { - $this->config = $config; } /** diff --git a/Entity/Traits/MoneyFromDecimalTrait.php b/Entity/Traits/MoneyFromDecimalTrait.php index 1559b89..0109bcb 100644 --- a/Entity/Traits/MoneyFromDecimalTrait.php +++ b/Entity/Traits/MoneyFromDecimalTrait.php @@ -24,7 +24,7 @@ private function setMoney(?BigDecimal &$amount, ?Money $value, bool $strict = tr { if ($strict && $this->getCurrency() !== null && !$this->getCurrency()->is($value->getCurrency())) { throw new \LogicException( - sprintf('Class "%s" has currency of "%s", tried to set "%s"', __CLASS__, $this->getCurrency(), $value->getCurrency()) + sprintf('Class "%s" has currency of "%s", tried to set "%s"', self::class, $this->getCurrency(), $value->getCurrency()) ); } diff --git a/Form/CurrencyType.php b/Form/CurrencyType.php index 5e128d9..da24228 100644 --- a/Form/CurrencyType.php +++ b/Form/CurrencyType.php @@ -11,11 +11,8 @@ class CurrencyType extends AbstractType { - private array $config; - - public function __construct(array $config) + public function __construct(private array $config) { - $this->config = $config; } /** @@ -26,9 +23,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder ->addModelTransformer( new CallbackTransformer( - function (?Currency $modelData = null): ?string { - return $modelData !== null ? $modelData->getCurrencyCode() : null; - }, + fn(?Currency $modelData = null): ?string => $modelData !== null ? $modelData->getCurrencyCode() : null, function ($formData): ?Currency { if ($formData === null) { return null; diff --git a/Form/DecimalType.php b/Form/DecimalType.php index 913931c..5f42b5c 100644 --- a/Form/DecimalType.php +++ b/Form/DecimalType.php @@ -3,7 +3,6 @@ namespace Padam87\MoneyBundle\Form; use Brick\Math\BigDecimal; -use Padam87\MoneyBundle\Money\Context\BundleContext; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Extension\Core\Type\NumberType; diff --git a/Service/DatabaseExchangeRateProvider.php b/Service/DatabaseExchangeRateProvider.php index bb78ba9..46573cb 100644 --- a/Service/DatabaseExchangeRateProvider.php +++ b/Service/DatabaseExchangeRateProvider.php @@ -11,11 +11,8 @@ class DatabaseExchangeRateProvider implements ExchangeRateProvider { - private $doctrine; - - public function __construct(ManagerRegistry $doctrine) + public function __construct(private ManagerRegistry $doctrine) { - $this->doctrine = $doctrine; } public function getExchangeRate(string $sourceCurrencyCode, string $targetCurrencyCode): BigNumber|int|float|string diff --git a/Service/MoneyFormatter.php b/Service/MoneyFormatter.php index 6705c99..c5ba629 100644 --- a/Service/MoneyFormatter.php +++ b/Service/MoneyFormatter.php @@ -4,21 +4,13 @@ use Brick\Money\Currency; use Brick\Money\Money; -use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Intl\Currencies; class MoneyFormatter { - private RequestStack $requestStack; - private string $defaultLocale; - private array $config; - - public function __construct(RequestStack $requestStack, string $defaultLocale, array $config) + public function __construct(private RequestStack $requestStack, private string $defaultLocale, private array $config) { - $this->requestStack = $requestStack; - $this->defaultLocale = $defaultLocale; - $this->config = $config; } public function format(?Money $money, ?int $digits = null): ?string diff --git a/Twig/Extension/MoneyExtension.php b/Twig/Extension/MoneyExtension.php index 309f29e..e2bff9f 100644 --- a/Twig/Extension/MoneyExtension.php +++ b/Twig/Extension/MoneyExtension.php @@ -2,6 +2,7 @@ namespace Padam87\MoneyBundle\Twig\Extension; +use Brick\Math\BigNumber; use Brick\Money\Currency; use Brick\Money\CurrencyConverter; use Brick\Money\Money; @@ -13,13 +14,8 @@ class MoneyExtension extends AbstractExtension { - private MoneyFormatter $formatter; - private CurrencyConverter $converter; - - public function __construct(MoneyFormatter $formatter, CurrencyConverter $converter) + public function __construct(private MoneyFormatter $formatter, private CurrencyConverter $converter) { - $this->formatter = $formatter; - $this->converter = $converter; } public function getFilters(): array @@ -49,12 +45,8 @@ function ($currency): ?string { public function getFunctions(): array { return [ - new TwigFunction('money', function ($amount, $currency): Money { - return Money::of($amount, $currency); - }), - new TwigFunction('currency', function ($code): Currency { - return Currency::of($code); - }), + new TwigFunction('money', fn(BigNumber|int|float|string $amount, Currency|string|int $currency): Money => Money::of($amount, $currency)), + new TwigFunction('currency', fn(string|int $code): Currency => Currency::of($code)), ]; } }