Content is user-generated and unverified.
<?php // 1. Primeiro, crie um middleware personalizado para detectar tanto revendedor quanto empresa // app/Http/Middleware/InitializeTenancyByDomainAndPath.php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Stancl\Tenancy\Middleware\InitializeTenancyByDomain; use Stancl\Tenancy\Resolvers\DomainTenantResolver; use Stancl\Tenancy\Resolvers\PathTenantResolver; use Stancl\Tenancy\Tenancy; class InitializeTenancyByDomainAndPath { protected $tenancy; public function __construct(Tenancy $tenancy) { $this->tenancy = $tenancy; } public function handle(Request $request, Closure $next) { $domain = $request->getHost(); $path = trim($request->getPathInfo(), '/'); // Primeiro, identifica o revendedor pelo subdomínio $revendedor = $this->getRevendedorByDomain($domain); if (!$revendedor) { abort(404, 'Revendedor não encontrado'); } // Se não há path ou é apenas o path base, usa o tenant do revendedor if (empty($path) || $path === '/') { $this->tenancy->initialize($revendedor); return $next($request); } // Extrai o primeiro segmento do path $pathSegments = explode('/', $path); $empresaSlug = $pathSegments[0]; // Busca a empresa dentro do revendedor $empresa = $this->getEmpresaBySlug($revendedor, $empresaSlug); if (!$empresa) { // Se não encontrar empresa, usa o tenant do revendedor $this->tenancy->initialize($revendedor); } else { // Inicializa com o tenant da empresa $this->tenancy->initialize($empresa); } return $next($request); } protected function getRevendedorByDomain($domain) { // Extrai o subdomínio (ex: agenda.gendu.com.br -> agenda) $parts = explode('.', $domain); $subdomain = $parts[0]; // Busca o revendedor pelo subdomínio return \App\Models\Tenant::where('data->tipo', 'revendedor') ->where('data->subdomain', $subdomain) ->first(); } protected function getEmpresaBySlug($revendedor, $slug) { // Busca a empresa que pertence ao revendedor return \App\Models\Tenant::where('data->tipo', 'empresa') ->where('data->revendedor_id', $revendedor->id) ->where('data->slug', $slug) ->first(); } } // 2. Crie um modelo customizado para seus tenants // app/Models/Tenant.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; protected $fillable = [ 'id', 'data', ]; protected $casts = [ 'data' => 'array', ]; public function isRevendedor() { return $this->data['tipo'] === 'revendedor'; } public function isEmpresa() { return $this->data['tipo'] === 'empresa'; } public function getRevendedor() { if ($this->isRevendedor()) { return $this; } return static::find($this->data['revendedor_id']); } public function getEmpresas() { if ($this->isEmpresa()) { return collect(); } return static::where('data->revendedor_id', $this->id)->get(); } } // 3. Configure o arquivo de configuração // config/tenancy.php return [ 'tenant_model' => \App\Models\Tenant::class, 'id_generator' => Stancl\Tenancy\UuidGenerator::class, 'domain_model' => \Stancl\Tenancy\Database\Models\Domain::class, // Remova os middlewares padrão e use o customizado 'middleware' => [ // Comente ou remova estes: // \Stancl\Tenancy\Middleware\InitializeTenancyByDomain::class, // \Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain::class, // Use o middleware customizado: \App\Http\Middleware\InitializeTenancyByDomainAndPath::class, ], 'database' => [ 'central_connection' => env('DB_CONNECTION', 'mysql'), 'template_tenant_connection' => null, 'prefix' => 'tenant', 'suffix' => '', 'managers' => [ 'mysql' => \Stancl\Tenancy\Database\DatabaseManager::class, ], ], 'cache' => [ 'tag_base' => 'tenant', ], 'filesystem' => [ 'suffix_base' => 'tenant', 'disks' => [ 'local', 'public', ], ], 'redis' => [ 'prefix_base' => 'tenant', ], 'features' => [ \Stancl\Tenancy\Features\UserImpersonation::class, \Stancl\Tenancy\Features\TelescopeTags::class, \Stancl\Tenancy\Features\UniversalRoutes::class, \Stancl\Tenancy\Features\TenantConfig::class, \Stancl\Tenancy\Features\CrossDomainRedirect::class, \Stancl\Tenancy\Features\ViteBundler::class, ], ]; // 4. Crie uma migration para ajustar a estrutura dos tenants // database/migrations/xxxx_update_tenants_structure.php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class UpdateTenantsStructure extends Migration { public function up() { Schema::table('tenants', function (Blueprint $table) { // Adicione índices para melhorar performance $table->index(['data->tipo']); $table->index(['data->subdomain']); $table->index(['data->slug']); $table->index(['data->revendedor_id']); }); } public function down() { Schema::table('tenants', function (Blueprint $table) { $table->dropIndex(['data->tipo']); $table->dropIndex(['data->subdomain']); $table->dropIndex(['data->slug']); $table->dropIndex(['data->revendedor_id']); }); } } // 5. Helper para criar tenants // app/Services/TenantService.php namespace App\Services; use App\Models\Tenant; use Stancl\Tenancy\Database\Models\Domain; class TenantService { public function createRevendedor($nome, $subdomain, $dados = []) { $tenant = Tenant::create([ 'data' => array_merge([ 'tipo' => 'revendedor', 'nome' => $nome, 'subdomain' => $subdomain, ], $dados) ]); // Cria o domínio para o revendedor $tenant->domains()->create([ 'domain' => "{$subdomain}.seudominio.com.br" ]); return $tenant; } public function createEmpresa($nome, $slug, $revendedorId, $dados = []) { $tenant = Tenant::create([ 'data' => array_merge([ 'tipo' => 'empresa', 'nome' => $nome, 'slug' => $slug, 'revendedor_id' => $revendedorId, ], $dados) ]); return $tenant; } } // 6. Route Service Provider personalizado // app/Providers/TenantRouteServiceProvider.php namespace App\Providers; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Route; class TenantRouteServiceProvider extends ServiceProvider { public function boot() { $this->routes(function () { // Rotas para revendedores (quando não há path) Route::middleware([ 'web', InitializeTenancyByDomainAndPath::class, PreventAccessFromCentralDomains::class, ]) ->group(function () { // Verifica se é revendedor if (tenant() && tenant()->isRevendedor()) { Route::group([], base_path('routes/revendedor.php')); } // Verifica se é empresa if (tenant() && tenant()->isEmpresa()) { Route::group([], base_path('routes/empresa.php')); } }); }); } } // 7. Exemplo de uso em um controller // app/Http/Controllers/TenantController.php namespace App\Http\Controllers; use Illuminate\Http\Request; class TenantController extends Controller { public function index() { $tenant = tenant(); if ($tenant->isRevendedor()) { return view('revendedor.dashboard', [ 'revendedor' => $tenant, 'empresas' => $tenant->getEmpresas(), ]); } if ($tenant->isEmpresa()) { return view('empresa.dashboard', [ 'empresa' => $tenant, 'revendedor' => $tenant->getRevendedor(), ]); } abort(404); } } // 8. Exemplo de criação de tenants // database/seeders/TenantSeeder.php namespace Database\Seeders; use App\Services\TenantService; use Illuminate\Database\Seeder; class TenantSeeder extends Seeder { public function run() { $tenantService = new TenantService(); // Cria revendedor $revendedor = $tenantService->createRevendedor( 'Gendu', 'agenda', ['email' => 'contato@gendu.com.br'] ); // Cria empresas do revendedor $tenantService->createEmpresa( 'Salão da Maria', 'salao-maria', $revendedor->id, ['email' => 'maria@salao.com'] ); $tenantService->createEmpresa( 'Barbearia do Zé', 'ze', $revendedor->id, ['email' => 'ze@barbearia.com'] ); } }
Content is user-generated and unverified.
    Configuração Multi-Tenancy Aninhado | Claude