### Storage path examples Source: https://tenancyforlaravel.com/docs/v3/realtime-facades Illustrates the difference between tenant-scoped storage paths and incorrect path structures. ```text storage/tenant123/app/foo.png ``` ```text storage/app/tenant123/foo.png ``` -------------------------------- ### Configure Nova Middleware Source: https://tenancyforlaravel.com/docs/v3/integrations/nova Example configuration for the nova.middleware array to include tenancy-specific middleware. ```php 'middleware' => [ // You can make this simpler by creating a tenancy route group InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class, 'web', Authenticate::class, DispatchServingNovaEvent::class, BootTools::class, Authorize::class, ], ``` -------------------------------- ### Run Tenancy Installation Command Source: https://tenancyforlaravel.com/docs/v3/installation Generates necessary migrations, configuration, routes, and service provider files. ```bash php artisan tenancy:install ``` -------------------------------- ### Run Migrations Source: https://tenancyforlaravel.com/docs/v3/quickstart After installation, run the Artisan migrate command to apply the database migrations for the tenancy package. ```bash php artisan migrate ``` -------------------------------- ### Base Test Case for Tenant Initialization Source: https://tenancyforlaravel.com/docs/v3/testing Configure a base test case to handle tenant creation and initialization during setup. ```php class TestCase // extends ... { protected $tenancy = false; public function setUp(): void { parent::setUp(); if ($this->tenancy) { $this->initializeTenancy(); } } public function initializeTenancy() { $tenant = Tenant::create(); tenancy()->initialize($tenant); } // ... } ``` -------------------------------- ### Run Tenancy Artisan Command Source: https://tenancyforlaravel.com/docs/v3/quickstart Execute the tenancy:install Artisan command to generate necessary configuration files, migrations, and route files for tenancy. ```bash php artisan tenancy:install ``` -------------------------------- ### Tenant Test Case Implementation Source: https://tenancyforlaravel.com/docs/v3/testing Example of enabling tenancy for specific test classes by extending the base test case. ```php class FooTest extends TestCase { protected $tenancy = true; /** @test */ public function some_test() { $this->assertTrue(...); } } ``` -------------------------------- ### Install Tenancy for Laravel via Composer Source: https://tenancyforlaravel.com/docs/v3/installation Requires Laravel 9.0 or higher. ```bash composer require stancl/tenancy ``` -------------------------------- ### Define primary and secondary models Source: https://tenancyforlaravel.com/docs/v3/single-database-tenancy Example of setting up a primary model with BelongsToTenant and a secondary model without it. ```php class Post extends Model { use BelongsToTenant; public function comments() { return $this->hasMany(Comment::class); } } class Comment extends Model { public function post() { return $this->belongsTo(Post::class); } } ``` -------------------------------- ### Create a Tenant Source: https://tenancyforlaravel.com/docs/v3/tenants Tenants can be created like any other Eloquent model. After creation, events are fired to handle tasks like database setup. ```php $tenant = Tenant::create([ 'plan' => 'free', ]); ``` -------------------------------- ### Configure Central Domains (Default) Source: https://tenancyforlaravel.com/docs/v3/quickstart Specify the central domains for your application in the `config/tenancy.php` file. This example uses 'saas.test' for local development with Laravel Valet. ```php 'central_domains' => [ 'saas.test', // Add the ones that you use. I use this one with Laravel Valet. ], ``` -------------------------------- ### Install stancl/tenancy Package Source: https://tenancyforlaravel.com/docs/v3/quickstart Use Composer to add the stancl/tenancy package to your Laravel project. This is the first step in setting up multi-tenancy. ```bash composer require stancl/tenancy ``` -------------------------------- ### Create Central User Source: https://tenancyforlaravel.com/docs/v3/synced-resources-between-tenants Example of creating a user in the central database. This user record exists only in the central database and serves as the master record for synchronization. ```php $user = CentralUser::create([ 'global_id' => 'acme', 'name' => 'John Doe', 'email' => 'john@localhost', 'password' => 'secret', 'role' => 'superadmin', // unsynced ]); ``` -------------------------------- ### Define unique indexes for tenant-scoped tables Source: https://tenancyforlaravel.com/docs/v3/single-database-tenancy Examples of scoping unique indexes to include tenant or parent model identifiers. ```php $table->unique('slug'); ``` ```php $table->unique(['tenant_id', 'slug']); ``` ```php // Imagine we're in a 'comments' table migration $table->unique(['post_id', 'user_id']); ``` -------------------------------- ### Define Custom Tenant Model Source: https://tenancyforlaravel.com/docs/v3/tenants Extend the base Tenant model to include database and domain features. This is a common setup for applications requiring per-tenant databases and domain identification. ```php '/{tenant}', 'middleware' => [InitializeTenancyByPath::class], ], function () { Route::get('/foo', 'FooController@index'); }); ``` -------------------------------- ### Create Tenant User and Initialize Tenancy Source: https://tenancyforlaravel.com/docs/v3/synced-resources-between-tenants Initializes tenancy for a specific tenant and then creates a user within that tenant's database. The user in the tenant database shares the same 'global_id' as the central user. ```php tenancy()->initialize($tenant); // Create the same user in tenant DB $user = User::create([ 'global_id' => 'acme', 'name' => 'John Doe', 'email' => 'john@localhost', 'password' => 'secret', 'role' => 'commenter', // unsynced ]); ``` -------------------------------- ### Run Migrations Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Execute this command after publishing the migrations to create the necessary database table for impersonation tokens. ```bash php artisan migrate ``` -------------------------------- ### Configure Tenant File Field Previews Source: https://tenancyforlaravel.com/docs/v3/integrations/nova Implementation of thumbnail and preview methods using tenant_asset to ensure correct file path resolution. ```php Avatar::make('Avatar', 'photo') ->disk('public') ->thumbnail(fn ($value, $disk) => tenant_asset($value)), ->preview(fn ($value, $disk) => tenant_asset($value)), ``` -------------------------------- ### Create framework directories for tenants Source: https://tenancyforlaravel.com/docs/v3/realtime-facades A job class to be added to the TenantCreated pipeline to ensure necessary framework directories exist for real-time facades. ```php tenant = $tenant; } public function handle() { $this->tenant->run(function ($tenant) { $storage_path = storage_path(); mkdir("$storage_path/framework/cache", 0777, true); }); } } ``` -------------------------------- ### Populate Tenant Databases Source: https://tenancyforlaravel.com/docs/v3/quickstart Execute logic across all tenant databases using the runForEach method to seed data like users. ```php App\Models\Tenant::all()->runForEach(function () { App\Models\User::factory()->create(); }); ``` -------------------------------- ### Initialize Tenancy Source: https://tenancyforlaravel.com/docs/v3/event-system This code initializes the current tenant. It's typically called by identification middleware. ```php $this->tenancy->initialize($tenant); ``` -------------------------------- ### Publish Impersonation Migrations Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Run this Artisan command to publish the migration file for the `tenant_user_impersonation_tokens` table. ```bash php artisan vendor:publish --tag=impersonation-migrations ``` -------------------------------- ### Apply Middleware to Tenant Routes Source: https://tenancyforlaravel.com/docs/v3/integrations/orchid Include the platform middleware and tenancy identification in the tenant route file. ```php Route::middleware([ 'web', 'platform', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class, ]); ``` -------------------------------- ### Execute tenant migrations Source: https://tenancyforlaravel.com/docs/v3/migrations Run this command to execute migrations located in the tenant-specific directory. ```bash php artisan tenants:migrate ``` -------------------------------- ### Map Tenant Storage to Multiple Config Keys Source: https://tenancyforlaravel.com/docs/v3/features/tenant-config To map a single tenant storage value to multiple configuration keys, provide an array of config paths as the value in the `$storageToConfigMap`. ```php \Stancl\Tenancy\Features\TenantConfig::$storageToConfigMap = [ 'locale' => [ 'app.locale', 'locales.default', ], ]; ``` -------------------------------- ### Enable TenantConfig Feature Source: https://tenancyforlaravel.com/docs/v3/features/tenant-config Uncomment the TenantConfig class in your `tenancy.features` configuration file to enable this feature. ```php // Stancl\Tenancy\Features\TenantConfig::class, ``` -------------------------------- ### Automate Privilege Granting in Docker Source: https://tenancyforlaravel.com/docs/v3/integrations/sail Configuration snippet for docker-compose.yml to mount an initialization SQL file for the MySQL container. ```yaml mysql: # ... volumes: # ... - 'PATH_TO_THE_SQL_FILE:/docker-entrypoint-initdb.d/SQL_FILE_NAME.sql' ``` -------------------------------- ### Configure Livewire File Upload Middleware Source: https://tenancyforlaravel.com/docs/v3/integrations/livewire To enable file uploads with tenancy, specify the correct identification middleware for `FilePreviewController` and the temporary file upload middleware in `config/livewire.php`. ```php // specify the right identification middleware FilePreviewController::$middleware = ['web', 'universal', InitializeTenancyByDomain::class]; ``` ```php // config/livewire.php 'livewire.temporary_file_upload.middleware' => ['throttle:60,1', 'universal', InitializeTenancyByDomain::class], ``` -------------------------------- ### Publish configuration for laravel-login-link Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Publishes the configuration file for the login-link package. ```bash php artisan vendor:publish --tag="login-link-config" ``` -------------------------------- ### Run Code in Tenant Context Source: https://tenancyforlaravel.com/docs/v3/tenants Use the `run()` method on a tenant object to execute a callable within that tenant's context. This is useful for performing tenant-specific operations. ```php $tenant->run(function () { User::create(...); }); ``` -------------------------------- ### Create a bootstrapper for laravel-permission Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Alternative approach to managing permission cache keys using a dedicated bootstrapper class. ```php class SpatiePermissionsBootstrapper implements TenancyBootstrapper { public function __construct( protected PermissionRegistrar $registrar, ) {} public function bootstrap(Tenant $tenant): void { $this->registrar->cacheKey = 'spatie.permission.cache.tenant.' . $tenant->getTenantKey(); } public function revert(): void { $this->registrar->cacheKey = 'spatie.permission.cache'; } } ``` -------------------------------- ### Manually Initialize Tenancy Source: https://tenancyforlaravel.com/docs/v3/manual-initialization Use this method when you need to initialize tenancy for a specific tenant outside of the automatic middleware, command traits, or queue tenancy. This is common for interactive shell sessions like `artisan tinker`. ```php $tenant = Tenant::find('some-id'); tenancy()->initialize($tenant); ``` -------------------------------- ### Implementing a Custom Bootstrapper Source: https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers Create a class implementing the TenancyBootstrapper interface to define custom bootstrap and revert logic. ```php namespace App; use Stancl\Tenancy\Contracts\TenancyBootstrapper; use Stancl\Tenancy\Contracts\Tenant; class MyBootstrapper implements TenancyBootstrapper { public function bootstrap(Tenant $tenant) { // ... } public function revert() { // ... } } ``` -------------------------------- ### Enable UniversalRoutes Feature Source: https://tenancyforlaravel.com/docs/v3/features/universal-routes Uncomment this line in your tenancy.features configuration file to enable the feature. ```php Stancl\Tenancy\Features\UniversalRoutes::class, ``` -------------------------------- ### Custom Tenant Command Implementation Source: https://tenancyforlaravel.com/docs/v3/tenant-aware-commands Implement tenant awareness manually by accepting a `tenant_id` argument and using `tenancy()->find()` within the `handle()` method to execute code for a specific tenant. ```php tenancy()->find($this->argument('tenant_id'))->run(function () { // Your actual command code }); ``` -------------------------------- ### Configure Filesystem Root Override for Tenancy Source: https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers Customize the filesystem configuration to override disk roots for tenant awareness. The %storage_path% placeholder is replaced with the output of storage_path(). ```php // Tenancy config (tenancy.filesystem.root_override) // %storage_path% gets replaced by storage_path()'s output // E.g. Storage::disk('local')->path('') will return "/$path_to_your_application/storage/tenant42/app" // (Given a suffix_base of 'tenant' and a tenant with a key of `42`. Same as in the example above in the Storage path helper section) 'root_override' => [ 'local' => '%storage_path%/app/', 'public' => '%storage_path%/app/public/', ] ``` -------------------------------- ### Tenant Command with Multiple Tenant Option Source: https://tenancyforlaravel.com/docs/v3/tenant-aware-commands Use `TenantAwareCommand` and `HasATenantsOption` traits for commands that accept multiple tenant IDs. By default, the command runs for all tenants if no option is provided. ```php class FooCommand extends Command { use TenantAwareCommand, HasATenantsOption; public function handle() { // } } ``` -------------------------------- ### Enable Telescope Tags Feature Source: https://tenancyforlaravel.com/docs/v3/features/telescope-tags Uncomment this line in your `tenancy.features` config file to enable automatic tagging of Telescope requests by the active tenant. ```php // Stancl\Tenancy\Features\TelescopeTags::class, ``` -------------------------------- ### Define a basic job pipeline Source: https://tenancyforlaravel.com/docs/v3/event-system Initialize a pipeline by specifying an array of job classes to be executed. ```php use Stancl\JobPipeline\JobPipeline; use Stancl\Tenancy\Jobs\{CreateDatabase, MigrateDatabase, SeedDatabase}; JobPipeline::make([ CreateDatabase::class, MigrateDatabase::class, SeedDatabase::class, ]) ``` -------------------------------- ### Register a pipeline as an event listener Source: https://tenancyforlaravel.com/docs/v3/event-system Convert the configured pipeline into a listener and bind it to an event using the Event facade. ```php use Stancl\Tenancy\Events\TenantCreated; use Stancl\JobPipeline\JobPipeline; use Stancl\Tenancy\Jobs\{CreateDatabase, MigrateDatabase, SeedDatabase}; use Illuminate\Support\Facades\Event; Event::listen(TenantCreated::class, JobPipeline::make([ CreateDatabase::class, MigrateDatabase::class, SeedDatabase::class, ])->send(function (TenantCreated $event) { return $event->tenant; })->shouldBeQueued(true)->toListener()); ``` -------------------------------- ### Map Tenant Storage to Config Keys Source: https://tenancyforlaravel.com/docs/v3/features/tenant-config Configure the `$storageToConfigMap` static property to map tenant storage keys (e.g., custom columns on the Tenant model) to Laravel config keys. This automatically copies the tenant's value to the specified config path upon tenancy initialization. ```php \Stancl\Tenancy\Features\TenantConfig::$storageToConfigMap = [ 'paypal_api_key' => 'services.paypal.api_key', ]; ``` -------------------------------- ### Register Tenancy Service Provider Source: https://tenancyforlaravel.com/docs/v3/installation Add the TenancyServiceProvider to the bootstrap/providers.php file. ```php return [ App\Providers\AppServiceProvider::class, App\Providers\TenancyServiceProvider::class, // <-- here ]; ``` -------------------------------- ### Map tenant keys to Passport configuration Source: https://tenancyforlaravel.com/docs/v3/integrations/passport Configure the storage-to-config mapping in the boot method of your TenancyServiceProvider to enable tenant-specific Passport keys. ```php \Stancl\Tenancy\Features\TenantConfig::$storageToConfigMap = [ 'passport_public_key' => 'passport.public_key', 'passport_private_key' => 'passport.private_key', ]; ``` -------------------------------- ### Listen to TenancyInitialized Event Source: https://tenancyforlaravel.com/docs/v3/event-system Register a listener for the TenancyInitialized event. This is commonly done in the TenancyServiceProvider to bootstrap tenancy configurations. ```php Event::listen(TenancyInitialized::class, BootstrapTenancy::class); ``` -------------------------------- ### Publish Nova Migrations Source: https://tenancyforlaravel.com/docs/v3/integrations/nova Command to publish Nova migrations for relocation to the tenant migration directory. ```bash php artisan vendor:publish --tag=nova-migrations ``` -------------------------------- ### List all tenants Source: https://tenancyforlaravel.com/docs/v3/console-commands Displays a list of all existing tenants and their associated identifiers. ```bash php artisan tenants:list Listing all tenants. [Tenant] id: dbe0b330-1a6e-11e9-b4c3-354da4b4f339 @ localhost [Tenant] id: 49670df0-1a87-11e9-b7ba-cf5353777957 @ dev.localhost ``` -------------------------------- ### Create Tenant Route for Impersonation Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Define a tenant route that accepts an impersonation token and uses `UserImpersonation::makeResponse()` to handle the login and redirection. ```php use Stancl\Tenancy\Features\UserImpersonation; // We're in your tenant routes! Route::get('/impersonate/{token}', function ($token) { return UserImpersonation::makeResponse($token); }); // Of course use a controller in a production app and not a Closure route. // Closure routes cannot be cached. ``` -------------------------------- ### Configuring Asset Controller Middleware Source: https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers Assign the identification middleware to the TenantAssetsController in your service provider. ```php // TenancyServiceProvider (don't forget to import the classes) public function boot() { // Update the middleware used by the asset controller TenantAssetsController::$tenancyMiddleware = InitializeTenancyByDomainOrSubdomain::class; } ``` -------------------------------- ### Set Tenancy Middleware for Assets Source: https://tenancyforlaravel.com/docs/v3/integrations/nova Configuration for TenantAssetsController middleware within the TenancyServiceProvider boot method. ```php // In App\Providers\TenancyServiceProvider public function boot(): void { // ... TenantAssetsController::$tenancyMiddleware = InitializeTenancyByDomainOrSubdomain::class; } ``` -------------------------------- ### TenantAssetsController File Response Source: https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers The default implementation for serving tenant-specific files via the asset helper. ```php // TenantAssetsController return response()->file(storage_path('app/public/' . $path)); ``` -------------------------------- ### Migrate tenant databases Source: https://tenancyforlaravel.com/docs/v3/console-commands Executes migrations for specified tenants. By default, it looks for migrations in database/migrations/tenant. ```bash php artisan tenants:migrate --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23 ``` -------------------------------- ### Redirect using the tenant_route helper Source: https://tenancyforlaravel.com/docs/v3/features/cross-domain-redirect The tenant_route helper generates a URL for a specific domain and route, which can then be used for redirection. ```php return redirect(tenant_route($domain, 'home')); ``` -------------------------------- ### Database migration for 3.x upgrade Source: https://tenancyforlaravel.com/docs/v3/upgrading Migration script to update the domains and tenants table schemas for version 3.x compatibility. ```php dropPrimary(); }); Schema::table('domains', function (Blueprint $table) { $table->increments('id')->first(); $table->unique('domain'); $table->timestamps(); }); Schema::table('tenants', function (Blueprint $table) { $table->json('data')->nullable()->change(); $table->timestamps(); }); DB::table('domains')->update([ 'created_at' => now(), 'updated_at' => now() ]); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('domains', function (Blueprint $table) { $table->dropColumn('id'); $table->dropUnique(['domain']); $table->dropTimestamps(); $table->primary('domain'); }); Schema::table('tenants', function (Blueprint $table) { $table->json('data')->nullable(false)->change(); $table->dropTimestamps(); }); } } ``` -------------------------------- ### Redirect to Impersonation Route (Path Identification) Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Construct a URL using the tenant's ID as a prefix to redirect the user to the impersonation route. ```php // Make sure you use the correct prefix for your routes. return redirect("{$tenant->id}/impersonate/{$token->token}"); ``` -------------------------------- ### Enable and Configure Tenant Resolver Caching Source: https://tenancyforlaravel.com/docs/v3/cached-lookup Configure static properties on the resolver class within the TenancyServiceProvider to enable caching and define TTL and storage. ```php // TenancyServiceProvider::boot() use Stancl\Tenancy\Resolvers; // enable cache DomainTenantResolver::$shouldCache = true; // seconds, 3600 is the default value DomainTenantResolver::$cacheTTL = 3600; // specify some cache store // null resolves to the default cache store DomainTenantResolver::$cacheStore = 'redis'; ``` -------------------------------- ### Registering Custom Bootstrappers Source: https://tenancyforlaravel.com/docs/v3/tenancy-bootstrappers Add your custom bootstrapper class to the tenancy configuration file. ```php 'bootstrappers' => [ Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper::class, Stancl\Tenancy\Bootstrappers\CacheTenancyBootstrapper::class, Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper::class, Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper::class, App\MyBootstrapper::class, ], ``` -------------------------------- ### Make Command Tenant-Aware with TenantAwareCommand Trait Source: https://tenancyforlaravel.com/docs/v3/tenant-aware-commands Utilize the `TenantAwareCommand` trait to make your Artisan command tenant-aware. This trait requires the implementation of a `getTenants()` method. ```php class MyCommand extends Command { use TenantAwareCommand; } ``` -------------------------------- ### Create Tenants and Domains in Tinker Source: https://tenancyforlaravel.com/docs/v3/quickstart Use Laravel Tinker to programmatically create tenant records and associate them with specific domains. ```php $ php artisan tinker >>> $tenant1 = App\Models\Tenant::create(['id' => 'foo']); >>> $tenant1->domains()->create(['domain' => 'foo.localhost']); >>> >>> $tenant2 = App\Models\Tenant::create(['id' => 'bar']); >>> $tenant2->domains()->create(['domain' => 'bar.localhost']); ``` -------------------------------- ### Run fresh migrations for tenants Source: https://tenancyforlaravel.com/docs/v3/console-commands Wipes the tenant database and runs migrations. This is a tenant-aware version of the standard migrate:fresh command. ```bash php artisan tenants:migrate-fresh --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23 ``` -------------------------------- ### Configure middleware for laravel-login-link Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Adds tenant identification middleware to the login-link configuration. ```php 'middleware' => [ 'web', // Add whatever tenant identification middlewares you use in your tenant routes InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class, ], ``` -------------------------------- ### Enable ViteBundler in Tenancy Configuration Source: https://tenancyforlaravel.com/docs/v3/features/vite-bundler Uncomment the ViteBundler class within the features array of the tenancy configuration file. ```php 'features' => [ // [...] Stancl\Tenancy\Features\ViteBundler::class, ], ``` -------------------------------- ### Configure Livewire Middleware Group (v2.x) Source: https://tenancyforlaravel.com/docs/v3/integrations/livewire For Livewire versions prior to 3.x, modify the 'middleware_group' in `config/livewire.php` to include tenancy middleware. ```php 'middleware_group' => [ 'web', 'universal', InitializeTenancyByDomain::class, // or whatever tenancy middleware you use ], ``` -------------------------------- ### Configure Orchid Platform Middleware Source: https://tenancyforlaravel.com/docs/v3/integrations/orchid Add tenant identification middleware to the platform configuration to ensure proper tenant context. ```php 'middleware' => [ 'public' => ['web', 'universal', InitializeTenancyByDomain::class], // Don't forget to import the middleware 'private' => ['web', 'platform', 'universal', InitializeTenancyByDomain::class], ], ``` -------------------------------- ### Configure Tenant Model in Tenancy Config Source: https://tenancyforlaravel.com/docs/v3/tenants Specify your custom Tenant model in the `config/tenancy.php` file. This tells the package which model to use for tenant-related operations. ```php 'tenant_model' => \App\Tenant::class, ``` -------------------------------- ### Register Tenant Routes Source: https://tenancyforlaravel.com/docs/v3/routes Register tenant-specific routes in `routes/tenant.php`. These routes are applied with 'web' middleware, initialization middleware, and protection against central domain access. ```php Route::middleware([ 'web', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class, ])->group(function () { Route::get('/', function () { return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id'); }); }); ``` -------------------------------- ### Create Custom Tenant Model Source: https://tenancyforlaravel.com/docs/v3/quickstart Define a custom Tenant model that extends the base Tenant model and uses HasDatabase and HasDomains traits for multi-database and domain support. ```php middleware(['universal', InitializeTenancyByDomain::class]); ``` -------------------------------- ### Execute custom commands for tenants Source: https://tenancyforlaravel.com/docs/v3/console-commands Runs arbitrary artisan commands across specified tenants using CLI or programmatic calls. ```bash php artisan tenants:run email:send --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23 --option="queue=1" --option="subject=New Feature" --argument="body=We have launched a new feature. ..." ``` ```php Artisan::call('tenants:run', [ 'commandname' => 'email:send', // String '--tenants' => ['8075a580-1cb8-11e9-8822-49c5d8f8ff23'] // Array '--option' => ['queue=1', 'subject=New Feature'] // Array '--argument' => ['body=We have launched a new feature.'] // Array ]) ``` -------------------------------- ### Create tenants table migration Source: https://tenancyforlaravel.com/docs/v3/tenant-attribute-encryption Add custom columns to the tenants table with sufficient length to store encrypted data. ```php string('id')->primary(); // Your custom columns $table->string('tenancy_db_username', 512)->nullable(); $table->string('tenancy_db_password', 512)->nullable(); $table->timestamps(); $table->json('data')->nullable(); }); } } ``` -------------------------------- ### Configure pipeline queuing Source: https://tenancyforlaravel.com/docs/v3/event-system Enable background processing for the pipeline by calling shouldBeQueued(true). ```php use Stancl\Tenancy\Events\TenantCreated; use Stancl\JobPipeline\JobPipeline; use Stancl\Tenancy\Jobs\{CreateDatabase, MigrateDatabase, SeedDatabase}; JobPipeline::make([ CreateDatabase::class, MigrateDatabase::class, SeedDatabase::class, ])->send(function (TenantCreated $event) { return $event->tenant; })->shouldBeQueued(true), ``` -------------------------------- ### Tenant Command with Single Tenant Argument Source: https://tenancyforlaravel.com/docs/v3/tenant-aware-commands Employ `TenantAwareCommand` and `HasATenantArgument` traits for commands that require a single tenant ID as a required argument. ```php class BarCommand extends Command { use TenantAwareCommand, HasATenantArgument; public function handle() { // } } ``` -------------------------------- ### Tenant User Model with Resource Syncing Source: https://tenancyforlaravel.com/docs/v3/synced-resources-between-tenants Defines the User model for tenant databases, implementing resource syncing. It includes methods for managing global identifiers and synced attributes, similar to the CentralUser model. ```php class User extends Model implements Syncable { use ResourceSyncing; protected $guarded = []; public $timestamps = false; public function getGlobalIdentifierKey() { return $this->getAttribute($this->getGlobalIdentifierKeyName()); } public function getGlobalIdentifierKeyName(): string { return 'global_id'; } public function getCentralModelName(): string { return CentralUser::class; } public function getSyncedAttributeNames(): array { return [ 'name', 'password', 'email', ]; } } ``` -------------------------------- ### Redirect to Impersonation Route (Domain Identification) Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Construct a URL using the tenant's primary domain to redirect the user to the impersonation route. ```php // Note: This is not part of the package, it's up to you to implement // a concept of "primary domains" if you need them. Or maybe you use // one domain per tenant. The package lets you do anything you want. $domain = $tenant->primary_domain; return redirect("https://$domain/impersonate/{$token->token}"); ``` -------------------------------- ### Configure Static Properties Source: https://tenancyforlaravel.com/docs/v3/configuration Set static properties for middleware or other classes, typically within the boot method of the TenancyServiceProvider. ```php \Stancl\Tenancy\Middleware\InitializeTenancyByDomain::$onFail = function () { return redirect('https://my-central-domain.com/'); }; ``` -------------------------------- ### Specify Tenant Database Name Source: https://tenancyforlaravel.com/docs/v3/customizing-databases Set the `tenancy_db_name` key when creating a tenant to define its specific database name. If not provided, the name is constructed from configuration prefixes and the tenant ID. ```php Tenant::create([ 'tenancy_db_name' => 'acme', ]); ``` -------------------------------- ### Publish and move migrations for laravel-permission Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Publishes the permission package migrations and moves them to the tenant migration directory. ```bash php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" --tag="migrations" mv database/migrations/*_create_permission_tables.php database/migrations/tenant ``` -------------------------------- ### Access Current Tenant Source: https://tenancyforlaravel.com/docs/v3/tenants The `tenant()` helper function can be used to access the current tenant model or a specific attribute from it (e.g., `tenant('id')`). ```php tenant('id') ``` -------------------------------- ### Define custom columns on Tenant model Source: https://tenancyforlaravel.com/docs/v3/tenant-attribute-encryption Register the custom columns in the Tenant model to ensure they are handled correctly by the package. ```php public static function getCustomColumns(): array { return [ 'id', 'tenancy_db_username', 'tenancy_db_password', ]; } ``` -------------------------------- ### Register CreateTenantConnection Listener Source: https://tenancyforlaravel.com/docs/v3/manual-mode Register the CreateTenantConnection listener in the TenancyServiceProvider to enable manual tenant connection management. ```php // app/Providers/TenancyServiceProvider.php Events\TenancyInitialized::class => [ Listeners\CreateTenantConnection::class, ], ``` -------------------------------- ### Attach a tenant to a central user Source: https://tenancyforlaravel.com/docs/v3/synced-resources-between-tenants Uses the TenantPivot model to establish a relationship and cascade synced resources to the tenant database. ```php $user = CentralUser::create(...); $user->tenants()->attach($tenant); ``` -------------------------------- ### Enable User Impersonation Feature Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Add the UserImpersonation class to the `features` array in your `config/tenancy.php` file to enable this feature. ```php Stancl\Tenancy\Features\UserImpersonation::class, ``` -------------------------------- ### Generate Impersonation Token Source: https://tenancyforlaravel.com/docs/v3/features/user-impersonation Use the `tenancy()->impersonate()` method to generate an impersonation token for a given tenant, user ID, and redirect URL. ```php // Let's say we want to be redirected to the dashboard // after we're logged in as the impersonated user. $redirectUrl = '/dashboard'; $token = tenancy()->impersonate($tenant, $user->id, $redirectUrl); ``` -------------------------------- ### Apply tenant-scoped validation rules Source: https://tenancyforlaravel.com/docs/v3/single-database-tenancy Manual scoping of unique and exists validation rules to the current tenant. ```php Rule::unique('posts', 'slug')->where('tenant_id', tenant('id')); ``` ```php // You may retrieve the current tenant using the tenant() helper. // $tenant = tenant(); $rules = [ 'id' => $tenant->exists('posts'), 'slug' => $tenant->unique('posts'), ] ``` -------------------------------- ### Register custom URL generator in media-library config Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Updates the media-library configuration to use the custom tenant-aware URL generator. ```php 'url_generator' => TenantAwareUrlGenerator::class, ``` -------------------------------- ### Configure event listeners for laravel-permission Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Registers listeners in TenancyServiceProvider to handle permission cache keys per tenant. ```php Events\TenancyBootstrapped::class => [ function (Events\TenancyBootstrapped $event) { $permissionRegistrar = app(\Spatie\Permission\PermissionRegistrar::class); $permissionRegistrar->cacheKey = 'spatie.permission.cache.tenant.' . $event->tenancy->tenant->getTenantKey(); } ], Events\TenancyEnded::class => [ function (Events\TenancyEnded $event) { $permissionRegistrar = app(\Spatie\Permission\PermissionRegistrar::class); $permissionRegistrar->cacheKey = 'spatie.permission.cache'; } ], ``` -------------------------------- ### Create a custom URL generator for laravel-medialibrary Source: https://tenancyforlaravel.com/docs/v3/integrations/spatie Extends the default URL generator to ensure media URLs are tenant-aware. ```php use Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator; class TenantAwareUrlGenerator extends DefaultUrlGenerator { public function getUrl(): string { $url = asset($this->getPathRelativeToRoot()); $url = $this->versionUrl($url); return $url; } } ``` -------------------------------- ### Create Passport clients in tenant seeder Source: https://tenancyforlaravel.com/docs/v3/integrations/passport Use this code in your tenant database seeder to generate default password grant and personal access clients for each tenant. ```php public function run() { $client = new ClientRepository(); $client->createPasswordGrantClient(null, 'Default password grant client', 'http://your.redirect.path'); $client->createPersonalAccessClient(null, 'Default personal access client', 'http://your.redirect.path'); } ```