Search

How to Create Middleware to Handle Country Specific URLs in Laravel

Listen to this article

country-directionsNot all PHP developers are fans of Laravel. Well that’s probably because most of them haven’t worked with it.  I’ve been working with the framework for more than a year now. Sure there were some teething troubles initially. But the framework has now grown on me.

When it comes to large-scale applications especially, development and management is simplified. And you’ve always got fellow developers helping you out with hurdles along the way.

So, looking at how Laravel tips and tricks can help others, I decided to write this post on a recent piece of code I recently wrote.

Handling Country Specific URLs

Many-a-times, e-stores or websites across the web, service different countries or regions. Possibly to provide users with a more personalized experience. For example, websites might change their language, currency, or filter products based on the country the visitor is from.

In such a case, there is a need to handle country specific URLs. So, if you take the case of Amazon, there’s Amazon.com, Amazon.es, Amazon.de, and so on, that serve content in different languages. There could also be a case that a person browsing from Spain might search for Amazon.com, but would have to be redirected to the more relevant, Amazon.es.

Such a situation is pretty common, and if you had a similar requirement when working with Laravel, here’s what you would need to do.

Do note, the below code has been tested on Laravel 5.3 and 5.2.

So first we need to start by creating a country table in the database to store the country name and country shortcode. This information will be used to handle the URLs.

Create Migration for Country Table

Start by migrating to your project folder; in your command prompt type:

php artisan make:migration country_table

This will create a migration file in database/migration/ folder. Next, open county_table migration file and enter the following:

(Note: the Schema used here is only for demonstration purpose and you need to make the necessary changes according to your  database architecture)

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CountryTable extends Migration
{
   /**
    * Run the migrations.
    */
   public function up()
   {
       Schema::create('country', function (Blueprint $table) {
           $table->increments('id');
           $table->string('country_name');
           $table->string('country_shortcode')->unique();  // the country shortcode that will be used in the URL
           $table->tinyinteger('is_active');   // to enable or disable a country(1 for enable , 0 for disable)
       });
   }

   /**
    * Reverse the migrations.
    */
   public function down()
   {
       Schema::drop('country');
   }
}

Create Country Model

php artisan make:model Country

Open the model file which is created in App folder and make necessary changes:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Country extends Model
{
   /**
    * The table associated with the model.
    *
    * @var string
    */
   protected $table = 'country';
}

Create Seeder File for Country Table

php artisan make:seeder CountrySeeder

Open the CountrySeeder file present in database/seeds/ folder and enter the following:

<?php

use Illuminate\Database\Seeder;

class CountrySeeder extends Seeder {
  /**
  * Run the database seeds.
  *
  * @return void
  */
  public function run() {
    DB::table('country')->insert([
    [
      'country_name' => 'India',
      'country_shortcode' => 'in',
      'is_active' => 1,
    ],
    [
      'country_name' => 'United States',
      'country_shortcode' => 'us',
      'is_active' => 1,
    ],
    [
      'country_name' => 'United Kingdom',
      'country_shortcode' => 'uk',
      'is_active' => 1,
    ],
    ]);
  }
}

The above code will create entries in country table for India, United States and United Kingdom. You will need to add countries as per your requirements.

Include the following line under run() function in database/seeds/DatabaseSeeder.php file:

$this->call(CountrySeeder::class);

Migrate Refresh  Tables and Seed them

This will delete all tables and create them once again along with the country table and will seed it with the seeder data.

Note: Do NOT use refresh on a production site, as it will wipe out all your data!

php artisan migrate:refresh --seed

Create Country Middleware

The middleware will check for the validity of the country in the URL and if valid, will set the corresponding country in a country session or if invalid will redirect to ‘/’ router.

php artisan make:middleware CountryMiddleware

This will create Country middleware file in app/http/middleware folder. Open the folder and then enter in the following code.

<?php

namespace App\Http\Middleware;

use App\Country;  //country model
use Closure;
use Request;
use Route;

class CountryMiddleware
{
   /**
    * Handle an incoming request.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Closure                 $next
    *
    * @return mixed
    */
   public function handle($request, Closure $next)
   {
       $countryShortcode = $request->route('country');  //get country part from url
       $country = Country::where('country_shortcode', '=', $countryShortcode)->where('is_active', '=', 1)->first();
       if ($country === null) {
           return redirect('/');
       }
       $request->session()->put('country', $country);
       $request->session()->save();
       return $next($request);
   }
}

In certain  cases you would need to redirect a page to a particular country. For example, suppose you want to redirect a person on login to a country specific page based on his location.

In such a case what you can do, is set a redirect_to_country (choose your variable name) session while the action is performed (in our example- login) and check the value of the session in country middleware.

Modify the handle function  as shown bellow:

<?php
public function handle($request, Closure $next)
   {
       $countryShortcode = $request->route('country');
       $routeName = $request->route()->getName();
       $routeParameters = $request->route()->parameters();
       if ($request->session()->has('redirect_to_country')) {
           $redirectTo = $request->session()->get('redirect_to_country');
           if ($country === $redirectTo) {
               $request->session()->forget('redirect_to_country');
           } else {
               $routeParameters['country'] = $redirectTo;
               return redirect()->route($routeName, $routeParameters);
           }
       }
       $country = Country::where('country_shortcode', '=', $countryShortcode)->where('is_active', '=', 1)->first();
       if ($country === null) {
           return redirect('/');
       }
       $request->session()->put('country', $country);
       $request->session()->save();
       return $next($request);
}

You need to now register your Middleware in app/Http/Kernel.php. Within Kernel.php, add a ‘country’ middleware group in $middlewareGroups as shown below

protected $middlewareGroups = [
       'web' => [
           \App\Http\Middleware\EncryptCookies::class,
           \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
           \Illuminate\Session\Middleware\StartSession::class,
           \Illuminate\View\Middleware\ShareErrorsFromSession::class,
           \App\Http\Middleware\VerifyCsrfToken::class,
           \Illuminate\Routing\Middleware\SubstituteBindings::class,
       ],
       'api' => [
           'throttle:60,1',
           'bindings',
       ],
'country' =>[
           'web',
            \App\Http\Middleware\CountryMiddleware::class
        ],
   ];

Since we are using sessions in our Middleware, we are dependent on the following middleware classes:

\Illuminate\Session\Middleware\StartSession::class

\Illuminate\View\Middleware\ShareErrorsFromSession::class

The above middlewares are already included in ‘web’ middleware group. Hence we simply include ‘web’ middleware group in our ‘country’ middleware group just before our country middleware class.

Route through the Middleware

Now, to finally handle the URLs, add the following line in your route file.

  • for Laravel 5.1, 5.2 routes.php file is found in app/Http/ folder
  • for Laravel 5.3 add the routes to routes/web.php file
Route::group(['prefix' => '{country}', 'middleware' => 'country'], function () {
  Route::get('/test', function ($country, Illuminate\Http\Request $request) {
    return $request->session()->get('country');
  });
});

Testing the Code

Now that all changes have been made, you can check if all is okay by testing your code. Here’s what you can do, try fetching http://domain/in/test ,  this should return details of India from the session.

Also try http://domain/gg/test; since ‘gg’ is an invalid code, you should be redirected to http://domain (where domain is the ‘domain name:port’ of your Laravel project (e.g. localhost:8080))

Well and that is it!

Do let me know if this code helped…. or if you have any doubts in our comments!

Stay tuned for more such Laravel tips and tricks from me!

Karthik Thayyil

Karthik Thayyil

5 Responses

  1. hi,
    if i define any route inside middleware flash message not working. please help me.
    Ex.- return redirect()->back()->with(‘success’,’Contact saved Successfully’); not working.

Leave a Reply

Your email address will not be published. Required fields are marked *

Get The Latest Updates

Subscribe to our Newsletter

A key to unlock the world of open-source. We promise not to spam your inbox.

Suggested Reads

Join our 55,000+ Subscribers

    The Wisdm Digest delivers all the latest news, and resources from the world of open-source businesses to your inbox.

    Suggested Reads