Laravel SaaS Using Laravel 5.8 and Hyn Tenancy 5.4

This article is split into several parts.
Part 1, Part 2, Part 3, Part 4, Part 5

In this series, I will attempt to build out a Laravel based Software as a Solution framework that can be easily adopted to different situations. This method is what I will be implementing on the e-cards.io site for selling online gift cards. Much of the content & concepts here are based on an excellent article by Ashok Gelal on Medium along with other sources.

The goals will be:

  • Separate accounts by subdomain
  • Separate account content
  • Separate account databases
  • Allow for new user creation
  • Provide a subscription service to access
  • Define user roles
  • Implement a simple “service” example

Notes

At the time of writing this, Laravel 6.0 is out and there’s also a new version of Tenancy, however Tenancy is in it’s Alpha release and not ready for a production environment. The Hyn Tenancy 5.4 package requires Laravel 5.8, so that’s what we’re going to use here.

For the purposes of this write-up, I’m going to assume you already have PHP and MySQL or MaraiaDB installed. If you don’t already have WampServer, MAMP or equivalent, check out a Homestead virtual machine.

Alright, let’s get started.

1. Install Laravel

Firstly, let’s install the proper version of Laravel via Composer. If you don’t already have Composer installed, follow the instructions at getcomposer.org. I’m on a Windows 10 machine, so the Windows installer works for me. Make sure to choose PHP 7.1.3 or higher. Ideally, use 7.2.0 or higher, as it has improved performance.

Next, we are unable to use the typical Laravel installer because that will install the latest version (6.0), which isn’t what we want. Instead, open up a PowerShell, command line, bash or whatever you’re using, navigate to the parent folder that you want to locate your new project and install version 5.8.

$ composer create-project --prefer-dist laravel/laravel saas-framework "5.8.*"

You should now have a fresh install of Laravel 5.8.

2. Install Tenancy

Now, we’re going to install the Hyn Tenancy package. If you’re not familiar with the concepts of tenancy, have a quick read over at Wikipedia. It’s essentially a single code base that serves up multiple versions of the same app/site based on their credentials, or in our case a subdomain.

To make use of the Tenancy package, we’ll need a database user that has privileges to CREATE DATABASE. On MySQL or MariaDB that’s the “GRANT OPTION” permission. In this example, we’re creating a “tenancy” user.

CREATE DATABASE IF NOT EXISTS tenancy;
CREATE USER IF NOT EXISTS tenancy@localhost IDENTIFIED BY 'someRandomPassword';
GRANT ALL PRIVILEGES ON *.* TO tenancy@localhost WITH GRANT OPTION;

Now we’re going to add a configuration file to use this new database. Back in your project folder, open up your database.php config file and add in a section for ‘system’ under ‘connections’.

config/database.php
    'connections' => [

        'system' => [
            'driver' => 'mysql',
            'host' => env('TENANCY_HOST', '127.0.0.1'),
            'port' => env('TENANCY_PORT', '3306'),
            'database' => env('TENANCY_DATABASE', 'tenancy'),
            'username' => env('TENANCY_USERNAME', 'tenancy'),
            'password' => env('TENANCY_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],

        'sqlite' => [
            'driver' => 'sqlite',
            'url' => env('DATABASE_URL'),
            ....
            ....

After that, open up your .env file to add in the environment variables referenced in the new system connection. Also, change your DB_CONNECTION to “system”.

.env
DB_CONNECTION=system
TENANCY_HOST=127.0.0.1
TENANCY_PORT=3306
TENANCY_DATABASE=tenancy
TENANCY_USERNAME=tenancy
TENANCY_PASSWORD=someRandomPassword

Now let’s install the tenancy package…

$ composer require "hyn/multi-tenant:5.4.*"

… and use artisan to publish the configuration files for tenancy.

$ php artisan vendor:publish --tag=tenancy

Copied Directory [\vendor\hyn\multi-tenant\assets\migrations] To [\database\migrations]
Copied File [\vendor\hyn\multi-tenant\assets\configs\tenancy.php] To [\config\tenancy.php]
Copied File [\vendor\hyn\multi-tenant\assets\configs\webserver.php] To [\config\webserver.php]
Publishing complete.

If you check, you should have new migration files under database/migrations with _tenancy_ in the filenames.

Tenancy migrations folder structure

Next, we’re going to move the original migrations for creating Users to a special database/migrations/tenant folder. This will ensure that each time we run a tenant migration that they’re separated out from the system migrations.

Tenancy migrations moved users folder view

It’s time to install the database, but there’s one additional step to consider before doing so. If you’re using MySQL and try to migrate right now, you’ll get an error about the specified key length being too long.

Migration table created successfully.
Migrating: 2017_01_01_000003_tenancy_websites
Migrated:  2017_01_01_000003_tenancy_websites (0.05 seconds)
Migrating: 2017_01_01_000005_tenancy_hostnames

   Illuminate\Database\QueryException  : SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 1000 bytes (SQL: alter table `hostnames` add unique `hostnames_fqdn_unique`(`fqdn`))

The Solution to this is to update your AppServiceProvider file, limiting the string length in the boot method. Make sure to add the new Facade at the top of the file as well.

app/Providers/AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{

   ....
   ....

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // restrict string length
        Schema::defaultStringLength(191);
    }

You may also need to add an additional line to your .env file.

LIMIT_UUID_LENGTH_32=true

After you’ve added that line to your .env, clear your application’s cache.

$ php artisan config:cache

If you had the previous error, remove the tables from your database with the “:fresh” option. Otherwise you can omit this.

$ php artisan migrate:fresh --database=system

Dropped all tables successfully.
Migration table created successfully.
Migrating: 2017_01_01_000003_tenancy_websites
Migrated:  2017_01_01_000003_tenancy_websites (0.05 seconds)
Migrating: 2017_01_01_000005_tenancy_hostnames
Migrated:  2017_01_01_000005_tenancy_hostnames (0.19 seconds)
Migrating: 2018_04_06_000001_tenancy_websites_needs_db_host
Migrated:  2018_04_06_000001_tenancy_websites_needs_db_host (0.08 seconds)

And that’s it. You now have the Laravel 5.8 and Hyn Tenancy 5.4 installed. In the next article, I’ll go over setting up Websites, Hostnames and add a new tenant.

Continue to Part 2 »

Additional Resources: