Handling Money


In this video we are going to cover the Money input type for Symfony's form. This is a fairly straightforward field type with likely only one thing that may be initially confusing - the divisor.

Now, working with money in software development terms is open to a wide degree of interpretation. As an easy starting point, the MoneyType form field is good enough to get going, but be aware there are entire bundles devoted to handling money.

Adding a MoneyType to your form is very straightforward:

<?php

//src/AppBundle/Form/Type/ProductType.php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ProductType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('money', MoneyType::class)
            ->add('save', SubmitType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\Product'
        ]);
    }
}

Fundamentally, nothing we haven't seen already in this series.

The MoneyType has a few options though - be sure to check the docs for the latest list.

Changing these up may look as follows:

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('money', MoneyType::class, [
                'currency' => 'JPY',    // default value is 'EUR'
                'divisor'  => 1000,     // default value is 1
                'scale'    => 0,        // default value is 2
            ])
            ->add('save', SubmitType::class)
        ;
    }

Again, nothing particularly new to us at this point in the course.

The currency text is the ISO 4217 currency code - GBP, USD, EUR, that sort of thing.

What's more interesting is the divisor, which is initially a little confusing until you play around with it.

On initial submission of data, the divisor doesn't do anything unusual. So imagine we have the form above, and we submit a value of '10000' as our money amount.

We would expect to see a value of '10000' in the database. This is the sort of thing you want to have covered by tests, and also entity validation as much as possible.

Let's imagine that this data got saved off with a row id of 3.

Later on, our user comes back and decides they want to edit this data. Via some CRUD setup, they can see their entries and choose record id: 3 from the list, which in turn loads their data back from the database.

We can represent this in code without doing a real DB call:

public function formEditExampleAction(Request $request)
{
    $existingProduct = new Product();
    $existingProduct->setMoney(9999.99);

    $form = $this->createForm(ProductType::class, $existingProduct);

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        // other stuff, not relevant here
    }

    return $this->render(':form-example:index.html.twig', [
        'form' => $form->createView()
    ]);
}

Again, if unsure then do watch this video where all this was covered already.

However, when our form reloads, the divisor will divide the value of 9999.99 by 1000 in our case, as per the form definition.

It will then round the value up, as we have set our scale to 0.

This would then display as 10 as the existing value on our form field.

This is exactly the sort of thing that can and will confuse end users - particularly if returning to a long form to update / modify their partially saved data - if not properly explained.

Code For This Course

Get the code for this course.

Episodes