diff --git a/README.md b/README.md index a97f16c8..79a6dca4 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ You won't have to change a thing in your application's code.\* * [Artisan commands](#artisan-commands) - [`tenants:list`](#-tenants-list-) - [`tenants:migrate`, `tenants:rollback`, `tenants:seed`](#-tenants-migrate----tenants-rollback----tenants-seed-) + - [Running your commands for tenants](#running-your-commands-for-tenants) + [Tenant migrations](#tenant-migrations) * [Testing](#testing) - [Tips](#tips) @@ -487,6 +488,7 @@ Available commands for the "tenants" namespace: tenants:list List tenants. tenants:migrate Run migrations for tenant(s) tenants:rollback Rollback migrations for tenant(s). + tenants:run Run a command for tenant(s). tenants:seed Seed tenant database(s). ``` @@ -509,6 +511,18 @@ Tenant: 8075a580-1cb8-11e9-8822-49c5d8f8ff23 (laravel.localhost) Database seeding completed successfully. ``` +### Running your commands for tenants + +You can use the `tenants:run` command to run your own commands for tenants. + +If your command's signature were `email:send {user} {--queue} {--subject} {body}`, you would run this command like this: + +``` +$ 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. ..." +``` + +The `=` separates the argument/option name from its value, but you can still use `=` in the argument's value. + ### Tenant migrations Tenant migrations are located in `database/migrations/tenant`, so you should move your tenant migrations there. diff --git a/src/Commands/Run.php b/src/Commands/Run.php new file mode 100644 index 00000000..d3e00861 --- /dev/null +++ b/src/Commands/Run.php @@ -0,0 +1,66 @@ +initialized) { + $previous_tenants_domain = tenant('domain'); + } + + tenant()->all($this->option('tenants'))->each(function ($tenant) { + $this->line("Tenant: {$tenant['uuid']} ({$tenant['domain']})"); + tenancy()->init($tenant['domain']); + + $callback = function ($prefix = '') { + return function ($arguments, $argument) use ($prefix) { + [$key, $value] = explode('=', $argument, 2); + $arguments[$prefix . $key] = $value; + + return $arguments; + }; + }; + + // Turns ['foo=bar', 'abc=xyz=zzz'] into ['foo' => 'bar', 'abc' => 'xyz=zzz'] + $arguments = array_reduce($this->option('argument'), $callback(), []); + + // Turns ['foo=bar', 'abc=xyz=zzz'] into ['--foo' => 'bar', '--abc' => 'xyz=zzz'] + $options = array_reduce($this->option('option'), $callback('--'), []); + + // Run command + $this->call($this->argument('commandname'), array_merge($arguments, $options)); + + tenancy()->end(); + }); + + if ($tenancy_was_initialized) { + tenancy()->init($previous_tenants_domain); + } + } +} diff --git a/src/TenancyServiceProvider.php b/src/TenancyServiceProvider.php index 12005e9f..233b87bb 100644 --- a/src/TenancyServiceProvider.php +++ b/src/TenancyServiceProvider.php @@ -2,6 +2,7 @@ namespace Stancl\Tenancy; +use Stancl\Tenancy\Commands\Run; use Stancl\Tenancy\Commands\Seed; use Illuminate\Cache\CacheManager; use Stancl\Tenancy\Commands\Migrate; @@ -23,9 +24,10 @@ class TenancyServiceProvider extends ServiceProvider { if ($this->app->runningInConsole()) { $this->commands([ + Run::class, + Seed::class, Migrate::class, Rollback::class, - Seed::class, TenantList::class, ]); } diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index 3998719d..d26b71c1 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -76,7 +76,7 @@ class CommandsTest extends TestCase } /** @test */ - public function database_connection_is_switched_to_default_after_migrating_or_seeding_or_rolling_back() + public function database_connection_is_switched_to_default() { $originalDBName = DB::connection()->getDatabaseName(); @@ -88,13 +88,29 @@ class CommandsTest extends TestCase Artisan::call('tenants:rollback'); $this->assertSame($originalDBName, DB::connection()->getDatabaseName()); + + $this->run_commands_works(); + $this->assertSame($originalDBName, DB::connection()->getDatabaseName()); } /** @test */ - public function database_connection_is_switched_to_default_after_migrating_or_seeding_or_rolling_back_when_tenancy_has_been_initialized() + public function database_connection_is_switched_to_default_when_tenancy_has_been_initialized() { tenancy()->init('localhost'); - $this->database_connection_is_switched_to_default_after_migrating_or_seeding_or_rolling_back(); + $this->database_connection_is_switched_to_default(); + } + + /** @test */ + public function run_commands_works() + { + $uuid = tenant()->create('run.localhost')['uuid']; + + Artisan::call('tenants:migrate', ['--tenants' => $uuid]); + + $this->artisan("tenants:run foo --tenants=$uuid --argument='a=foo' --option='b=bar' --option='c=xyz'") + ->expectsOutput("User's name is Test command") + ->expectsOutput('foo') + ->expectsOutput('xyz'); } } diff --git a/tests/Etc/ConsoleKernel.php b/tests/Etc/ConsoleKernel.php new file mode 100644 index 00000000..a0d82a84 --- /dev/null +++ b/tests/Etc/ConsoleKernel.php @@ -0,0 +1,17 @@ + 999, + 'name' => 'Test command', + 'email' => 'test@command.com', + 'password' => bcrypt('password'), + ]); + + $this->line("User's name is " . User::find(999)->name); + $this->line($this->argument('a')); + $this->line($this->option('c')); + } +} + +class User extends \Illuminate\Database\Eloquent\Model +{ + protected $guarded = []; +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 7cc55500..3bc0889f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -130,6 +130,17 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase $app->singleton('Illuminate\Contracts\Http\Kernel', Etc\HttpKernel::class); } + /** + * Resolve application Console Kernel implementation. + * + * @param \Illuminate\Foundation\Application $app + * @return void + */ + protected function resolveApplicationConsoleKernel($app) + { + $app->singleton('Illuminate\Contracts\Console\Kernel', Etc\ConsoleKernel::class); + } + public function randomString(int $length = 10) { return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);