Created
October 19, 2019 16:46
-
-
Save drfraker/ff702e76d793abe14fb82dce1cc4b1fa to your computer and use it in GitHub Desktop.
Tenancy Testing Help
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
use Illuminate\Support\Str; | |
return [ | |
/* | |
|-------------------------------------------------------------------------- | |
| Default Database Connection Name | |
|-------------------------------------------------------------------------- | |
| | |
| Here you may specify which of the database connections below you wish | |
| to use as your default connection for all database work. Of course | |
| you may use many connections at once using the Database library. | |
| | |
*/ | |
'default' => env('DB_CONNECTION', 'system'), | |
/* | |
|-------------------------------------------------------------------------- | |
| Database Connections | |
|-------------------------------------------------------------------------- | |
| | |
| Here are each of the database connections setup for your application. | |
| Of course, examples of configuring each database platform that is | |
| supported by Laravel is shown below to make development simple. | |
| | |
| | |
| All database work in Laravel is done through the PHP PDO facilities | |
| so make sure you have the driver for your particular database of | |
| choice installed on your machine before you begin development. | |
| | |
*/ | |
'connections' => [ | |
'system' => [ | |
'driver' => 'mysql', | |
'url' => env('DATABASE_URL'), | |
'host' => env('DB_HOST', '127.0.0.1'), | |
'port' => env('DB_PORT', '3306'), | |
'database' => env('DB_DATABASE', 'forge'), | |
'username' => env('DB_USERNAME', 'forge'), | |
'password' => env('DB_PASSWORD', ''), | |
'unix_socket' => env('DB_SOCKET', ''), | |
'charset' => 'utf8mb4', | |
'collation' => 'utf8mb4_unicode_ci', | |
'prefix' => '', | |
'prefix_indexes' => true, | |
'strict' => true, | |
'engine' => null, | |
'options' => extension_loaded('pdo_mysql') ? array_filter([ | |
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), | |
]) : [], | |
], | |
'tenant' => [ | |
'driver' => 'mysql', | |
'url' => env('DATABASE_URL'), | |
'host' => env('DB_HOST', '127.0.0.1'), | |
'port' => env('DB_PORT', '3306'), | |
'username' => env('DB_USERNAME', 'forge'), | |
'password' => env('DB_PASSWORD', ''), | |
'unix_socket' => env('DB_SOCKET', ''), | |
'charset' => 'utf8mb4', | |
'collation' => 'utf8mb4_unicode_ci', | |
'prefix' => '', | |
'prefix_indexes' => true, | |
'strict' => true, | |
'engine' => null, | |
'options' => extension_loaded('pdo_mysql') ? array_filter([ | |
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), | |
]) : [], | |
], | |
], | |
/* | |
|-------------------------------------------------------------------------- | |
| Migration Repository Table | |
|-------------------------------------------------------------------------- | |
| | |
| This table keeps track of all the migrations that have already run for | |
| your application. Using this information, we can determine which of | |
| the migrations on disk haven't actually been run in the database. | |
| | |
*/ | |
'migrations' => 'migrations', | |
/* | |
|-------------------------------------------------------------------------- | |
| Redis Databases | |
|-------------------------------------------------------------------------- | |
| | |
| Redis is an open source, fast, and advanced key-value store that also | |
| provides a richer body of commands than a typical key-value system | |
| such as APC or Memcached. Laravel makes it easy to dig right in. | |
| | |
*/ | |
'redis' => [ | |
'client' => env('REDIS_CLIENT', 'phpredis'), | |
'options' => [ | |
'cluster' => env('REDIS_CLUSTER', 'redis'), | |
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), | |
], | |
'default' => [ | |
'url' => env('REDIS_URL'), | |
'host' => env('REDIS_HOST', '127.0.0.1'), | |
'password' => env('REDIS_PASSWORD', null), | |
'port' => env('REDIS_PORT', 6379), | |
'database' => env('REDIS_DB', 0), | |
], | |
'cache' => [ | |
'url' => env('REDIS_URL'), | |
'host' => env('REDIS_HOST', '127.0.0.1'), | |
'password' => env('REDIS_PASSWORD', null), | |
'port' => env('REDIS_PORT', 6379), | |
'database' => env('REDIS_CACHE_DB', 1), | |
], | |
], | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers\Auth; | |
use App\Owner; | |
use App\Organization; | |
use App\Billing\PaymentGateway; | |
use Illuminate\Support\Facades\DB; | |
use App\Http\Controllers\Controller; | |
use Illuminate\Support\Facades\Hash; | |
use App\Http\Requests\RegistrationRequest; | |
class RegisterController extends Controller | |
{ | |
public function __construct() | |
{ | |
$this->middleware('guest'); | |
} | |
public function index() | |
{ | |
return view('auth.register'); | |
} | |
public function store(RegistrationRequest $request) | |
{ | |
tenancy()->create([$request->subdomain]); | |
tenancy()->init($request->subdomain); | |
// Add info to the tenant's private database. | |
$this->createNewAccount($request); | |
return redirect(route('dashboard')); | |
} | |
protected function createNewAccount($request) | |
{ | |
return DB::transaction(function () use ($request) { | |
$subscription = $this->createOnStripe($request); | |
$organization = Organization::create([ | |
'name' => $request->business_name, | |
'email' => $request->business_email, | |
'phone' => $request->business_phone, | |
'subdomain' => $request->subdomain, | |
'stripe_id' => $subscription->customer, | |
]); | |
$organization->subscription()->create([ | |
'name' => 'default', | |
'stripe_id' => $subscription->id, | |
'stripe_status' => $subscription->status, | |
'stripe_plan' => $subscription->plan->id, | |
'quantity' => $subscription->quantity, | |
'trial_ends_at' => $subscription->trial_end, | |
'ends_at' => $subscription->current_period_end, | |
]); | |
Owner::create([ | |
'organization_id' => $organization->id, | |
'first_name' => $request->first_name, | |
'last_name' => $request->last_name, | |
'email' => $request->admin_email, | |
'password' => Hash::make($request->password), | |
]); | |
return $organization; | |
}); | |
} | |
protected function createOnStripe($request) | |
{ | |
$paymentGateway = app(PaymentGateway::class); | |
$stripeCustomer = $paymentGateway->createCustomer($request->payment_token, ['email' => $request->business_email]); | |
$subscription = $paymentGateway->createSubscription($stripeCustomer->id, 'main_plan'); | |
return $subscription; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tests\Feature; | |
use App\Owner; | |
use Tests\TestCase; | |
use App\Organization; | |
use App\Billing\PaymentGateway; | |
use App\Billing\FakePaymentGateway; | |
use Illuminate\Support\Facades\Bus; | |
use Illuminate\Support\Facades\Hash; | |
use Illuminate\Foundation\Testing\RefreshDatabase; | |
class RegisterOrganizationTest extends TestCase | |
{ | |
use RefreshDatabase; | |
/** | |
* @var FakePaymentGateway | |
*/ | |
protected $paymentGateway; | |
public function setUp(): void | |
{ | |
parent::setUp(); | |
Bus::fake(); | |
$this->paymentGateway = new FakePaymentGateway(); | |
$this->app->instance(PaymentGateway::class, $this->paymentGateway); | |
} | |
/** @test */ | |
public function it_creates_an_organization_and_an_owner_when_registration_is_successful() | |
{ | |
$response = $this->post(route('register'), $this->getValidRegisterFormData()); | |
$response->assertRedirect(route('dashboard')); | |
$this->assertDatabaseHas('organizations', [ | |
'name' => 'biz name', | |
'email' => '[email protected]', | |
'phone' => '444-444-4444', | |
]); | |
$this->assertDatabaseHas('owners', [ | |
'first_name' => 'john', | |
'last_name' => 'doe', | |
'email' => '[email protected]', | |
]); | |
$this->assertTrue(Hash::check('secret', Owner::first()->password)); | |
} | |
/** @test */ | |
public function after_successful_registration_owner_belongs_to_organization() | |
{ | |
$this->withoutExceptionHandling(); | |
$this->post(route('register'), $this->getValidRegisterFormData()); | |
$organization = Organization::with('owner')->where('email', '[email protected]')->first(); | |
$this->assertEquals("[email protected]", $organization->owner->email); | |
} | |
private function getValidRegisterFormData($overrides = []) | |
{ | |
return array_merge([ | |
'business_name' => 'biz name', | |
'business_email' => '[email protected]', | |
'business_phone' => '444-444-4444', | |
'subdomain' => 'acme', | |
'first_name' => 'john', | |
'last_name' => 'doe', | |
'admin_email' => '[email protected]', | |
'password' => 'secret', | |
'payment_token' => 'tok_visa', | |
], $overrides); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
return [ | |
'storage_driver' => 'db', | |
'storage_drivers' => [ | |
'db' => [ | |
'driver' => Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver::class, | |
'data_column' => 'data', | |
'custom_columns' => [ | |
// 'plan', | |
], | |
'connection' => null, | |
'table_names' => [ | |
'TenantModel' => 'tenants', | |
'DomainModel' => 'domains', | |
], | |
], | |
'redis' => [ | |
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class, | |
'connection' => 'tenancy', | |
], | |
], | |
'tenant_route_namespace' => 'App\Http\Controllers', | |
'exempt_domains' => [ // e.g. domains which host landing pages, sign up pages, etc | |
'qn2020.test', | |
'localhost', | |
], | |
'database' => [ | |
'based_on' => 'tenant', // The connection that will be used as a base for the dynamically created tenant 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', | |
// 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, | |
], | |
'database_manager_connections' => [ | |
// Connections used by TenantDatabaseManagers. This tells, for example, the | |
// MySQLDatabaseManager to use the mysql connection to create databases. | |
//'sqlite' => 'sqlite', | |
'mysql' => 'system', | |
//'pgsql' => 'pgsql', | |
], | |
'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\TenantConfig::class, | |
// Stancl\Tenancy\Features\TelescopeTags::class, | |
// Stancl\Tenancy\Features\TenantRedirect::class, | |
], | |
'storage_to_config_map' => [ // Used by the TenantConfig feature | |
// 'paypal_api_key' => 'services.paypal.api_key', | |
], | |
'home_url' => '/dashboard', | |
'queue_database_creation' => false, | |
'migrate_after_creation' => true, // run migrations after creating a tenant | |
'seed_after_migration' => false, // should the seeder run after automatic migration | |
'seeder_parameters' => [ | |
'--class' => 'DatabaseSeeder', // root seeder class to run after automatic migrations, eg: 'DatabaseSeeder' | |
], | |
'queue_database_deletion' => false, | |
'delete_database_after_tenant_deletion' => false, // delete the tenant's database after deleting the tenant | |
'unique_id_generator' => Stancl\Tenancy\UniqueIDGenerators\UUIDGenerator::class, | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tests; | |
use Stancl\Tenancy\Tenant; | |
use Illuminate\Support\Facades\DB; | |
use Illuminate\Foundation\Testing\TestCase as BaseTestCase; | |
abstract class TestCase extends BaseTestCase | |
{ | |
use CreatesApplication; | |
public function setUp(): void | |
{ | |
parent::setUp(); | |
$this->beforeApplicationDestroyed(function () { | |
// Delete all tenant databases; | |
tenancy()->all()->each(function (Tenant $t) { | |
$dbName = $t->getDatabaseName(); | |
DB::connection('tenant')->statement("DROP DATABASE IF EXISTS `$dbName`"); | |
}); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment