loading...

Laravel validation rule — money

mattkingshott profile image Matt Kingshott 👨🏻‍💻 Originally published at Medium on ・4 min read

Image courtesy of Unsplash

Laravel validation rule — money

In this new series, we’ll be exploring the concept of custom validation rules in Laravel and why you’ll want to use them. I’m aiming to release a new article each day containing a new rule which you can use in your own projects. I’m also collecting these rules into a package that you can pull in via composer.

In this article, we’ll be confirming that the user has provided a valid financial / monetary figure for a given field.

Recap

If you’re unfamiliar with the reasoning behind creating custom rules to handle data validation, check out the intro section from the first article in this series.

You may also want to review the section on creating a rule (in the same post), as we won’t be repeating the mechanics of each method in your validation classes, we’ll just be exploring the code required to make the rule work.

Promotion

I’ve recently released Pulse — a friendly, affordable server and site monitoring service designed specifically for developers. It includes all the usual hardware monitors, custom service monitors, alerting via various notification channels, logs, as well as full API support. Take a look… https://pulse.alphametric.co

Server & Site Monitoring with Pulse

The check and response

First of all, let’s take a moment to outline what we expect a user to provide for a monetary figure... it should consist of a single currency symbol followed by a variably-sized floating point value.

As with our record owner validation rule, we’ll need to be able to set a few parameters for the rule to work properly. The first parameter should be the currency symbol, while the second should specify a maximum number of digits before the decimal point, and the third should specify an maximum number of digits after the decimal point (this is, of course, optional).

As with the strong password rule we created, the easiest way to enforce the requirements, is to use a regular expression:

public function passes($attribute, $value)
{
    // Extract the parameters
    $symbol = $this->parameters[0];
    $before = $this->parameters[1];
    $after = $this->parameters[2];

    // Perform the check
    return preg\_match(
        "/^\\{$symbol}[0-9]{1,{$before}}(\.[0-9]{1,{$after}})?$/", $value
    );
}

The expression checks for the required symbol, then one or more digits up to the given limit before the decimal point, then one or more digits up to the given limit after the decimal point (but does not require a value be present).

Next, we’ll need to write an error message to return when the user has not supplied a valid monetary figure:

public function message()
{
    return 'The :attribute must be a monetary figure';
}

A more helpful error

While a static error message is sufficient, we can go a step further. Wouldn’t it be better if we supplied an example value? Of course it would! Let’s create a separate method that generates a valid example and include it in the error:

public function example()
{
    return $this->parameters[0] .
           mt\_rand(1, (int) str\_repeat('9', $this->parameters[1])) . 
           '.' .
           mt\_rand(1, (int) str\_repeat('9', $this->parameters[2]));
}

All we need to do now, is modify the message function:

public function message()
{
    return 'The :attribute must be a monetary figure e.g. ' . 
           $this->example();
}

Testing it works

As before, we’ll write a quick unit test to confirm that the regular expression works correctly and rejects invalid monetary figures:

/\*\* @test \*/
public function a\_monetary\_figure\_can\_be\_validated()
{
    $rule = ['deposit' => [new MonetaryFigure('$', 4, 4)]];

    $this->assertFalse(validator(['deposit' => '$'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$123'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1234'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '$12345'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '$1234.'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1234.1'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1234.12'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1234.123'], $rule)->passes());

    $this->assertTrue(validator(['deposit' => '$1234.1234'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '$1234.12345'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '$12345.1234'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '$abcd.efgh'], $rule)->passes());

    $this->assertFalse(validator(['deposit' => '£1234.1234'], $rule)->passes());

}

Since we also have a method that provides an example value, we should write a test to confirm it produces something valid:

public function a\_monetary\_figure\_example\_is\_valid()
{
    $rule = ['deposit' => [$class = new MonetaryFigure('$', 4, 2)]];

    $this->assertTrue(validator(['deposit' => $class -> example()], $rule)->passes());
}

Wrapping Up

We now have a reusable validation rule to ensure that users supply a valid monetary figure when we require it. We also respond with a suitable error message when the check fails, and we have a test to ensure the rule works.

You can see the complete class and pull it into your projects by visiting the repo on Github: https://github.com/alphametric/laravel-validation-rules

I have additional validation rules that I intend to share with you in the coming days, so be sure to follow me for those articles. If you’re interested, you can also follow me on Twitter to see everything I’m up to.

Happy coding!

Posted on by:

mattkingshott profile

Matt Kingshott 👨🏻‍💻

@mattkingshott

Founder. Developer. Writer. Lunatic. Created Pulse, IodineJS, Axiom, and more. #PHP #Laravel #Vue #TailwindCSS

Discussion

markdown guide