柔晶美网络工作室

柔晶美网络工作室,倾心于web技术的博客站点

关注我 微信公众号

您现在的位置是: 首页 > 博客日记

laravel前台和Dcat-admin后台,实现saas的简易方式

2021-07-02 admin laravel  2030

原来使用了stancl/tenancy插件,感觉功能太多,文档过于简单,和dcat admin结合时,比较难实现数据隔离。本文使用laravel自身的路由功能来实现saas的基本功能。


实现原理:在laravel前台和后台路由入口,区分主域名和子域名,然后通过config来切换url、数据库和存储位置,实现基本的隔离。


一、在config/database.php文件中添加一个备用数据库

        'mysql_extend' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],


二、前台路由,在app/Providers/RouteServiceProvider.php中修改boot方法,添加:

        //前台路由,区分主站和子站,并读取主站users表,判断子站是否存在
        if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] !== 'yzm.dzbfsj.com'){
            $url_first = explode('.',$_SERVER['HTTP_HOST'])[0];
            config(['app.url' => $url_first.'.yzm.dzbfsj.com']);//全局替换网址
            config(['database.connections.mysql.database' => 'db_'.$url_first]);
            config(['database.connections.mysql_extend.database' => 'yzm_dzbfsj_com']);//连接主数据库,用于查询网址是否存在
            if(DB::connection('mysql_extend')->table('users')->where('name',$url_first)->doesntExist()){
                exit('站点不存在,请从主站登录:<a href="https://yzm.dzbfsj.com">点击进入</a>');
            }
        }

三、后台路由,在app/Admin/routes.php添加:

use Illuminate\Support\Facades\DB;

//后台路由,区分主站和子站,并读取主站users表,判断子站是否存在
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] !== 'yzm.dzbfsj.com'){
    $url_first = explode('.',$_SERVER['HTTP_HOST'])[0];
    config(['app.url' => $url_first.'.yzm.dzbfsj.com']);//全局替换网址
    config(['database.connections.mysql.database' => 'db_'.$url_first]);
    config(['database.connections.mysql_extend.database' => 'yzm_dzbfsj_com']);//连接主数据库,用于查询网址是否存在
    if(DB::connection('mysql_extend')->table('users')->where('name',$url_first)->doesntExist()){
        exit('站点不存在,请从主站登录:<a href="https://yzm.dzbfsj.com">点击进入</a>');
    }
}

这样,前后台都能区分主站和子站了,并动态的修改了数据库和url,如果要修改存储位置,也可通过config来修改。

四、主站后台租户管理,可以使用主站的Users表,创建一个通过用户管理,关键代码:

            $form->hidden('tenant');
            
            //name是唯一的,对应三级域名,如www;tenant对应是的数据库名,格式为db_name
            $form->saving(function (Form $form) {
                if ($form->isCreating()) {
                    $password = $form->password ?? '123456';
                    $form->password = Hash::make($password);
                    $form->email_verified_at = now();//默认激活
                    $db_name = 'db_'.$form->name;
                    $form->tenant = $db_name;
                    //创建数据库并导入根目录db.sql
                    $charset = config('database.connections.mysql.charset');
                    $collation = config('database.connections.mysql.collation');
                    $path = database_path('/data.sql');
                    if(DB::statement("CREATE DATABASE `{$db_name}` CHARACTER SET `$charset` COLLATE `$collation`") && file_exists($path)){
                        config(['database.connections.mysql_extend.database' => $db_name]);//切换数据库
                        DB::connection('mysql_extend')->unprepared(file_get_contents($path));
                    }
                }else{
                    if(!$form->password){
                        $form->deleteInput('password');//没有输入新密码,不保存
                    }else{
                        $form->password = Hash::make($form->password);
                    }
                }
            });
            
            $form->deleting(function (Form $form){
                $dsc = $form->model()->toArray();//循环删除租户数据库
                foreach($dsc as $val){
                    if($db_name = $val['tenant']){
                        if(DB::select("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$db_name'")){
                            DB::statement("DROP DATABASE `{$db_name}`");
                        }
                    }
                }
            });

上面充分利用了laravel和数据库切换功能,实现了租户的创建和删除(仅以myql为例,如果是其它数据库,可参照修改)。

五、添加三级域名泛解析

在阿里云域名管理处,添加泛解析:*.yzm解析到服务器ip,然后在宝塔面板,添加主域名和*.主域名,部署ssl证书即可。

六、前台路由

if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] == 'yzm.dzbfsj.com'){
    //主路由
    Auth::routes(['verify' => true]);;
    
    Route::middleware(['verified'])->group(function () {
        Route::get('/', [HomeController::class, 'index'])->name('index');
        Route::get('/home', [HomeController::class, 'index'])->name('home');
        Route::get('/scsj', [HomeController::class, 'scsj'])->name('scsj');
    });
}else{
    //子路由
    Route::domain('{account}.yzm.dzbfsj.com')->group(function () {
        Route::get('/', function ($account) {
            return redirect('/admin');
        });
    });

}


经测试前台和后台,功能完全正常。

走过的弯路:

路由入口判断租户是否存在,需要用config切换主副数据库(主副调换)后,通过DB::connection('mysql_extend')的方式读取主站users表来判断。不可直接用User Model,否则读取的是子站的数据库users表;也不可在切换前通过DB来读取,因为config优先执行,放在切换前读取,实际读取的也是切换后的子站数据库。

后期将用来整合第三方登录和支付,以及功能模板的灵活调用。


文章评论


需要 登录 才能发表评论
热门评论
0条评论

暂时没有评论!