Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Multitenancy Bundle Laravel Package

codeplace-io/multitenancy-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require codeplace-io/multitenancy-bundle
    

    Publish the bundle’s configuration and migrations:

    php artisan vendor:publish --provider="Codeplace\MultitenancyBundle\MultitenancyServiceProvider" --tag="config"
    php artisan vendor:publish --provider="Codeplace\MultitenancyBundle\MultitenancyServiceProvider" --tag="migrations"
    

    Run migrations:

    php artisan migrate
    
  2. Configure Tenant Model Extend the provided Tenant model (or create your own) in config/multitenancy.php:

    'tenant_model' => \App\Models\Tenant::class,
    
  3. First Tenant Request Use middleware to resolve the tenant before each request:

    // app/Http/Kernel.php
    protected $middlewareGroups = [
        'web' => [
            \Codeplace\MultitenancyBundle\Http\Middleware\ResolveTenant::class,
            // ... other middleware
        ],
    ];
    

    Test with a request to /tenant/{tenant-slug} (or your defined route).


First Use Case: Tenant-Aware Routes

Define tenant-specific routes in routes/web.php:

Route::middleware(['tenant'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
});

Use tenant() helper to access the current tenant:

$tenant = tenant(); // Returns the resolved Tenant model

Implementation Patterns

1. Tenant Resolution Workflow

  • Dynamic Tenancy: Resolve tenants via:
    • Subdomain (tenant.example.comconfig/multitenancy.php subdomain key).
    • Route parameter (/tenant/{slug}config/multitenancy.php route key).
    • Header (X-Tenant-ID → Custom middleware).
  • Fallback Tenant: Set a default tenant in config/multitenancy.php:
    'fallback_tenant' => 1,
    

2. Database Scoping

  • Automatic Scoping: The bundle scopes queries to the current tenant’s database by default.
    $users = User::all(); // Automatically scoped to tenant's DB
    
  • Global Queries: Use tenant()->unscoped() or DB::connection()->getDatabaseName() to bypass scoping.

3. Middleware Integration

  • Custom Middleware: Extend ResolveTenant to add logic:
    public function handle($request, Closure $next) {
        $tenant = Tenant::where('identifier', $request->header('X-Tenant-ID'))->first();
        if (!$tenant) abort(404);
        tenant()->set($tenant);
        return $next($request);
    }
    
  • Tenant-Specific Middleware: Apply middleware per tenant:
    Route::middleware(['tenant', 'auth:tenant'])->group(...);
    

4. Seeding and Factories

  • Use Tenant factories to seed data per tenant:
    // database/seeders/TenantSeeder.php
    Tenant::factory()->create(['identifier' => 'acme']);
    User::factory()->create(['tenant_id' => tenant()->id]);
    

5. APIs and Tenant Context

  • Pass tenant context in API responses:
    return response()->json([
        'data' => $data,
        'tenant' => tenant()->toArray(),
    ]);
    
  • Use tenant() in API controllers to validate requests:
    public function store(Request $request) {
        $request->validate(['tenant_id' => 'required|exists:tenants,id']);
        // ...
    }
    

Gotchas and Tips

Pitfalls

  1. Database Connection Leaks

    • Issue: Forgetting to reset the database connection after tenant switches can cause stale queries.
    • Fix: Use tenant()->resetConnection() in middleware or after tenant changes:
      tenant()->set($newTenant)->resetConnection();
      
  2. Caching Conflicts

    • Issue: Cached routes or views may not reflect tenant-specific changes.
    • Fix: Clear cache per tenant or use tenant-aware cache keys:
      Cache::put("tenant_{$tenant->id}_key", $value, $seconds);
      
  3. Migration Conflicts

    • Issue: Running migrations without specifying a tenant may fail.
    • Fix: Use php artisan migrate --tenant=1 or scope migrations to a tenant.
  4. Middleware Order

    • Issue: ResolveTenant must run before Auth or other tenant-dependent middleware.
    • Fix: Place it first in the web middleware group.

Debugging Tips

  • Log Tenant Resolution:
    \Codeplace\MultitenancyBundle\Facades\Tenant::set($tenant);
    \Log::debug("Current tenant: ", tenant()->toArray());
    
  • Check Database Connection:
    \DB::connection()->getDatabaseName(); // Verify active DB
    
  • Disable Scoping Temporarily:
    User::withoutTenantScoping()->get(); // Bypass tenant scoping
    

Extension Points

  1. Custom Tenant Identifiers Override Tenant::resolveByIdentifier() to support custom logic (e.g., UUIDs, emails).

  2. Dynamic Database Switching Extend the DatabaseResolver to support multi-tenant databases beyond the default schema:

    // app/Providers/AppServiceProvider.php
    public function boot() {
        \Codeplace\MultitenancyBundle\Facades\Tenant::extend(function ($app) {
            $app->bind(\Codeplace\MultitenancyBundle\Contracts\DatabaseResolver::class, function () {
                return new CustomDatabaseResolver();
            });
        });
    }
    
  3. Tenant-Specific Config Load tenant-specific config files:

    // config/multitenancy.php
    'config_paths' => [
        'tenants/{tenant_id}/config.php',
    ],
    
  4. Soft Deletes Ensure Tenant model uses SoftDeletes if tenants can be "disabled" without deletion:

    use Illuminate\Database\Eloquent\SoftDeletes;
    class Tenant extends Model {
        use SoftDeletes;
    }
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky