loading...

Laravel validation rules — ISBN, ends with string, even & odd numbers, and lower-upper-titlecase…

mattkingshott profile image Matt Kingshott 👨🏻‍💻 Originally published at itnext.io on ・5 min read

Image courtesy of Unsplash

Laravel validation rules — ISBN, ends with string, even & odd numbers, and lower-upper-titlecase strings

In this new series, we’ll be exploring the concept of custom validation rules in Laravel and how they can assist you. I’m posting an article each day with a new rule you can use in your projects. The rules are also part of a package.

In this post, we’ll be creating a bunch of what I like to call “utility” rules. You may not be likely to reach for them very often, but when that specific use case comes along, it’s one less thing to worry about. The rules we’re reviewing are:

  1. ISBN-10 and ISBN-13 numbers.
  2. Ends with a string (Laravel includes a “starts with”, but no “ends with”).
  3. Even and odd numbers.
  4. Lower, upper and title case strings.

If you’re new to this topic, check out the first article in the series, as well as this article to learn how to use parameters in your rules.

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 and Site Monitoring with Pulse

Rule #1 — ISBN Numbers

While you might think that writing the logic involved for validating an ISBN number is simple, you’d be wrong. Part of this stems from ISBN existing in two formats 10 and 13, but it’s also the case that you can display it in a range of ways (with or without spaces, with or without the ISBN prefix etc.)

If you’re interested in learning more about the complexity of validating ISBN numbers, check out this article on the O’Reilly website.

NOTE : Technically, it is possible for the number to be valid, but the final character to be invalid if the checksum is wrong. If this is of concern to you, you can always implement a PHP version of the solution shown via the link above.

Needless to say, the quickest approach is to use a regular expression to see if an ISBN is likely to be valid, so let’s insert that logic into the passes method:

public function passes($attribute, $value)
{
    return preg\_match(
        "/^(?:ISBN(-1(?:(0)|3))?:?\ )?(?(1)(?(2)(?=[0-9X]{10}$|(?=
         (?:[0-9]+[-]){3})[- 0-9X]{13}$)[0-9]{1,5}[-]?[0-9]+[-]?
         [0-9]+[-]?[0-9X]|(?=[0-9]{13}$|(?=(?:[0-9]+[-]){4})[- 0-
         9]{17}$)97[89][-]?[0-9]{1,5}[-]?[0-9]+[-]?[0-9]+[-]?[0-
         9])|(?=[0-9X]{10}$|(?=(?:[0-9]+[-]){3})[- 0-9X]
         {13}$|97[89][0-9]{10}$|(?=(?:[0-9]+[-]){4})[- 0-9]{17}$)
         (?:97[89][-]?)?[0-9]{1,5}[-]?[0-9]+[-]?[0-9]+[-]?[0-
         9X])$/", $value
    );
}

Next, we’ll need to write an error message to respond with when the user has supplied a value which is not a valid ISBN number:

public function message()
{
    return 'The :attribute must be a valid ISBN number';
}

As before, we’ll write a quick unit test to confirm that the rule works correctly and rejects values that are not ISBN numbers:

/\*\* @test \*/
public function the\_isbn\_rule\_can\_be\_validated()
{
    $rule = ['book' => [new ISBN]];

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

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

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

    $this->assertTrue(validator(['book' => 'ISBN 978-0-596-52068-7'], $rule)->passes());

    $this->assertTrue(validator(['book' => 'ISBN-13: 978-0-596-52068-7'], $rule)->passes());

    $this->assertTrue(validator(['book' => '978 0 596 52068 7'], $rule)->passes());

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

    $this->assertTrue(validator(['book' => 'ISBN-10 0-596-52068-9'], $rule)->passes());

    $this->assertTrue(validator(['book' => '0-596-52068-9'], $rule)->passes());

}

Rule #2 — Ends With

Next, we’ll add a companion rule to Laravel’s native “starts with” rule. As you would imagine, the logic is absurdly simple. We use the native function in the string library and compare the supplied value against the single parameter:

public function passes($attribute, $value)
{
    Str::endsWith($value, $this->parameters[0]);
}

As before, we’ll need to write an error message to respond with:

public function message()
{
    return 'The :attribute must end with the text "' . 
           $this->parameters[0] . '"';
}

And finally, include a unit test:

/\*\* @test \*/
public function the\_ends\_with\_rule\_can\_be\_validated()
{
    $rule = ['text' => [new EndsWith('world')]];

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

    $this->assertTrue(validator(['text' => 'hello world'], $rule)->passes());

    $this->assertFalse(validator(['text' => 'hello worldy'], $rule)->passes());
}

Rule #3, #4 — Odd & Even Numbers

Next, we’ll add a pair of rules that enforce the requirement that a user supply either an odd or an even number.

Like the previous rule, the logic is incredibly simple. So much so, that I’ve merged the checks into single code blocks below to make the article shorter, though in the reality these checks would have their own separate rule.

public function passes($attribute, $value)
{
    // Odd numbers
    return intval($value) % 2 !== 0;

    // Even numbers
    return intval($value) % 2 === 0;
}

We’ll also include the error response(s):

public function message()
{
    return 'The :attribute must be an odd number';
           'The :attribute must be an even number';
}

And the unit test(s):

/\*\* @test \*/
public function the\_odd\_even\_number\_rule\_can\_be\_validated()
{

    // ===========    
    // ODD NUMBERS
    // ===========

    $rule = ['number' => [new OddNumber]];

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

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

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

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


    // ============
    // EVEN NUMBERS
    // ============

    $rule = ['number' => [new EvenNumber]];

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

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

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

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

}

Rule #5, #6, #7 — Upper, Lower & Titlecase strings

Finally, we’ll add a set of rules that enforce the requirement that a user supply either an uppercase, lowercase or titlecase string.

As before, we’ll keep this article shorter by merging the logic into a single code blocks. Since the functions used will be common to almost all PHP developers, no further explanation should be necessary as to how they work:

public function passes($attribute, $value)
{
    // Lowercase
    return mb\_strtolower($value) === $value;

    // Titlecase
    return ucwords($value) === $value;

    // Uppercase
    return mb\_strtoupper($value) === $value;
}

We’ll also include the error response(s):

public function message()
{
    return 'The :attribute must be entirely uppercase text';
           'The :attribute must be entirely lowercase text';
           'Words in :attribute must begin with a capital letter';
}

And the unit test(s):

/\*\* @test \*/
public function the\_upper\_lower\_titlecase\_rule\_can\_be\_validated()
{

    // =========    
    // LOWERCASE
    // =========

    $rule = ['text' => [new Lowercase]];

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

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


    // =========
    // UPPERCASE
    // =========

    $rule = ['text' => [new Uppercase]];

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

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


    // =========
    // TITLECASE
    // =========

    $rule = ['text' => [new Titlecase]];

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

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

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

    $this->assertTrue(validator(['text' => 'Hello World'], $rule)->passes());

    $this->assertTrue(validator(['text' => 'HELLO WORLD'], $rule)->passes());

}

Wrapping Up

You can see the classes and pull them 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