EasyAdminBundle icon indicating copy to clipboard operation
EasyAdminBundle copied to clipboard

Unsupported moneyphp/money package by MoneyField

Open Troi opened this issue 11 months ago • 3 comments

Describe the bug I need to work with Money object in all lifecycle of entities but MoneyField doesn't support that. It is clearly issue of MoneyConfigurator.

TypeError:
Unsupported operand types: Money\Money / int

  at vendor/easycorp/easyadmin-bundle/src/Field/Configurator/MoneyConfigurator.php:54
  at EasyCorp\Bundle\EasyAdminBundle\Field\Configurator\MoneyConfigurator->configure(object(FieldDto), object(EntityDto), object(AdminContext))
     (vendor/easycorp/easyadmin-bundle/src/Factory/FieldFactory.php:107)
  at EasyCorp\Bundle\EasyAdminBundle\Factory\FieldFactory->processFields(object(EntityDto), object(FieldCollection))
     (vendor/easycorp/easyadmin-bundle/src/Factory/EntityFactory.php:43)
  at EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory->processFields(object(EntityDto), object(FieldCollection))
     (vendor/easycorp/easyadmin-bundle/src/Controller/AbstractCrudController.php:217)
  at EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController->edit(object(AdminContext))
     (vendor/symfony/http-kernel/HttpKernel.php:181)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:76)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:197)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/var/www/html/vendor/autoload_runtime.php')
     (public/index.php:5)

To Reproduce Composer: moneyphp/money: v4.5.0 MoneyType is my Docrine Mapper between DB and Money object. Entity:

#[ORM\Column(type: MoneyType::TYPE, nullable: true)]
    private ?Money $absoluteDiscount = null;

public function getAbsoluteDiscount(): ?Money
    {
        return $this->absoluteDiscount;
    }

    public function setAbsoluteDiscount(?Money $absoluteDiscount): static
    {
        $this->absoluteDiscount = $absoluteDiscount;

        return $this;
    }

CrudController:

yield EAField\MoneyField::new('absoluteDiscount', 'Discount')
            ->setCurrency('CZK');

Troi avatar Mar 22 '24 15:03 Troi

It accepts int which is fine, it shouldn't relay on some 3rd party libraries tbh.

You could add some conversion between int and the Money object to your entity and be fine I think:

public function getAbsoluteDiscountInt(): ?int
{
    if (!$this->absoluteDiscount) {
       return null;
    }
   
     return (int)$this->absoluteDiscount->getAmount()
}

public function setAbsoluteDiscountInt(?int $absoluteDiscount ): self
{
    if (!$absoluteDiscount) {
       return $this;
    }

    $this->absoluteDiscount = Money::CZK($absoluteDiscount);
   
     return $this;
}

and then use that in the CRUD:

yield EAField\MoneyField::new('absoluteDiscountInt', 'Discount')
            ->setCurrency('CZK');

That probably should fix it (provided your money values do not exceed Max. 64 bit int)

KDederichs avatar Mar 23 '24 09:03 KDederichs

It accepts int which is fine, it shouldn't relay on some 3rd party libraries tbh.

You could add some conversion between int and the Money object to your entity and be fine I think:

I can but it makes entities quite mess and defeats the purpose to have encapsulated inner logic.

I understand you don't want to rely on 3rd party, but optional dependency would be possible too. Or do you link my repo if I make small toolbox/extensions?

I think this good candidate to ready to go solution. Prices as int in application are antipattern and making a lot of mess in case of adding new currencies to application (and it happened on every project I worked on)

Troi avatar Mar 27 '24 15:03 Troi

@Troi the way to make it work is by using the property path syntax:

// baseAmount is my Money instance exposed with a `getBaseAmount(): Money` method in my entity class

MoneyField::new('baseAmount.amount', 'Base Amount')
    ->setNumDecimals(2)
    ->setStoredAsCents()
    ->setCurrencyPropertyPath('baseAmount.currency.code'),

hhamon avatar Aug 27 '24 07:08 hhamon