PHP code example of phpsoftbox / multi-tenant

1. Go to this page and download the library: Download phpsoftbox/multi-tenant 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/ */

    

phpsoftbox / multi-tenant example snippets


use PhpSoftBox\MultiTenant\Auth\ScopedAuthRoleOperationsInterface;
use PhpSoftBox\MultiTenant\Auth\ScopedAuthRoleOperationsResolverInterface;

final class AppScopedAuthRoleOperationsResolver implements ScopedAuthRoleOperationsResolverInterface
{
    public function core(): ScopedAuthRoleOperationsInterface
    {
        return $this->buildCoreOperations();
    }

    public function tenant(): ScopedAuthRoleOperationsInterface
    {
        return $this->buildTenantOperations();
    }
}

// config/app/tenancy.php
return [
    'providers' => [
        'primary' => 'database', // config|database
        'enforce_parity' => true,
        'database' => [
            'connection' => 'default',
        ],
    ],
    'tenants' => [
        'tenant-1' => [
            'name' => 'Tenant 1',
            'database_connection' => 'tenant',
            'database_name' => 'tenant_db_1',
            'domains' => [
                ['domain' => 'tenant1.chegdesklad.local', 'is_primary' => true],
                ['domain' => 'www.tenant1.chegdesklad.local'],
            ],
            'data' => [
                'pushr' => [
                    'app_id' => 'tenant-1',
                    'secret' => 'secret-tenant-1',
                ],
                'notifications_config' => [
                    'mail' => [
                        'smtp' => [
                            'from' => '[email protected]',
                        ],
                        'brand' => [
                            'name' => 'Tenant 1',
                            'caption' => 'Tenant Portal',
                            'logo_url' => 'https://tenant1.chegdesklad.local/assets/logo.svg',
                            'unsubscribe_url' => 'https://tenant1.chegdesklad.local/profile/subscriptions',
                            'unsubscribe_label' => 'Управлять подпиской',
                        ],
                    ],
                    'notifications' => [
                        'email' => [
                            'view_path' => 'resources/views/tenant',
                        ],
                    ],
                ],
                'telegram_bots' => [
                    [
                        'code' => 'account',
                        'token' => 'telegram-token-account',
                        'username' => 'account_bot',
                        'is_default' => true,
                        'enabled' => true,
                    ],
                ],
            ],
            'enabled' => true,
        ],
    ],
    'provision' => [
        'template_tenant' => 'tenant-template',
    ],
];

use PhpSoftBox\Config\Config;
use PhpSoftBox\Database\Connection\ConnectionManagerInterface;
use PhpSoftBox\MultiTenant\Contracts\TenantProviderInterface;
use PhpSoftBox\MultiTenant\Tenant\Provider\ConfigTenantProvider;
use PhpSoftBox\MultiTenant\Tenant\Provider\ConsistentTenantProvider;
use PhpSoftBox\MultiTenant\Tenant\Provider\DatabaseTenantProvider;
use Psr\Container\ContainerInterface;

use function DI\factory;

return [
    TenantProviderInterface::class => factory(static function (ContainerInterface $container): TenantProviderInterface {
        return new ConsistentTenantProvider(
            primary: new DatabaseTenantProvider($container->get(ConnectionManagerInterface::class), 'default'),
            secondary: new ConfigTenantProvider($container->get(Config::class)),
            enforceParity: true,
        );
    }),
];

use PhpSoftBox\MultiTenant\Http\Middleware\TenantResolveMiddleware;
use PhpSoftBox\MultiTenant\Http\Resolver\HeaderTenantRequestResolver;

$middleware = new TenantResolveMiddleware(
    tenants: $tenantProvider,
    tenantRuntime: $tenantRuntime,
    resolver: new HeaderTenantRequestResolver(headerName: 'X-Tenant-Id'),
);

use PhpSoftBox\MultiTenant\Http\Resolver\ChainTenantRequestResolver;
use PhpSoftBox\MultiTenant\Http\Resolver\HeaderTenantRequestResolver;
use PhpSoftBox\MultiTenant\Http\Resolver\HostTenantRequestResolver;

$resolver = new ChainTenantRequestResolver([
    new HeaderTenantRequestResolver(),
    new HostTenantRequestResolver(),
]);

use PhpSoftBox\MultiTenant\Orm\TenantEntityManager;
use PhpSoftBox\MultiTenant\Orm\TenantEntityManagerInterface;
use PhpSoftBox\MultiTenant\Orm\TenantEntityManagerRegistry;
use PhpSoftBox\MultiTenant\Orm\TenantEntityManagerRegistryInterface;
use PhpSoftBox\MultiTenant\Bootstrap\TenantOrmEntityManagerBootstrapper;
use PhpSoftBox\Orm\Contracts\EntityAwareEntityManagerRegistryInterface;
use PhpSoftBox\Orm\Contracts\EntityManagerRegistryInterface as BaseEntityManagerRegistryInterface;
use Psr\Container\ContainerInterface;

use function DI\factory;
use function DI\get;

return [
    TenantEntityManagerRegistryInterface::class => factory(
        static fn (ContainerInterface $container): TenantEntityManagerRegistryInterface => new TenantEntityManagerRegistry(
            registry: $container->get(BaseEntityManagerRegistryInterface::class),
            tenantConnectionName: 'tenant',
        ),
    ),

    EntityAwareEntityManagerRegistryInterface::class => get(TenantEntityManagerRegistryInterface::class),

    TenantOrmEntityManagerBootstrapper::class => factory(
        static fn (ContainerInterface $container): TenantOrmEntityManagerBootstrapper => new TenantOrmEntityManagerBootstrapper(
            $container->get(TenantEntityManagerRegistryInterface::class),
        ),
    ),

    TenantEntityManagerInterface::class => factory(
        static fn (ContainerInterface $container): TenantEntityManagerInterface => new TenantEntityManager(
            $container->get(TenantEntityManagerRegistryInterface::class),
        ),
    ),
];

use PhpSoftBox\MultiTenant\Bootstrap\DatabaseTenantConnectionBootstrapper;
use PhpSoftBox\MultiTenant\Bootstrap\TenantBootstrapPipeline;
use PhpSoftBox\MultiTenant\Bootstrap\TenantOrmEntityManagerBootstrapper;

$pipeline = new TenantBootstrapPipeline([
    $container->get(DatabaseTenantConnectionBootstrapper::class),
    $container->get(TenantOrmEntityManagerBootstrapper::class),
    // Остальные tenant bootstrappers.
]);

new ServicesResetter($container, [
    // ...
    TenantEntityManagerRegistryInterface::class => 'reset',
]);

EntityAwareEntityManagerRegistryInterface::class => get(TenantEntityManagerRegistryInterface::class),

use PhpSoftBox\MultiTenant\Orm\ChangeLog\TenantMongoEntityChangeLogger;

$logger = new TenantMongoEntityChangeLogger(
    tenantMongo: $tenantMongo,
    logger: $psrLogger,
    collection: 'entity_changelog',
    technicalFields: ['created_datetime', 'updated_datetime', 'deleted_datetime'],
);

use PhpSoftBox\MultiTenant\Orm\ChangeLog\TenantMongoEntityChangeLogger;
use PhpSoftBox\Orm\Attribute\Changelog;

#[Changelog(logHandler: TenantMongoEntityChangeLogger::class)]
final class Product
{
}

use PhpSoftBox\MultiTenant\Orm\ChangeLog\Initiator\AuthManagerEntityChangeInitiatorResolver;
use PhpSoftBox\MultiTenant\Orm\ChangeLog\Initiator\ChainEntityChangeInitiatorResolver;
use PhpSoftBox\MultiTenant\Orm\ChangeLog\Initiator\RequestAttributeEntityChangeInitiatorResolver;
use PhpSoftBox\MultiTenant\Orm\ChangeLog\TenantEntityChangeContextResolver;

$initiatorResolver = new ChainEntityChangeInitiatorResolver([
    new RequestAttributeEntityChangeInitiatorResolver($request),
    new AuthManagerEntityChangeInitiatorResolver(
        request: $request,
        auth: $auth,
        guard: 'tenant',
    ),
]);

$contextResolver = new TenantEntityChangeContextResolver(
    tenantResolver: $tenantContextResolver,
    initiatorResolver: $initiatorResolver,
    request: $request,
);

$initiatorResolver = new RequestAttributeEntityChangeInitiatorResolver(
    request: $request,
    userIdAttribute: 'auth_user_id',
    userAttribute: '_authUser',
);

use PhpSoftBox\MultiTenant\Orm\ChangeLog\Initiator\StaticEntityChangeInitiatorResolver;
use PhpSoftBox\MultiTenant\Orm\ChangeLog\TenantEntityChangeContextResolver;

$contextResolver = new TenantEntityChangeContextResolver(
    tenantResolver: $tenantContextResolver,
    initiatorResolver: new StaticEntityChangeInitiatorResolver($job->initiatorUserId),
);

use PhpSoftBox\MultiTenant\Bootstrap\TenantBootstrapPipeline;
use PhpSoftBox\MultiTenant\Context\TenantContextFactory;
use Psr\Container\ContainerInterface;

use function DI\factory;

return [
    TenantContextFactory::class => factory(static function (ContainerInterface $container): TenantContextFactory {
        return new TenantContextFactory([
            $container->get(App\Tenancy\Loader\PushrTenantLoader::class),
            $container->get(App\Tenancy\Loader\TelegramTenantLoader::class),
        ]);
    }),

    TenantBootstrapPipeline::class => factory(static function (ContainerInterface $container): TenantBootstrapPipeline {
        return new TenantBootstrapPipeline([
            $container->get(App\Tenancy\Bootstrap\DatabaseConnectionBootstrapper::class),
            $container->get(App\Tenancy\Bootstrap\TelegramRegistryBootstrapper::class),
        ]);
    }),
];

'commands' => [
    'tenant:auth:role:sync --scope=tenant --tenant={tenant_id}',
    'tenant:user:create {owner_phone} --tenant={tenant_id} --name="{owner_name}" --email={owner_email}',
],