diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..4bea7147
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: stancl
+patreon: samuelstancl
+open_collective: # Replace with a single Open Collective username
+ko_fi: samuelstancl
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://www.paypal.me/samuelstancl', 'https://gumroad.com/l/tenancy']
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..e98b3e80
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+name: CI
+
+env:
+ COMPOSE_INTERACTIVE_NO_CLI: 1
+
+on:
+ push:
+ branches: [ 2.x ]
+ pull_request:
+ branches: [ 2.x ]
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ laravel: ["^6.0", "^7.0"]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Start docker containers
+ run: docker-compose up -d
+ - name: Install dependencies
+ run: docker-compose exec -T test composer require --no-interaction "laravel/framework:${{ matrix.laravel }}"
+ - name: Run tests
+ run: ./fulltest
+ - name: Send code coverage to codecov
+ env:
+ CODECOV_TOKEN: 24382d15-84e7-4a55-bea4-c4df96a24a9b
+ run: bash <(curl -s https://codecov.io/bash)
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index d1a607ff..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-env:
- - LARAVEL_VERSION="^7.0"
- - LARAVEL_VERSION="^6.0"
-
-# language: php
-# php:
-# - '7.4'
-
-services:
- - docker
-
-before_install:
- - docker-compose up -d
-
-install:
- - travis_retry docker-compose exec test composer require --no-interaction "laravel/framework:$LARAVEL_VERSION"
- - travis_retry docker-compose exec test composer install --no-interaction
-
-before_script:
- - export DB_USERNAME=root DB_PASSWORD="" DB_DATABASE=tenancy CODECOV_TOKEN="24382d15-84e7-4a55-bea4-c4df96a24a9b"
- - cat vendor/laravel/framework/src/Illuminate/Foundation/Application.php| grep 'const VERSION'
-
-script: ./fulltest
-
-after_success:
- - bash <(curl -s https://codecov.io/bash)
diff --git a/DONATIONS.md b/DONATIONS.md
index 0b0cb90f..bb48a79a 100644
--- a/DONATIONS.md
+++ b/DONATIONS.md
@@ -4,9 +4,15 @@ Any donations will be greatly appreciated and help ensure that the package is de
If you're a company and this package is helping you make money, please consider donating.
+**If you'd like to donate in your local currency, see the Bank transfer section.**
+
+### Patreon
+
+If you would like to support me on a monthly basis, you can use Patreon: https://patreon.com/samuelstancl
+
### PayPal
-PayPal is the preferable donation method as it comes with the lowest fees.
+PayPal is the preferable donation method for one-time donations as it comes with the lowest fees.
You can donate here: [https://paypal.me/samuelstancl](https://paypal.me/samuelstancl)
@@ -16,11 +22,17 @@ If you can't use PayPal, you may use my Gumroad link. This comes with higher fee
You can donate here: [https://gumroad.com/l/tenancy](https://gumroad.com/l/tenancy)
+### Bank transfer
+
+If you'd like to donate money from your bank account, you can can do that too. I use [TransferWise](https://transferwise.com/invite/u/samuels1719) (affiliate link 🙂), so I can accept bank transfers in virtually any currency.
+
+Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com?subject=Donation) and I'll give you bank details for your local currency.
+
### Legal
If you're a business making a donation, you may want an invoice.
-Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com) and let me know what you need to have on the invoice and I will make it happen.
+Contact me on [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com?subject=Donation%20with%20invoice) and let me know what you need to have on the invoice and I will make it happen.
### Thank you!
diff --git a/README.md b/README.md
index 5f934854..a91ce3a4 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,16 @@
-
+
-
+
-
+
### *Automatic multi-tenancy for your Laravel app.*
@@ -20,9 +20,9 @@ You won't have to change a thing in your application's code.
- :heavy_check_mark: No replacing of Laravel classes (`Cache`, `Storage`, ...) with tenancy-aware classes
- :heavy_check_mark: Built-in tenant identification based on hostname (including second level domains)
-### [Documentation](https://tenancy.samuelstancl.me/docs/v2/)
+### [Documentation](https://tenancyforlaravel.com/docs/v2/)
-Documentation can be found here: https://tenancy.samuelstancl.me/docs/v2/
+Documentation can be found here: https://tenancyforlaravel.com/docs/v2/
The repository with the documentation source code can be found here: [stancl/tenancy-docs](https://github.com/stancl/tenancy-docs).
@@ -30,5 +30,5 @@ The repository with the documentation source code can be found here: [stancl/ten
### Credits
-- Created by [Samuel Å tancl](https://github.com/stancl)
-- Logo by [Caneco](https://twitter.com/caneco)
+- Created by [Samuel Å tancl](https://twitter.com/samuelstancl)
+- Logo by [Brian Dillingham](https://twitter.com/dillinghammm)
diff --git a/SUPPORT.md b/SUPPORT.md
index 8f782e51..e35775e1 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -2,6 +2,8 @@
If you need help with implementing the package, you can:
- Open an [issue here on GitHub](https://github.com/stancl/tenancy/issues/new?assignees=stancl&labels=support&template=support-question.md&title=)
-- Message me (`@stancl`) on the [Unofficial Laravel Discord](https://discord.gg/zGVGFAd), in the `#stancl_tenancy` channel
+- Join our new [Discord server](https://discord.gg/7cpgPxv) and ask in `#help`
+
+The methods above are preferred, but you may also
- Contact me on Telegram: [@samuelstancl](https://t.me/samuelstancl)
- Send me an email: [samuel.stancl@gmail.com](mailto:samuel.stancl@gmail.com)
diff --git a/art/logo.png b/art/logo.png
index 365ee375..9080de1e 100644
Binary files a/art/logo.png and b/art/logo.png differ
diff --git a/art/old_logo.png b/art/old_logo.png
new file mode 100644
index 00000000..365ee375
Binary files /dev/null and b/art/old_logo.png differ
diff --git a/assets/config.php b/assets/config.php
index 792ae4bf..aea10c5e 100644
--- a/assets/config.php
+++ b/assets/config.php
@@ -3,112 +3,286 @@
declare(strict_types=1);
return [
+ /**
+ * Storage drivers are used to store information about your tenants.
+ * They hold the Tenant Storage data and keeps track of domains.
+ */
'storage_driver' => 'db',
'storage_drivers' => [
+ /**
+ * The majority of applications will want to use this storage driver.
+ * The information about tenants is persisted in a relational DB
+ * like MySQL or PostgreSQL. The only downside is performance.
+ *
+ * A database connection to the central database has to be established on each
+ * request, to identify the tenant based on the domain. This takes three DB
+ * queries. Then, the connection to the tenant database is established.
+ *
+ * Note: From v2.3.0, the performance of the DB storage driver can be improved
+ * by a lot by using Cached Tenant Lookup. Be sure to enable that if you're
+ * using this storage driver. Enabling that feature can completely avoid
+ * querying the central database to identify build the Tenant object.
+ */
'db' => [
'driver' => Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver::class,
'data_column' => 'data',
'custom_columns' => [
// 'plan',
],
- 'connection' => null, // Your central database connection. Set to null to use the default connection.
+
+ /**
+ * Your central database connection. Set to null to use the default one.
+ *
+ * Note: It's recommended to create a designated central connection,
+ * to let you easily use it in your app, e.g. via the DB facade.
+ */
+ 'connection' => null,
+
'table_names' => [
'tenants' => 'tenants',
'domains' => 'domains',
],
- 'cache_store' => null, // What store should be used to cache tenant resolution. Set to null to disable cache or a string with a specific cache store name.
+
+ /**
+ * Here you can enable the Cached Tenant Lookup.
+ *
+ * You can specify what cache store should be used to cache the tenant resolution.
+ * Set to string with a specific cache store name, or to null to disable cache.
+ */
+ 'cache_store' => null,
'cache_ttl' => 3600, // seconds
],
+
+ /**
+ * The Redis storage driver is much more performant than the database driver.
+ * However, by default, Redis is a not a durable data storage. It works well for ephemeral data
+ * like cache, but to hold critical data, it needs to be configured in a way that guarantees
+ * that data will be persisted permanently. Specifically, you want to enable both AOF and
+ * RDB. Read this here: https://tenancy.samuelstancl.me/docs/v2/storage-drivers/#redis.
+ */
'redis' => [
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class,
'connection' => 'tenancy',
],
],
+
+ /**
+ * Controller namespace used by routes in routes/tenant.php.
+ */
'tenant_route_namespace' => 'App\Http\Controllers',
- 'exempt_domains' => [ // e.g. domains which host landing pages, sign up pages, etc
+
+ /**
+ * Central domains (hostnames), e.g. domains which host landing pages, sign up pages, etc.
+ */
+ 'exempt_domains' => [
// 'localhost',
],
- 'database' => [
- 'based_on' => null, // The connection that will be used as a base for the dynamically created tenant connection. Set to null to use the default connection.
- 'prefix' => 'tenant',
- 'suffix' => ''
- ],
- 'redis' => [
- 'prefix_base' => 'tenant',
- 'prefixed_connections' => [
- // 'default',
- ],
- ],
- 'cache' => [
- 'tag_base' => 'tenant',
- ],
- 'filesystem' => [ // https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/
- 'suffix_base' => 'tenant',
- 'suffix_storage_path' => true, // Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3.
- 'asset_helper_tenancy' => true, // should asset() be automatically tenant-aware. You may want to disable this if you use tools like Horizon.
- // Disks which should be suffixed with the suffix_base + tenant id.
- 'disks' => [
- 'local',
- 'public',
- // 's3',
- ],
- 'root_override' => [
- // Disks whose roots should be overriden after storage_path() is suffixed.
- 'local' => '%storage_path%/app/',
- 'public' => '%storage_path%/app/public/',
- ],
- ],
- 'database_managers' => [
- // Tenant database managers handle the creation & deletion of tenant databases.
- 'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
- 'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
- 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
- // 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
- ],
- 'database_manager_connections' => [
- // Connections used by TenantDatabaseManagers. This tells, for example, the
- // MySQLDatabaseManager to use the mysql connection to create databases.
- 'sqlite' => 'sqlite',
- 'mysql' => 'mysql',
- 'pgsql' => 'pgsql',
- ],
+
+ /**
+ * Tenancy bootstrappers are executed when tenancy is initialized.
+ * Their responsibility is making Laravel features tenant-aware.
+ *
+ * To configure their behavior, see the config keys below.
+ */
'bootstrappers' => [
- // Tenancy bootstrappers are executed when tenancy is initialized.
- // Their responsibility is making Laravel features tenant-aware.
'database' => Stancl\Tenancy\TenancyBootstrappers\DatabaseTenancyBootstrapper::class,
'cache' => Stancl\Tenancy\TenancyBootstrappers\CacheTenancyBootstrapper::class,
'filesystem' => Stancl\Tenancy\TenancyBootstrappers\FilesystemTenancyBootstrapper::class,
'queue' => Stancl\Tenancy\TenancyBootstrappers\QueueTenancyBootstrapper::class,
// 'redis' => Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class, // Note: phpredis is needed
],
- 'features' => [
- // Features are classes that provide additional functionality
- // not needed for tenancy to be bootstrapped. They are run
- // regardless of whether tenancy has been initialized.
- // Stancl\Tenancy\Features\Timestamps::class,
- // Stancl\Tenancy\Features\TenantConfig::class,
- // Stancl\Tenancy\Features\TelescopeTags::class,
- // Stancl\Tenancy\Features\TenantRedirect::class,
+ /**
+ * Database tenancy config. Used by DatabaseTenancyBootstrapper.
+ */
+ 'database' => [
+ /**
+ * The connection that will be used as a template for the dynamically created tenant connection.
+ * Set to null to use the default connection.
+ */
+ 'based_on' => null,
+
+ /**
+ * Tenant database names are created like this:
+ * prefix + tenant_id + suffix.
+ */
+ 'prefix' => 'tenant',
+ 'suffix' => '',
+ ],
+
+ /**
+ * Redis tenancy config. Used by RedisTenancyBoostrapper.
+ *
+ * Note: You need phpredis to use Redis tenancy.
+ *
+ * Note: You don't need to use this if you're using Redis only for cache.
+ * Redis tenancy is only relevant if you're making direct Redis calls,
+ * either using the Redis facade or by injecting it as a dependency.
+ */
+ 'redis' => [
+ 'prefix_base' => 'tenant', // Each key in Redis will be prepended by this prefix_base, followed by the tenant id.
+ 'prefixed_connections' => [ // Redis connections whose keys are prefixed, to separate one tenant's keys from another.
+ // 'default',
+ ],
+ ],
+
+ /**
+ * Cache tenancy config. Used by CacheTenancyBootstrapper.
+ *
+ * This works for all Cache facade calls, cache() helper
+ * calls and direct calls to injected cache stores.
+ *
+ * Each key in cache will have a tag applied on it. This tag is used to
+ * scope the cache both when writing to it and when reading from it.
+ */
+ 'cache' => [
+ 'tag_base' => 'tenant', // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call.
+ ],
+
+ /**
+ * Filesystem tenancy config. Used by FilesystemTenancyBootstrapper.
+ * https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/.
+ */
+ 'filesystem' => [
+ /**
+ * Each disk listed in the 'disks' array will be suffixed by the suffix_base, followed by the tenant_id.
+ */
+ 'suffix_base' => 'tenant',
+ 'disks' => [
+ 'local',
+ 'public',
+ // 's3',
+ ],
+
+ /**
+ * Use this for local disks.
+ *
+ * See https://tenancy.samuelstancl.me/docs/v2/filesystem-tenancy/
+ */
+ 'root_override' => [
+ // Disks whose roots should be overriden after storage_path() is suffixed.
+ 'local' => '%storage_path%/app/',
+ 'public' => '%storage_path%/app/public/',
+ ],
+
+ /**
+ * Should storage_path() be suffixed.
+ *
+ * Note: Disabling this will likely break local disk tenancy. Only disable this if you're using an external file storage service like S3.
+ *
+ * For the vast majority of applications, this feature should be enabled. But in some
+ * edge cases, it can cause issues (like using Passport with Vapor - see #196), so
+ * you may want to disable this if you are experiencing these edge case issues.
+ */
+ 'suffix_storage_path' => true,
+
+ /**
+ * By default, asset() calls are made multi-tenant too. You can use global_asset() and mix()
+ * for global, non-tenant-specific assets. However, you might have some issues when using
+ * packages that use asset() calls inside the tenant app. To avoid such issues, you can
+ * disable asset() helper tenancy and explicitly use tenant_asset() calls in places
+ * where you want to use tenant-specific assets (product images, avatars, etc).
+ */
+ 'asset_helper_tenancy' => true,
+ ],
+
+ /**
+ * TenantDatabaseManagers are classes that handle the creation & deletion of tenant databases.
+ */
+ 'database_managers' => [
+ 'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
+ 'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
+ 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
+
+ /**
+ * Disable the pgsql manager above, enable the one below, and set the
+ * tenancy.database.separate_by config key to 'schema' if you would
+ * like to separate tenant DBs by schemas rather than databases.
+ */
+ // 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
+ ],
+
+ /**
+ * Connections used by TenantDatabaseManagers. This tells, for example, the
+ * MySQLDatabaseManager to use the mysql connection to create databases.
+ */
+ 'database_manager_connections' => [
+ 'sqlite' => 'sqlite',
+ 'mysql' => 'mysql',
+ 'pgsql' => 'pgsql',
+ ],
+
+ /**
+ * Features are classes that provide additional functionality
+ * not needed for tenancy to be bootstrapped. They are run
+ * regardless of whether tenancy has been initialized.
+ *
+ * See the documentation page for each class to
+ * understand which ones you want to enable.
+ */
+ 'features' => [
+ // Stancl\Tenancy\Features\Timestamps::class, // https://tenancy.samuelstancl.me/docs/v2/features/timestamps/
+ // Stancl\Tenancy\Features\TenantConfig::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-config/
+ // Stancl\Tenancy\Features\TelescopeTags::class, // https://tenancy.samuelstancl.me/docs/v2/telescope/
+ // Stancl\Tenancy\Features\TenantRedirect::class, // https://tenancy.samuelstancl.me/docs/v2/features/tenant-redirect/
],
'storage_to_config_map' => [ // Used by the TenantConfig feature
// 'paypal_api_key' => 'services.paypal.api_key',
],
+
+ /**
+ * The URL to which users will be redirected when they try to acceess a central route on a tenant domain.
+ */
'home_url' => '/app',
+
+ /**
+ * Automatically create a database when creating a tenant.
+ */
'create_database' => true,
+
+ /**
+ * Should tenant databases be created asynchronously in a queued job.
+ */
'queue_database_creation' => false,
- 'migrate_after_creation' => false, // run migrations after creating a tenant
+
+ /**
+ * Should tenant migrations be ran after the tenant's database is created.
+ */
+ 'migrate_after_creation' => false,
'migration_parameters' => [
- // '--force' => true, // force database migrations
+ // '--force' => true, // Set this to true to be able to run migrations in production
+ // '--path' => [], // If you need to customize paths to tenant migrations
],
+
+ /**
+ * Should tenant databases be automatically seeded after they're created & migrated.
+ */
'seed_after_migration' => false, // should the seeder run after automatic migration
'seeder_parameters' => [
'--class' => 'DatabaseSeeder', // root seeder class, e.g.: 'DatabaseSeeder'
- // '--force' => true, // force database seeder
+ // '--force' => true,
],
+
+ /**
+ * Should tenant databases be deleted asynchronously in a queued job.
+ */
'queue_database_deletion' => false,
- 'delete_database_after_tenant_deletion' => false, // delete the tenant's database after deleting the tenant
+
+ /**
+ * Automatically delete the tenant's database after the tenant is deleted.
+ *
+ * This will save space but permanently delete data which you might want to keep.
+ */
+ 'delete_database_after_tenant_deletion' => false,
+
+ /**
+ * If you don't supply an id when creating a tenant, this class will be used to generate a random ID.
+ */
'unique_id_generator' => Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator::class,
+
+ /**
+ * Middleware pushed to the global middleware stack.
+ */
'global_middleware' => [
Stancl\Tenancy\Middleware\InitializeTenancy::class,
],
diff --git a/fulltest b/fulltest
index 0c526f4e..de5d7542 100755
--- a/fulltest
+++ b/fulltest
@@ -4,4 +4,4 @@ set -e
# for development
docker-compose up -d
./test "$@"
-docker-compose exec test vendor/bin/phpcov merge --clover clover.xml coverage/
+docker-compose exec -T test vendor/bin/phpcov merge --clover clover.xml coverage/
diff --git a/src/Commands/Seed.php b/src/Commands/Seed.php
index 8042e8dc..36f59dd0 100644
--- a/src/Commands/Seed.php
+++ b/src/Commands/Seed.php
@@ -43,6 +43,12 @@ class Seed extends SeedCommand
*/
public function handle()
{
+ foreach (config('tenancy.seeder_parameters') as $parameter => $value) {
+ if (! $this->input->hasParameterOption($parameter)) {
+ $this->input->setOption(ltrim($parameter, '-'), $value);
+ }
+ }
+
if (! $this->confirmToProceed()) {
return;
}
@@ -52,12 +58,6 @@ class Seed extends SeedCommand
$this->input->setOption('database', $tenant->getConnectionName());
- foreach (config('tenancy.seeder_parameters') as $parameter => $value) {
- if (! $this->input->hasParameterOption($parameter)) {
- $this->input->setOption(ltrim($parameter, '-'), $value);
- }
- }
-
$tenant->run(function () {
// Seed
parent::handle();
diff --git a/src/Features/TelescopeTags.php b/src/Features/TelescopeTags.php
index c1d4450f..7978214b 100644
--- a/src/Features/TelescopeTags.php
+++ b/src/Features/TelescopeTags.php
@@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Features;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Stancl\Tenancy\Contracts\Feature;
+use Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains;
use Stancl\Tenancy\TenantManager;
class TelescopeTags implements Feature
@@ -30,7 +31,15 @@ class TelescopeTags implements Feature
Telescope::tag(function (IncomingEntry $entry) {
$tags = $this->getTags($entry);
- if (in_array('tenancy', optional(request()->route())->middleware() ?? [])) {
+ if (! request()->route()) {
+ return $tags;
+ }
+
+ $tenantRoute = PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'tenancy')
+ || PreventAccessFromTenantDomains::routeHasMiddleware(request()->route(), 'universal');
+
+ // Don't do anything if we're visiting a universal route on a central domain
+ if ($tenantRoute && tenancy()->initialized) {
$tags = array_merge($tags, [
'tenant:' . tenant('id'),
]);
diff --git a/src/Middleware/InitializeTenancy.php b/src/Middleware/InitializeTenancy.php
index ff6fe2e4..c8b3bd2f 100644
--- a/src/Middleware/InitializeTenancy.php
+++ b/src/Middleware/InitializeTenancy.php
@@ -36,7 +36,7 @@ class InitializeTenancy
try {
tenancy()->init($request->getHost());
} catch (TenantCouldNotBeIdentifiedException $e) {
- ($this->onFail)($e);
+ return ($this->onFail)($e, $request, $next);
}
}
diff --git a/src/Middleware/InitializeTenancyByRequestData.php b/src/Middleware/InitializeTenancyByRequestData.php
index 7753d034..33e17a09 100644
--- a/src/Middleware/InitializeTenancyByRequestData.php
+++ b/src/Middleware/InitializeTenancyByRequestData.php
@@ -41,7 +41,7 @@ class InitializeTenancyByRequestData
try {
$this->initializeTenancy($request);
} catch (TenantCouldNotBeIdentifiedException $e) {
- ($this->onFail)($e);
+ return ($this->onFail)($e, $request, $next);
}
}
diff --git a/src/Middleware/PreventAccessFromTenantDomains.php b/src/Middleware/PreventAccessFromTenantDomains.php
index 654babd5..83afb31f 100644
--- a/src/Middleware/PreventAccessFromTenantDomains.php
+++ b/src/Middleware/PreventAccessFromTenantDomains.php
@@ -13,6 +13,16 @@ use Illuminate\Support\Facades\Route as Router;
*/
class PreventAccessFromTenantDomains
{
+ /** @var callable */
+ protected $central404;
+
+ public function __construct(callable $central404 = null)
+ {
+ $this->central404 = $central404 ?? function () {
+ return 404;
+ };
+ }
+
/**
* Handle an incoming request.
*
@@ -39,13 +49,13 @@ class PreventAccessFromTenantDomains
}
if ($isExemptDomain && $isTenantRoute) { // accessing tenant routes on web domains
- abort(404);
+ return ($this->central404)($request, $next);
}
return $next($request);
}
- public function routeHasMiddleware(Route $route, $middleware): bool
+ public static function routeHasMiddleware(Route $route, $middleware): bool
{
if (in_array($middleware, $route->middleware(), true)) {
return true;
diff --git a/src/StorageDrivers/Database/CachedTenantResolver.php b/src/StorageDrivers/Database/CachedTenantResolver.php
index 9a4a345c..db4d5a46 100644
--- a/src/StorageDrivers/Database/CachedTenantResolver.php
+++ b/src/StorageDrivers/Database/CachedTenantResolver.php
@@ -28,7 +28,7 @@ class CachedTenantResolver
return $this->config->get('tenancy.storage_drivers.db.cache_ttl');
}
- public function getTenantIdByDomain(string $domain, Closure $query): string
+ public function getTenantIdByDomain(string $domain, Closure $query): ?string
{
return $this->cache->remember('_tenancy_domain_to_id:' . $domain, $this->ttl(), $query);
}
diff --git a/src/Traits/TenantAwareCommand.php b/src/Traits/TenantAwareCommand.php
index e86168df..fb11df9f 100644
--- a/src/Traits/TenantAwareCommand.php
+++ b/src/Traits/TenantAwareCommand.php
@@ -14,14 +14,8 @@ trait TenantAwareCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
$tenants = $this->getTenants();
-
- if (count($tenants) === 1) {
- return $tenants[0]->run(function () {
- return $this->laravel->call([$this, 'handle']);
- });
- }
-
$exitCode = 0;
+
foreach ($tenants as $tenant) {
$result = (int) $tenant->run(function () {
return $this->laravel->call([$this, 'handle']);
diff --git a/test b/test
index 1d492f02..0d5a2556 100755
--- a/test
+++ b/test
@@ -2,6 +2,6 @@
set -e
printf "Variant 1 (DB)\n\n"
-docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
+docker-compose exec -T test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
printf "Variant 2 (Redis)\n\n"
-docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/2.cov "$@"
+docker-compose exec -T test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/2.cov "$@"