From 796ec5d99b286b47913489b3d0b6c5d6b786e266 Mon Sep 17 00:00:00 2001 From: Sniper7Kills Date: Tue, 8 Sep 2020 09:40:57 -0400 Subject: [PATCH 1/3] remove third party package name --- docs/tenancy/1.x/faq-common-problems.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tenancy/1.x/faq-common-problems.md b/docs/tenancy/1.x/faq-common-problems.md index 5e148545..f95b2c71 100644 --- a/docs/tenancy/1.x/faq-common-problems.md +++ b/docs/tenancy/1.x/faq-common-problems.md @@ -101,7 +101,7 @@ I need to do some stuff directly after my affects are finished, but before the r *Example* -You might be using [PHP API Wrapper](https://github.com/CristalTeam/php-api-wrapper) in your application, and you need to configure it based on your tenant; AFTER your [affects-config](affects-configs) is finished. +You might be using a third party package in your application, and you need to configure it based on your tenant; AFTER your [affects-config](affects-configs) is finished. **Solution** @@ -127,7 +127,7 @@ class EventServiceProvider extends ServiceProvider protected $listen = [ \Tenancy\Affects\Configs\Events\ConfigureConfig::class => [ ConfigureTenantConfigs::class, - ConfigureApiWrapper::class, + ConfigurePackage::class, ], ]; } From 7a2f1bc228518dd081b8dd426be79c29cd4aa632 Mon Sep 17 00:00:00 2001 From: Sniper7Kills Date: Wed, 14 Oct 2020 13:03:07 -0400 Subject: [PATCH 2/3] WIP Initial feedback request --- docs.yaml | 1 + docs/tenancy/1.x/tutorial-non-tenant.md | 135 ++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 docs/tenancy/1.x/tutorial-non-tenant.md diff --git a/docs.yaml b/docs.yaml index e47c24c5..035e8e9e 100644 --- a/docs.yaml +++ b/docs.yaml @@ -56,6 +56,7 @@ packages: tutorials: - tutorial-basic-setup - tutorial-multi-database + - tutorial-non-tenant - name: Hyn description: > Run multiple websites using the same Laravel installation while diff --git a/docs/tenancy/1.x/tutorial-non-tenant.md b/docs/tenancy/1.x/tutorial-non-tenant.md new file mode 100644 index 00000000..246b2dc2 --- /dev/null +++ b/docs/tenancy/1.x/tutorial-non-tenant.md @@ -0,0 +1,135 @@ +--- +title: Non-Tenant Site Setup +icon: fal fa-gears +excerpt: > + How to setup goabal and admin sites in the same application +tags: + - tenancy + - tutorial + - non-tenant +--- + +## Introduction +When building a SaaS there tends to be a need for a "sub-site" such as an admin panel, or a public facing website describing your SaaS. + +This tutorial will go though the basics of setting these "sub-sites" within the same codebase without the need for creating a tenant do display these sites. + +## Requirements +This tutorial will assume that you have followed the [basic setup tutorial](tutorial-basic-setup). + +## Routes +The first thing we want to do is configure the [Routes Affect](affacts-routes). + +First start by creating a new file for all tenant specific routes. + +### Tenant Route File +For this example it will be stored at `routes\tenant.php` +```php +use Illuminate\Support\Facades\Route; + +/* +|-------------------------------------------------------------------------- +| Tenant Web Routes +|-------------------------------------------------------------------------- +| +| Here is where you can register web routes for your tenant application. +| They are not loaded by default, and will be loaded thought the Routes Affect. +| +*/ + +Route::get('/', function () { + //This is the "root" route for all tenants +}); + +``` + +### Routes Affect Listener +Next we need to create the listener for the [Routes Affect]() that will get rid of the system (non-tenant) routes and load our tenant route file instead. + +`app\Listeners\ConfigureTenantRoutes.php` +```php +namespace App\Listeners; + +use Tenancy\Affects\Routes\Events\ConfigureRoutes; + +class TenantRoutes +{ + public function handle(ConfigureRoutes $event) + { + if($event->event->tenant) + { + $event + ->flush() + ->fromFile( + ['middleware' => ['web']], + base_path('/routes/tenant.php') + ); + } + } +} +``` + +#### Registering the listener +Now update your `app\Providers\EventServiceProvider.php` to include your new listener. + +```php + protected $listen = [ + + ... + + Tenancy\Affects\Routes\Events\ConfigureRoutes::class => [ + App\Listeners\ConfigureTenantRoutes::class, + ] + + ... +``` + +## Status + +At this point you application is configured to load any tenant routes when a tenant is identified. When a tenant is not identified, the routes located in `routes/web.php` will be used instead. + +## Tips and Reminders +### Namespaceing +As with any Laravel application; if you are storing controllers in a different location than in `app\Http\Controllers` you will need to use the namespace option within your routes. + +`routes\tenant.php` +```php +Route::namespace('Tenant')->group(function () { + // Controllers Within The "App\Http\Controllers\Tenant" Namespace +}); +``` + +### Sub-site by domains +When using subdomains to identify your non-tenant sub-sites you will need to use the route option within your routes file. +`routes\web.php` +```php +Route::domain('admin.myapp.com')->group(function () { + Route::get('/', function () { + // This is the root route for the admin site + }); +}); + +Route::domain('www.myapp.com')->group(function () { + Route::get('/', function () { + // This is the root route for the public site + }); +}); + +Route::get('/shared', function() { + //This is a route that is shared with both the admin site and the public site +}) +``` + + +## Considerations to take into account +1. **Storing users in tenant databases** +When the User Model has the `OnTenant` trait applied to it, you will not be able to have those users login to any "non-tenant" routes. Because there is no tenant identified, it will not be possible to authenticate the user. +To overcome this you will need to either create two different User models (Admin & User), or will need to store all users in the system database. The option you choose will depend on youre goals and objectives. + +2. **Models using the `OnTenant` trait** +Much like the previous consideration, you will not be able to access any models stored within a tenant database without first identifying the tenant. +(Tutorial at a future date) + +3. **Subdomain identification** +When using subdomain identification, it is best practice to preform an additional check when registering a new tenant that they do not try to use a subdomain you have configured for a "non-tenant site". +I.E. Do not allow users to register the subdomains (`www`, `admin`, etc...) \ No newline at end of file From 131913ab398cf1bd0b83322145615d5a4470b749 Mon Sep 17 00:00:00 2001 From: Sniper7Kills Date: Wed, 14 Oct 2020 13:15:29 -0400 Subject: [PATCH 3/3] wip --- docs/tenancy/1.x/tutorial-non-tenant.md | 36 +++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/tenancy/1.x/tutorial-non-tenant.md b/docs/tenancy/1.x/tutorial-non-tenant.md index 246b2dc2..d85f369d 100644 --- a/docs/tenancy/1.x/tutorial-non-tenant.md +++ b/docs/tenancy/1.x/tutorial-non-tenant.md @@ -82,6 +82,7 @@ Now update your `app\Providers\EventServiceProvider.php` to include your new lis ] ... + ]; ``` ## Status @@ -120,16 +121,41 @@ Route::get('/shared', function() { }) ``` +### Accessing `OnTenant` models from a "sub-site" +Before a model that uses the `OnTenant` trait can be used a tenant needs to be identified. Because of this it is best practice to avoid using tenant models on a non-tenant site. + +In events where this access is still needed, a tenant will first need to be identified within your code. + +The following example assumes you are trying to create an admin view that lists all products a specific tenant has in their store. + +`app\Http\Controllers\TenantProducts.php` +```php +public function index(Request $request) +{ + // We currently do not have a tenant identified as this is the "admin sub-site" + + // Get our tenant (A Customer) + $customer = App\Models\Customer::firstOrFail($request->get('tenant_id')); + // Tell tenancy, that we want to access the above tenant + Tenancy::setTenant($customer) + + // We are still in the "admin sub-site", but we now also have an active tenant + // And we can access the information stored in their database. + + // We will assume that Products uses the `OnTenant` Trait + $tenant_products = Products::all(); + + // Return the products that belong to the tenant still within our "admin sub-site" + return $tenant_products + +} +``` ## Considerations to take into account 1. **Storing users in tenant databases** When the User Model has the `OnTenant` trait applied to it, you will not be able to have those users login to any "non-tenant" routes. Because there is no tenant identified, it will not be possible to authenticate the user. To overcome this you will need to either create two different User models (Admin & User), or will need to store all users in the system database. The option you choose will depend on youre goals and objectives. -2. **Models using the `OnTenant` trait** -Much like the previous consideration, you will not be able to access any models stored within a tenant database without first identifying the tenant. -(Tutorial at a future date) - -3. **Subdomain identification** +2. **Subdomain identification** When using subdomain identification, it is best practice to preform an additional check when registering a new tenant that they do not try to use a subdomain you have configured for a "non-tenant site". I.E. Do not allow users to register the subdomains (`www`, `admin`, etc...) \ No newline at end of file