loading...

Laravel validation rules - location coordinates (lat / long), country codes & language codes

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

Image courtesy of Unsplash

Laravel validation rules— location coordinates (lat / long), country codes & language codes

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 rule which confirms that a user has supplied a valid set of latitude and longitude coordinates, a rule to confirm a user has supplied an ISO country code, and a rule to confirm an ISO language code.

If you’re new to this topic, check out the first article in the series.

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 — Location Coordinates

Let’s begin by writing the logic necessary to ensure that the user has provided valid coordinates. To be considered correct, the following must be true:

  1. The latitude component must be between -90 and 90. It may possess any number of decimalised digits, or it may be a whole number.
  2. The longitude component must be between -180 and 180. It may possess any number of decimalised digits, or it may be a whole number.
  3. Technically, you can include any number of digits after the decimal point. However, once you go past 8, the level of accuracy (less than a millimeter) becomes superfluous in all but a highly-specialized number of fields.

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

public function passes($attribute, $value)
{
    return preg\_match("/^[-]?((([0-8]?[0-9])(\.(\d{1,8}))?)|(90(\.0+)?)),\s?[-]?((((1[0-7][0-9])|([0-9]?[0-9]))(\.(\d{1,8}))?)|180(\.0+)?)$/", $value);
}

The expression allows a user to provide a latitude, followed by a comma, then an optional space, followed by the longitude. The expression also does not require the user to provide decimals, however, if they do, it enforces a limit of 8 digits (see point #3 above).

Next, we’ll need to write an error message to respond with when the user has supplied a set of coordinates that do not meet the criteria:

public function message()
{
    return 'The :attribute must be a valid set of latitude and 
            longitude coordinates, with a limit of 8 digits after a 
            decimal point';
}

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

/\*\* @test \*/
public function a\_set\_of\_location\_coordinates\_can\_be\_validated()
{
    $rule = ['location' => [new LocationCoordinates]];

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

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

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

    $this->assertTrue(validator(['location' => '90.0,180.0'], $rule)->passes());

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

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

    $this->assertTrue(validator(['location' => '-77.0364335, 38.8951555'], $rule)->passes());

    $this->assertTrue(validator(['location' => '-77.03643357, 38.8951555'], $rule)->passes());

    $this->assertTrue(validator(['location' => '-77.0364335, 38.89515557'], $rule)->passes());

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

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

}

Rule #2 — Country Codes

There are two generally-agreed upon standards for country codes, ISO 3166–1 alpha-2 and alpha-3. The alpha component refers to the number of letters in the country code, two or three.

Therefore, for us to confirm that a user has supplied a valid country code, we need to supply a parameter indicating which alpha standard to use. After that, it’s just a matter of checking an array for the presence of the code:

public function passes($attribute, $value)
{
    $array = ($this->parameters[0] ?? 2) == 2 
             ? $this->two\_letters : $this->three\_letters;

    return array\_key\_exists(strtoupper($value), $array);
}

You’ll see code referring to two class variables, two_letters and three_letters. These are arrays containing a list of the country codes. I’ve opted not to include them here for brevity, but you can view them here.

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

public function message()
{
    return 'The :attribute must be a valid ISO 3166-1 alpha-' .
           ($this->parameters[0] ?? 2) . ' country code';
}

And finally, include a unit test:

/\*\* @test \*/
public function a\_two\_letter\_country\_code\_can\_be\_validated()
{
    $rule = ['code' => [new CountryCode(2)]];

    $this->assertTrue(validator(['code' => 'US'], $rule)->passes());     
    $this->assertTrue(validator(['code' => 'VU'], $rule)->passes());
    $this->assertFalse(validator(['code' => 'xx'], $rule)->passes());

    $rule = ['code' => [new CountryCode(3)]];

    $this->assertTrue(validator(['code' => 'SAU'], $rule)->passes());     
    $this->assertTrue(validator(['code' => 'SDN'], $rule)->passes());
    $this->assertFalse(validator(['code' => 'xxx'], $rule)->passes());
}

Rule #2 — Language Codes

As with country codes, there are two generally-agreed upon standards for language codes, ISO 639–1 alpha-2 and alpha-3. Likewise, the alpha part refers to the number of letters in the language code.

We can therefore, reuse the logic from the country code validation rule and simply update the contents of the arrays / the error message:

public function message()
{
    return 'The :attribute must be a valid ISO 639-1 alpha-' .
           ($this->parameters[0] ?? 2) . ' language code';
}

And finally, revise the unit test:

/\*\* @test \*/
public function a\_two\_letter\_language\_code\_can\_be\_validated()
{
    $rule = ['code' => [new LanguageCode(2)]];

$this->assertTrue(validator(['code' => 'EL'], $rule)->passes());     
    $this->assertTrue(validator(['code' => 'KL'], $rule)->passes());
    $this->assertFalse(validator(['code' => 'xx'], $rule)->passes());

    $rule = ['code' => [new LanguageCode(3)]];

    $this->assertTrue(validator(['code' => 'RUN'], $rule)->passes());     
    $this->assertTrue(validator(['code' => 'RUS'], $rule)->passes());
    $this->assertFalse(validator(['code' => 'xxx'], $rule)->passes());
}

Wrapping Up

You can see the complete 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