PHP code example of glueful / tenancy

1. Go to this page and download the library: Download glueful/tenancy library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

glueful / tenancy example snippets


// config/serviceproviders.php
return [
    'enabled' => [
        'Glueful\\Extensions\\Tenancy\\TenancyControlPlaneProvider',
        // ...application providers
    ],
];

'membership' => [
    'role_authority' => App\Tenancy\WorkspaceRoleAuthority::class,
],

// migration
$schema->createTable('projects', function ($table) {
    $table->bigInteger('id')->primary()->autoIncrement();
    $table->string('uuid', 12);
    $table->string('tenant_uuid', 12);          // <-- tenant ownership column
    $table->string('slug', 255);
    $table->string('name', 255);
    $table->timestamp('created_at')->default('CURRENT_TIMESTAMP');

    $table->unique('uuid');
    $table->index('tenant_uuid');

    // ⚠ per-tenant-unique business key MUST be COMPOSITE — see below
    $table->unique(['tenant_uuid', 'slug']);
});

use Glueful\Database\ORM\Model;
use Glueful\Extensions\Tenancy\ORM\Concerns\BelongsToTenant;

final class Project extends Model
{
    use BelongsToTenant;

    protected string $table = 'projects';

    protected array $fillable = ['slug', 'name'];
    // tenant_uuid does NOT need to be in $fillable — it is force-stamped on create.
}

'tables' => ['projects'],

$table->unique(['tenant_uuid', 'slug']);   // ✅ each tenant may use 'flagship'
// $table->unique('slug');                   // ❌ tenant B can never reuse a slug tenant A took

$router->group(['middleware' => ['auth', 'tenant']], function ($router) {
    $router->get('/projects', [ProjectController::class, 'index']);
});

// A central/optional route tolerates a missing tenant:
$router->get('/account', [AccountController::class, 'show'])->middleware(['auth', 'tenant:optional']);

'resolvers' => ['subdomain', 'path', 'header', 'query', 'jwt', 'active_session'],

'subdomain' => ['base_domain' => env('TENANCY_BASE_DOMAIN')], // acme.app.com → acme
'path'      => ['segment' => 't'],                            // /t/acme/...   → acme
'header'    => ['name' => 'X-Tenant-Id'],                     // request header
'query'     => ['name' => 'tenant_id'],                       // ?tenant_id=acme
'jwt'       => ['claim' => 'tenant_id'],                      // jwt.claims[tenant_id]
// 'active_session' reads the 'tenancy.active_tenant' request attribute (UX/session-driven)

// Inside a tenant request (context set by the `tenant` middleware):
Project::query($context)->get();              // only this tenant's projects
Project::create($context, ['slug' => 'app']); // tenant_uuid stamped automatically

use Glueful\Extensions\Tenancy\Query\TenantQuery;

$rows = TenantQuery::tenantTable($context, 'projects')->where('archived', false)->get();

use Glueful\Extensions\Tenancy\Bypass\Tenancy;

// Act AS a specific tenant: queries scope to it, writes stamp it, no bypass active.
// Accepts a Tenant, or a uuid/slug string (resolved + active-checked).
Tenancy::runAsTenant('acme', function () {
    Project::create($context, ['slug' => 'q3']);
});

// System / no-tenant privileged maintenance (migrations, schedulers, cross-tenant admin):
Tenancy::runAsSystem(function () {
    // runs unscoped, no active tenant
});

// Cross-tenant READ — scoped reads suspended so every tenant's rows are visible.
Tenancy::forAnyTenant(function () {
    return Project::query($context)->get();
});

use Glueful\Queue\Job;
use Glueful\Extensions\Tenancy\Queue\PropagatesTenant;

final class SendInvoice extends Job
{
    use PropagatesTenant;

    public function __construct(array $data = [], ?ApplicationContext $context = null)
    {
        parent::__construct($data, $context);
        $this->captureTenantContext($context); // runs inside the request
    }

    public function handle(): void
    {
        $this->runInTenantContext(function (): void {
            // tenant-scoped work — DB guard / auto-injection now see the tenant
        });
    }
}

use Glueful\Console\BaseCommand;
use Glueful\Extensions\Tenancy\Console\Concerns\RunsInTenantContext;

final class BuildReports extends BaseCommand
{
    use RunsInTenantContext;

    protected function configure(): void
    {
        $this->setName('reports:build');
        $this->configureTenantOption();          // adds --tenant
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        return $this->runInTenantContext($input, function (): int {
            // scoped to --tenant, or trusted SYSTEM context when the flag is omitted
            return self::SUCCESS;
        });
    }
}

use Glueful\Extensions\Tenancy\Scheduling\ForEachTenant;
use Glueful\Extensions\Tenancy\Models\Tenant;

$result = ForEachTenant::run($context, function (Tenant $tenant): void {
    // tenant-scoped maintenance for $tenant; inactive tenants are skipped
});
bash
php glueful migrate:run
bash
php glueful extensions:enable Tenancy