方案概述
这个方案的核心思路是:利用 Tenancy 包的多租户功能,在创建新租户时自动生成对应的 WordPress 安装目录和配置文件。这样每个租户(用户)都能拥有一个完全独立的 WordPress 实例,共享同一套 Laravel 应用代码,但数据完全隔离。
1. 安装与配置 Tenancy for Laravel
首先确保你已经安装并配置了 stancl/tenancy
包。
composer require stancl/tenancy
php artisan tenancy:install
php artisan migrate
在 config/tenancy.php
中配置中心域名:
'central_domains' => [
'your-central-app.com', // 你的主域名
],
2. 扩展租户模型与创建逻辑
创建一个自定义的租户模型,并添加 WordPress 相关的属性和方法。
<?php
namespace App\Models;
use Stancl\Tenancy\Database\Models\Tenant as BaseTenant;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;
class Tenant extends BaseTenant implements TenantWithDatabase
{
use HasDatabase, HasDomains;
// 添加 WordPress 安装路径等自定义属性
public function getWordPressPathAttribute()
{
return storage_path("app/tenants/{$this->id}/wordpress");
}
public function getWordPressUrlAttribute()
{
return "http://{$this->primary_domain}/blog";
}
}
3. 实现 WordPress 自动安装逻辑
创建一个事件监听器,在租户创建时自动安装 WordPress。
<?php
namespace App\Listeners;
use Stancl\Tenancy\Events\TenantCreated;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
class InstallWordPressForTenant
{
public function handle(TenantCreated $event)
{
$tenant = $event->tenant;
// 1. 创建租户专属目录
$wordpressPath = $tenant->wordpress_path;
File::makeDirectory($wordpressPath, 0755, true, true);
// 2. 下载最新 WordPress
$response = Http::get('https://wordpress.org/latest.zip');
$zipPath = $wordpressPath . '/latest.zip';
File::put($zipPath, $response->body());
// 3. 解压 ZIP 文件
$zip = new \ZipArchive;
if ($zip->open($zipPath) === TRUE) {
$zip->extractTo($wordpressPath);
$zip->close();
// 移动文件到正确位置
File::moveDirectory("$wordpressPath/wordpress", $wordpressPath, true);
}
// 4. 创建 WordPress 配置文件 (wp-config.php)
$configContent = $this->generateWpConfig($tenant);
File::put("$wordpressPath/wp-config.php", $configContent);
// 5. 为租户配置 Nginx(如果需要)
$this->configureNginx($tenant);
}
protected function generateWpConfig($tenant)
{
// 生成租户特定的数据库配置
return "<?php
define('DB_NAME', 'tenant_{$tenant->id}');
define('DB_USER', '{$tenant->db_username}');
define('DB_PASSWORD', '{$tenant->db_password}');
define('DB_HOST', 'localhost');
// ... 其他 WordPress 配置
";
}
}
在 EventServiceProvider
中注册事件监听:
protected $listen = [
TenantCreated::class => [
InstallWordPressForTenant::class,
],
];
4. 配置 Web 服务器路由
你需要配置 Web 服务器(如 Nginx),将租户的 WordPress 请求路由到正确的目录。 Nginx 配置示例:
server {
listen 80;
server_name ~^(?<subdomain>.+)\.your-central-app\.com$;
root /path/to/your/laravel/storage/app/tenants/$subdomain/wordpress;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
5. 创建租户的用户界面
创建一个管理界面,让管理员或用户自己能申请创建新的 WordPress 站点。
// 在控制器中
public function createTenantWithWordPress(Request $request)
{
$tenant = Tenant::create([
'id' => Str::slug($request->name),
'name' => $request->name,
]);
$tenant->domains()->create([
'domain' => $request->subdomain . '.your-central-app.com'
]);
// 事件监听器会自动安装 WordPress
return redirect()->back()->with('success', 'WordPress 站点创建成功!');
}
重要注意事项
- 安全性考虑:
- 确保租户之间完全隔离
- 定期更新所有 WordPress 实例的安全补丁
- 限制租户对系统文件的访问权限
- 性能优化:
- 考虑使用对象存储(如 AWS S3)来存储上传的文件
- 为 WordPress 配置缓存机制
- 监控数据库性能,随着租户数量增加可能需要优化
- 备份策略:
- 实现自动备份每个租户的数据库和文件
- 考虑差异备份以减少存储需求
替代方案考虑
如果上述方案对你来说太复杂,可以考虑以下简化方案:
- 使用 WordPress Multisite:WordPress 自带的 Multisite 功能可以实现类似的多站点管理,但所有站点共享同一个 WordPress 安装和数据库。
- 容器化部署:使用 Docker 为每个 WordPress 站点创建独立的容器,通过 Traefik 或类似工具进行路由管理。
这个方案的优势在于它允许你使用熟悉的 Laravel 生态来管理多个 WordPress 实例,同时保持了代码的简洁性和可维护性。