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

Multi Tenant Laravel Package

commitm/multi-tenant

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require commitm/multi-tenant
    

    Ensure MultiTenantBundle is enabled in config/bundles.php (Symfony) or config/app.php (Laravel via bridge if applicable).

  2. Configuration: Create config/multi_tenant.yaml (or merge into existing config):

    multi_tenant:
        tenants:
            company_id:
                entity: App\Entity\Company
                aware: App\Entity\Interfaces\TenantAware
                property: company
    
    • Replace App\Entity\Company with your tenant model.
    • aware must implement CommitM\MultiTenant\Multitenancy\TenantAware.
  3. First Use Case: Inject TenantsProvider into a service/controller:

    use CommitM\MultiTenant\Multitenancy\Provider\TenantsProvider;
    
    public function __construct(private TenantsProvider $tenantsProvider) {}
    
    // Set tenant for the current request (e.g., from middleware)
    $this->tenantsProvider->getTenantByClass(Company::class)->setEntity($company);
    

Implementation Patterns

Workflows

  1. Middleware Integration: Extract tenant from request (e.g., subdomain, header, or route param) and set it:

    public function handle(Request $request, Closure $next) {
        $company = Company::find($request->get('company_id'));
        $this->tenantsProvider->getTenantByClass(Company::class)->setEntity($company);
        return $next($request);
    }
    
  2. Automatic Tenant Propagation: For entities implementing TenantAware, the tenant is auto-set via the property (e.g., company):

    $product = new Product(); // Automatically sets $product->company if tenant is active
    
  3. Query Scoping: Use the TenantAware trait in repositories to scope queries:

    public function findByTenant() {
        return $this->createQueryBuilder()
            ->where('p.company = :tenant')
            ->setParameter('tenant', $this->getTenant())
            ->getQuery()
            ->getResult();
    }
    
  4. Dynamic Tenant Switching: Override tenant mid-request (e.g., for admin actions):

    $this->tenantsProvider->getTenantByClass(Company::class)->setEntity($newCompany);
    

Integration Tips

  • Laravel Bridge: Use spatie/laravel-symfony-bundle to integrate Symfony bundles into Laravel.
  • Doctrine Events: Listen to prePersist/preUpdate to auto-set tenant fields if missing.
  • Testing: Mock TenantsProvider to isolate tenant logic:
    $this->tenantsProvider->shouldReceive('getTenantByClass')->andReturn($mockTenant);
    

Gotchas and Tips

Pitfalls

  1. Interface Implementation: Forgetting to implement TenantAware on scoped entities will break auto-propagation. Fix: Add the interface and trait to all tenant-scoped models.

  2. Circular Dependencies: Setting the tenant entity too early (e.g., in a service constructor) may cause issues if the tenant itself depends on other services. Fix: Defer tenant setup until the request lifecycle (e.g., middleware).

  3. Query Performance: Unscoped queries (e.g., Product::all()) will return all tenants’ data. Fix: Always scope queries or use the TenantAware trait.

  4. Configuration Overrides: Hardcoding tenant IDs in config or services bypasses the provider. Fix: Centralize tenant logic via TenantsProvider.

Debugging

  • Check Active Tenant:
    $tenant = $this->tenantsProvider->getTenantByClass(Company::class);
    dump($tenant->getEntity()); // Should return the current tenant entity
    
  • Log Tenant Switches: Add debug logs in middleware to track tenant changes:
    $this->tenantsProvider->getTenantByClass(Company::class)->setEntity($company);
    \Log::debug("Switched tenant to: {$company->id}");
    

Extension Points

  1. Custom Tenant Providers: Extend TenantsProvider to add logic (e.g., tenant fallback or validation):

    class CustomTenantsProvider extends TenantsProvider {
        public function validateTenant($tenant) {
            if (!$tenant->isActive()) {
                throw new \RuntimeException("Tenant is inactive");
            }
        }
    }
    
  2. Dynamic Tenant Resolution: Override getTenantByClass to resolve tenants from context (e.g., API tokens):

    $this->tenantsProvider->extend(function ($class) {
        return $this->resolveFromAuth()->getTenant();
    });
    
  3. Multi-Tenant Migrations: Use Doctrine extensions to apply tenant-specific migrations:

    // In a custom migration service
    $this->tenantsProvider->getAllTenants()->each(function ($tenant) {
        $this->runMigrationForTenant($tenant);
    });
    

Config Quirks

  • Property Naming: The property in config must match the entity’s field name (e.g., company$entity->company). Fix: Use snake_case for consistency with Laravel conventions.

  • Entity Autowiring: Ensure the entity in config is fully qualified (e.g., App\Entity\Company), not a short class name.

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.
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver