mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 04:54:03 +00:00
Merge branch 'archtechx:3.x' into 3.x
This commit is contained in:
commit
394d6ad338
15 changed files with 88 additions and 26 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -20,6 +20,8 @@ jobs:
|
||||||
php: "8.0"
|
php: "8.0"
|
||||||
- laravel: 10
|
- laravel: 10
|
||||||
php: "8.1"
|
php: "8.1"
|
||||||
|
- laravel: 11
|
||||||
|
php: "8.3"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,6 +5,7 @@ vendor/
|
||||||
.idea
|
.idea
|
||||||
psysh
|
psysh
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
.phpunit.cache
|
||||||
phpunit_var_*.xml
|
phpunit_var_*.xml
|
||||||
coverage/
|
coverage/
|
||||||
clover.xml
|
clover.xml
|
||||||
|
|
|
||||||
|
|
@ -120,10 +120,12 @@ class TenancyServiceProvider extends ServiceProvider
|
||||||
|
|
||||||
protected function mapRoutes()
|
protected function mapRoutes()
|
||||||
{
|
{
|
||||||
if (file_exists(base_path('routes/tenant.php'))) {
|
$this->app->booted(function () {
|
||||||
Route::namespace(static::$controllerNamespace)
|
if (file_exists(base_path('routes/tenant.php'))) {
|
||||||
->group(base_path('routes/tenant.php'));
|
Route::namespace(static::$controllerNamespace)
|
||||||
}
|
->group(base_path('routes/tenant.php'));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function makeTenancyMiddlewareHighestPriority()
|
protected function makeTenancyMiddlewareHighestPriority()
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.0",
|
"php": "^8.0",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"illuminate/support": "^9.0|^10.0",
|
"illuminate/support": "^9.0|^10.0|^11.0",
|
||||||
"facade/ignition-contracts": "^1.0.2",
|
"facade/ignition-contracts": "^1.0.2",
|
||||||
"ramsey/uuid": "^4.7.3",
|
"ramsey/uuid": "^4.7.3",
|
||||||
"stancl/jobpipeline": "^1.6.2",
|
"stancl/jobpipeline": "^1.6.2",
|
||||||
"stancl/virtualcolumn": "^1.3.1"
|
"stancl/virtualcolumn": "^1.3.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"laravel/framework": "^9.0|^10.0",
|
"laravel/framework": "^9.0|^10.0|^11.0",
|
||||||
"orchestra/testbench": "^7.0|^8.0",
|
"orchestra/testbench": "^7.0|^8.0|^9.0",
|
||||||
"league/flysystem-aws-s3-v3": "^3.12.2",
|
"league/flysystem-aws-s3-v3": "^3.12.2",
|
||||||
"doctrine/dbal": "^3.6.0",
|
"doctrine/dbal": "^3.6.0",
|
||||||
"spatie/valuestore": "^1.3.2"
|
"spatie/valuestore": "^1.3.2"
|
||||||
|
|
|
||||||
18
phpunit.xml
18
phpunit.xml
|
|
@ -1,13 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
|
||||||
<coverage>
|
<coverage>
|
||||||
<include>
|
|
||||||
<directory suffix=".php">./src</directory>
|
|
||||||
</include>
|
|
||||||
<exclude>
|
|
||||||
<file>./src/routes.php</file>
|
|
||||||
<file>./src/Vite.php</file>
|
|
||||||
</exclude>
|
|
||||||
<report>
|
<report>
|
||||||
<clover outputFile="coverage/phpunit/clover.xml"/>
|
<clover outputFile="coverage/phpunit/clover.xml"/>
|
||||||
<html outputDirectory="coverage/phpunit/html" lowUpperBound="35" highLowerBound="70"/>
|
<html outputDirectory="coverage/phpunit/html" lowUpperBound="35" highLowerBound="70"/>
|
||||||
|
|
@ -29,4 +22,13 @@
|
||||||
<env name="AWS_DEFAULT_REGION" value="us-west-2"/>
|
<env name="AWS_DEFAULT_REGION" value="us-west-2"/>
|
||||||
</php>
|
</php>
|
||||||
<logging/>
|
<logging/>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">./src</directory>
|
||||||
|
</include>
|
||||||
|
<exclude>
|
||||||
|
<file>./src/routes.php</file>
|
||||||
|
<file>./src/Vite.php</file>
|
||||||
|
</exclude>
|
||||||
|
</source>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
|
||||||
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey();
|
$prefix = $this->config['tenancy.redis.prefix_base'] . $tenant->getTenantKey();
|
||||||
$client = Redis::connection($connection)->client();
|
$client = Redis::connection($connection)->client();
|
||||||
|
|
||||||
$this->originalPrefixes[$connection] = $client->getOption($client::OPT_PREFIX);
|
$this->originalPrefixes[$connection] = $client->getOption(\Redis::OPT_PREFIX);
|
||||||
$client->setOption($client::OPT_PREFIX, $prefix);
|
$client->setOption(\Redis::OPT_PREFIX, $prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ class RedisTenancyBootstrapper implements TenancyBootstrapper
|
||||||
foreach ($this->prefixedConnections() as $connection) {
|
foreach ($this->prefixedConnections() as $connection) {
|
||||||
$client = Redis::connection($connection)->client();
|
$client = Redis::connection($connection)->client();
|
||||||
|
|
||||||
$client->setOption($client::OPT_PREFIX, $this->originalPrefixes[$connection]);
|
$client->setOption(\Redis::OPT_PREFIX, $this->originalPrefixes[$connection]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->originalPrefixes = [];
|
$this->originalPrefixes = [];
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ final class MigrateFresh extends Command
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null);
|
$this->addOption('--drop-views', null, InputOption::VALUE_NONE, 'Drop views along with tenant tables.', null);
|
||||||
|
$this->addOption('--step', null, InputOption::VALUE_NONE, 'Force the migrations to be run so they can be rolled back individually.');
|
||||||
|
|
||||||
$this->setName('tenants:migrate-fresh');
|
$this->setName('tenants:migrate-fresh');
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +48,7 @@ final class MigrateFresh extends Command
|
||||||
$this->info('Migrating.');
|
$this->info('Migrating.');
|
||||||
$this->callSilent('tenants:migrate', [
|
$this->callSilent('tenants:migrate', [
|
||||||
'--tenants' => [$tenant->getTenantKey()],
|
'--tenants' => [$tenant->getTenantKey()],
|
||||||
|
'--step' => $this->option('step'),
|
||||||
'--force' => true,
|
'--force' => true,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
trait TenantAwareCommand
|
trait TenantAwareCommand
|
||||||
{
|
{
|
||||||
/** @return int */
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
protected function execute(InputInterface $input, OutputInterface $output)
|
|
||||||
{
|
{
|
||||||
$tenants = $this->getTenants();
|
$tenants = $this->getTenants();
|
||||||
$exitCode = 0;
|
$exitCode = 0;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class UniversalRoutes implements Feature
|
||||||
|
|
||||||
public static function routeHasMiddleware(Route $route, $middleware): bool
|
public static function routeHasMiddleware(Route $route, $middleware): bool
|
||||||
{
|
{
|
||||||
if (in_array($middleware, $route->middleware(), true)) {
|
if (in_array($middleware, $route->computedMiddleware ?? $route->middleware(), true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,14 @@ class PathTenantResolver extends Contracts\CachedTenantResolver
|
||||||
throw new TenantCouldNotBeIdentifiedByPathException($id);
|
throw new TenantCouldNotBeIdentifiedByPathException($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function resolved(Tenant $tenant, ...$args): void
|
||||||
|
{
|
||||||
|
/** @var Route $route */
|
||||||
|
$route = $args[0];
|
||||||
|
|
||||||
|
$route->forgetParameter(static::$tenantParameterName);
|
||||||
|
}
|
||||||
|
|
||||||
public function getArgsForTenant(Tenant $tenant): array
|
public function getArgsForTenant(Tenant $tenant): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,6 @@ class Vite extends BaseVite // todo move to a different directory in v4
|
||||||
*/
|
*/
|
||||||
protected function assetPath($path, $secure = null)
|
protected function assetPath($path, $secure = null)
|
||||||
{
|
{
|
||||||
return global_asset($path);
|
return $this->assetPathResolver ? ($this->assetPathResolver)($path, $secure) : global_asset($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,10 +196,12 @@ class CommandsTest extends TestCase
|
||||||
{
|
{
|
||||||
$tenantId1 = Tenant::create()->getTenantKey();
|
$tenantId1 = Tenant::create()->getTenantKey();
|
||||||
$tenantId2 = Tenant::create()->getTenantKey();
|
$tenantId2 = Tenant::create()->getTenantKey();
|
||||||
|
$tenantId3 = Tenant::create()->getTenantKey();
|
||||||
Artisan::call('tenants:migrate-fresh');
|
Artisan::call('tenants:migrate-fresh');
|
||||||
|
|
||||||
$this->artisan("tenants:run foo --tenants=$tenantId1 --tenants=$tenantId2 --argument='a=foo' --option='b=bar' --option='c=xyz'")
|
$this->artisan("tenants:run foo --tenants=$tenantId1 --tenants=$tenantId2 --argument='a=foo' --option='b=bar' --option='c=xyz'")
|
||||||
->expectsOutput('Tenant: ' . $tenantId1)
|
->expectsOutput('Tenant: ' . $tenantId1)
|
||||||
->expectsOutput('Tenant: ' . $tenantId2);
|
->expectsOutput('Tenant: ' . $tenantId2)
|
||||||
|
->doesntExpectOutput('Tenant: ' . $tenantId3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Tests;
|
namespace Stancl\Tenancy\Tests;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Spatie\Valuestore\Valuestore;
|
use Spatie\Valuestore\Valuestore;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
@ -25,7 +23,6 @@ use Illuminate\Queue\Events\JobProcessed;
|
||||||
use Illuminate\Queue\Events\JobProcessing;
|
use Illuminate\Queue\Events\JobProcessing;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use PDO;
|
|
||||||
use Stancl\Tenancy\Events\TenancyInitialized;
|
use Stancl\Tenancy\Events\TenancyInitialized;
|
||||||
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
||||||
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
||||||
|
|
@ -59,6 +56,7 @@ class QueueTest extends TestCase
|
||||||
|
|
||||||
public function tearDown(): void
|
public function tearDown(): void
|
||||||
{
|
{
|
||||||
|
parent::tearDown();
|
||||||
$this->valuestore->flush();
|
$this->valuestore->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,11 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
|
|
||||||
$app['config']->set([
|
$app['config']->set([
|
||||||
'database.default' => 'central',
|
'database.default' => 'central',
|
||||||
|
'cache.default' => 'redis',
|
||||||
'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
|
'database.redis.cache.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
|
||||||
'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
|
'database.redis.default.host' => env('TENANCY_TEST_REDIS_HOST', '127.0.0.1'),
|
||||||
'database.redis.options.prefix' => 'foo',
|
'database.redis.options.prefix' => 'foo',
|
||||||
|
'database.redis.client' => 'predis',
|
||||||
'database.connections.central' => [
|
'database.connections.central' => [
|
||||||
'driver' => 'mysql',
|
'driver' => 'mysql',
|
||||||
'url' => env('DATABASE_URL'),
|
'url' => env('DATABASE_URL'),
|
||||||
|
|
@ -80,6 +82,8 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
]) : [],
|
]) : [],
|
||||||
],
|
],
|
||||||
'database.connections.sqlite.database' => ':memory:',
|
'database.connections.sqlite.database' => ':memory:',
|
||||||
|
'database.connections.mysql.charset' => 'utf8mb4',
|
||||||
|
'database.connections.mysql.collation' => 'utf8mb4_unicode_ci',
|
||||||
'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'),
|
'database.connections.mysql.host' => env('TENANCY_TEST_MYSQL_HOST', '127.0.0.1'),
|
||||||
'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'),
|
'database.connections.pgsql.host' => env('TENANCY_TEST_PGSQL_HOST', '127.0.0.1'),
|
||||||
'tenancy.filesystem.disks' => [
|
'tenancy.filesystem.disks' => [
|
||||||
|
|
|
||||||
|
|
@ -63,4 +63,46 @@ class UniversalRouteTest extends TestCase
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertSee('acme');
|
->assertSee('acme');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function universal_route_works_when_middleware_is_inserted_via_controller_middleware()
|
||||||
|
{
|
||||||
|
Route::middlewareGroup('universal', []);
|
||||||
|
config(['tenancy.features' => [UniversalRoutes::class]]);
|
||||||
|
|
||||||
|
Route::get('/foo', [UniversalRouteController::class, 'show']);
|
||||||
|
|
||||||
|
$this->get('http://localhost/foo')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('Tenancy is not initialized.');
|
||||||
|
|
||||||
|
$tenant = Tenant::create([
|
||||||
|
'id' => 'acme',
|
||||||
|
]);
|
||||||
|
$tenant->domains()->create([
|
||||||
|
'domain' => 'acme.localhost',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->get('http://acme.localhost/foo')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('Tenancy is initialized.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UniversalRouteController
|
||||||
|
{
|
||||||
|
public function getMiddleware()
|
||||||
|
{
|
||||||
|
return array_map(fn($middleware) => [
|
||||||
|
'middleware' => $middleware,
|
||||||
|
'options' => [],
|
||||||
|
], ['universal', InitializeTenancyByDomain::class]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return tenancy()->initialized
|
||||||
|
? 'Tenancy is initialized.'
|
||||||
|
: 'Tenancy is not initialized.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue