Weave 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

Multitenant Laravel Package

phariscope/multitenant

phariscope/multitenant is a Laravel package for building multi-tenant SaaS applications. It helps isolate tenant data and switch tenant context, with tenant-aware database connections, migrations, and Eloquent scoping for per-tenant separation.

View on GitHub
Deep Wiki
Context7

Getting Started

MultiTenant is a pre-1.0 package (current line: 0.0.29), so treat the source and test suite as the primary documentation — the README covers the happy path, but the tests define the actual contract.

composer require phariscope/multi-tenant

Confirm the exact package name and namespace from the repo's composer.json before wiring anything up; at 0.0.x the naming has not necessarily settled.

Where to look first, in order:

  1. composer.json — PHP/Laravel version constraints and autoload namespace.
  2. src/ — the tenant resolver/context object and whatever query scoping or connection-switching mechanism is provided. This is the whole package; read it end to end, it's small.
  3. tests/ — the most reliable statement of intended usage, especially around how the tenant is set and reset.
  4. README — examples only; do not assume behaviour beyond what you can trace into src/.

First use case: a single shared database with a tenant_id column on tenant-owned tables. Install the package, publish any config it ships, register the service provider if it isn't auto-discovered, then resolve a tenant once per request (middleware) and let the package's scoping apply to model queries. Verify with a two-tenant fixture before you build features on it: create rows as tenant A, switch to tenant B, and assert you see nothing from A. That single test tells you more about the package's semantics than the README does.

Implementation Patterns

Resolve the tenant at the edge, not in models. Do tenant resolution in middleware (or a request-scoped binding) driven by subdomain, header, or authenticated user's tenant. Keep the resolution rule in one class so tests and console commands can reuse it. Models should never sniff the request.

Scope reads, enforce writes. If the package provides a global scope or query constraint, confirm it also backfills the tenant key on create(). Where it doesn't, add a creating observer on your base model — a missing tenant id on insert is the failure mode that leaks data into a shared bucket and is nearly impossible to clean up later.

Keep an explicit escape hatch. Admin screens, cross-tenant reports, and migrations need to run unscoped. Wrap that in a named method (withoutTenantScope()) rather than sprinkling raw DB::table() calls around, so you can grep for every place the boundary is crossed during review.

Long-running processes need explicit context. For queued jobs, set the tenant in the job's constructor or handle() and clear it in a finally. For Octane, do the same in middleware and reset on request termination. Anything that caches the tenant on a singleton is a leak waiting for the second request.

Prefix shared infrastructure. Cache keys and filesystem paths must include the tenant identifier. If the package doesn't do this for you, do it in a base service/repository class rather than at each call site.

Test with a tenant switcher. A small helper that runs a closure as a given tenant keeps isolation tests readable:

$this->asTenant($a, fn () => Order::create([...]));
$this->asTenant($b, fn () => $this->assertSame(0, Order::count()));

Integration tip: bind the tenant context as a scoped singleton in a service provider so both HTTP and console paths get the same instance, and add an artisan command (e.g. tenant:run {id} {command}) for one-off operations against a single tenant.

Gotchas and Tips

0.0.x means no compatibility promise. Pin an exact version in composer.json (not ^0.0) and upgrade deliberately. Between 0.0.27 and 0.0.29 there are no meaningful release notes — the changelog is only a compare link — so before upgrading, read the diff directly:

composer require phariscope/multi-tenant:0.0.29 --dry-run
# then open the compare URL and read the src/ + tests/ changes

If the diff touches anything you didn't expect (a class rename, a changed default, a moved namespace), treat it as breaking regardless of the version number.

Empty release notes are a signal. For a package this small, a two-patch jump with no notes usually means internal refactoring or test fixes rather than features. Verify your isolation tests still pass after upgrading; don't assume the public surface is unchanged just because nothing was announced.

Raw queries bypass every scope. DB::select(), DB::table(), and hand-written joins will not be filtered. Add a code-review checklist item and, ideally, a static-analysis rule banning them outside a small allowlist of files.

Unique constraints must include the tenant column. unique(email) will collide across tenants. Every unique index on a tenant-owned table should be unique(tenant_id, ...). Retrofitting this after launch is painful.

Migrations run in an ambiguous context. Schema changes usually need to be unscoped; seeding often needs a tenant. Keep migrations tenant-agnostic and push tenant-specific data into idempotent seeders run per tenant.

Debugging tips: log the resolved tenant id in a middleware with a request correlation id, and add an assertion in your base model's saving event that a tenant is present. When you see "no rows returned" the cause is almost always an unset tenant rather than missing data — check the resolver before you check the query.

Extension points: subclass the package's tenant resolver/context if it's concrete, or bind your own implementation if an interface exists, rather than editing vendor code. Because the package is small and moving fast, prefer composition around it over forking — a fork will diverge within two patch releases at this cadence.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo