Posted on Leave a comment

SaaS with laravel (1)-创建租户

github地址: https://github.com/liangdabiao/townhouse

We will create a full-featured app with support for roles and permissions, APIs, admin panel and few more. The app will allow users for listing rentals and we’ll call this app Townhouse

功能简单介绍:

Think of Townhouse as a clone of Craigslist but only focused on property rentals. A super admin (that is you) will be able to create a city (which will be our tenant) and assign an admin to manage the city’s rentals. Rental owners tenant’s users) will be able to signup and list their property.

php artisan tenancy:install //安装多租户

php artisan make:command CreateTenant // 创建命令

<?php

namespace App\Console\Commands;

use App\User;
use Hyn\Tenancy\Contracts\Repositories\CustomerRepository;
use Hyn\Tenancy\Contracts\Repositories\HostnameRepository;
use Hyn\Tenancy\Contracts\Repositories\WebsiteRepository;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Models\Customer;
use Hyn\Tenancy\Models\Hostname;
use Hyn\Tenancy\Models\Website;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;

class CreateTenant extends Command
{
    protected $signature = 'tenant:create {name} {email}';

    protected $description = 'Creates a tenant with the provided name and email address e.g. php artisan tenant:create boise boise@example.com';

    public function handle()
    {
        $name = $this->argument('name');
        $email = $this->argument('email');

        if ($this->tenantExists($name, $email)) {
            $this->error("A tenant with name '{$name}' and/or '{$email}' already exists.");

            return;
        }

        $hostname = $this->registerTenant($name, $email);
        app(Environment::class)->hostname($hostname);

        // we'll create a random secure password for our to-be admin
        $password = str_random();
        $this->addAdmin($name, $email, $password);

        $this->info("Tenant '{$name}' is created and is now accessible at {$hostname->fqdn}");
        $this->info("Admin {$email} can log in using password {$password}");
    }

    private function tenantExists($name, $email)
    {
        return Customer::where('name', $name)->orWhere('email', $email)->exists();
    }

    private function registerTenant($name, $email)
    {
        // create a customer
        $customer = new Customer;
        $customer->name = $name;
        $customer->email = $email;
        app(CustomerRepository::class)->create($customer);

        // associate the customer with a website
        $website = new Website;
        $website->customer()->associate($customer);
        app(WebsiteRepository::class)->create($website);

        // associate the website with a hostname
        $hostname = new Hostname;
        $baseUrl = config('app.url_base');
        $hostname->fqdn = "{$name}.{$baseUrl}";
        $hostname->customer()->associate($customer);
        app(HostnameRepository::class)->attach($hostname, $website);

        return $hostname;
    }

    private function addAdmin($name, $email, $password)
    {
        $admin = User::create(['name' => $name, 'email' => $email, 'password' => Hash::make($password)]);

        return $admin;
    }
}

php artisan tenant:create boise boise@example.com //创建租户

php artisan make:command DeleteTenant //创建 删除租户的命令

<?php

namespace App\Console\Commands;

use Hyn\Tenancy\Contracts\Repositories\CustomerRepository;
use Hyn\Tenancy\Contracts\Repositories\HostnameRepository;
use Hyn\Tenancy\Contracts\Repositories\WebsiteRepository;
use Hyn\Tenancy\Environment;
use Hyn\Tenancy\Models\Customer;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;

class DeleteTenant extends Command
{
    protected $signature = 'tenant:delete {name}';
    protected $description = 'Deletes a tenant of the provided name. Only available on the local environment e.g. php artisan tenant:delete boise';

    public function handle()
    {
        // because this is a destructive command, we'll only allow to run this command
        // if you are on the local environment
        if (!app()->isLocal()) {
            $this->error('This command is only avilable on the local environment.');

            return;
        }

        $name = $this->argument('name');
        $this->deleteTenant($name);
    }

    private function deleteTenant($name)
    {
        if ($customer = Customer::where('name', $name)->with(['websites', 'hostnames'])->firstOrFail()) {
            $hostname = $customer->hostnames->first();
            $website = $customer->websites->first();
            app(HostnameRepository::class)->delete($hostname, true);
            app(WebsiteRepository::class)->delete($website, true);
            app(CustomerRepository::class)->delete($customer, true);
            $this->info("Tenant {$name} successfully deleted.");
        }
    }
}

从代码可以看到,其实还需要删除 tenant’s database, tenant’s user, and tenant’s storage folders

执行: php artisan vendor:publish //发布服务

选择 服务 Provider: Hyn\Tenancy\Providers\Tenants\ConfigurationProvider

配置服务 config/tenancy.php

...
'auto-delete-tenant-directory' => env('AUTO_DELETE_TENANT_DIRECTORY', false),
...
'auto-delete-tenant-database' => env('AUTO_DELETE_TENANT_DATABASE', false),
...

.env 配置:

AUTO_DELETE_TENANT_DIRECTORY=true                       AUTO_DELETE_TENANT_DATABASE=true

然后才可以真正删除 租户,执行:

php artisan tenant:delete boise


Leave a Reply

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