Merge branch 'master' of github.com:stancl/tenancy-docs

This commit is contained in:
Samuel Štancl 2019-10-30 18:01:05 +01:00
commit 5141c7da43
12 changed files with 123 additions and 26 deletions

View file

@ -1,7 +1,7 @@
<?php <?php
return [ return [
'baseUrl' => 'http://jigsaw-docs-staging.tighten.co', 'baseUrl' => env('DEPLOY_URL') . '/docs',
'production' => false, 'production' => false,
// DocSearch credentials // DocSearch credentials

View file

@ -96,6 +96,7 @@ return [
'children' => [ 'children' => [
'Spatie Packages' => 'spatie', 'Spatie Packages' => 'spatie',
'Horizon' => 'horizon', 'Horizon' => 'horizon',
'Nova' => 'nova',
'Telescope' => 'telescope', 'Telescope' => 'telescope',
], ],
], ],

View file

@ -25,7 +25,7 @@ protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
$this->call('migrate'); $this->artisan('migrate');
tenancy()->create('test.localhost'); tenancy()->create('test.localhost');
tenancy()->init('test.localhost'); tenancy()->init('test.localhost');

View file

@ -45,4 +45,4 @@ Use the `global_asset()` helper.
### Central queues {#central-queues} ### Central queues {#central-queues}
Coming soon. Create a new queue connection with the `central` key set to `true`.

View file

@ -98,28 +98,34 @@ Features are similar to bootstrappers, but they are executed regardless of wheth
When a user tries to visit a non-tenant route on a tenant domain, the `PreventAccessFromTenantDomains` middleware will return a redirect to this url. When a user tries to visit a non-tenant route on a tenant domain, the `PreventAccessFromTenantDomains` middleware will return a redirect to this url.
### `queue_database_creation` {#queue-database-creation}
- Default: `false`
### `migrate_after_creation` {#migrate-after-creation} ### `migrate_after_creation` {#migrate-after-creation}
Run migrations after creating a tenant. Run migrations after creating a tenant.
- Default: `false` - Default: `false`
### `queue_automatic_migration` {#queue-automatic-migration} ### `seed_after_migration` {#seed-after-migration}
Whether automatic tenant migrations (if enabled) should be queued. Run seeds after creating a tenant.
- Default: `false` - Default: `false`
### `seeder_parameters` {#seeder_parameters}
Parameters passed to the `tenants:seed` command.
- Default: `['--class' => 'DatabaseSeeder']`
### `delete_database_after_tenant_deletion` {#delete-database-after-tenant-deletion} ### `delete_database_after_tenant_deletion` {#delete-database-after-tenant-deletion}
Delete the tenant's database after deleting the tenant. Delete the tenant's database after deleting the tenant.
- Default: `false` - Default: `false`
### `queue_database_creation` {#queue-database-creation}
- Default: `false`
### `queue_database_deletion` {#queue-database-deletion} ### `queue_database_deletion` {#queue-database-deletion}
- Default: `false` - Default: `false`

View file

@ -43,7 +43,7 @@ Laravel's `asset()` helper has two different paths of execution:
> Note: In 1.x, the `asset()` helper was not tenant-aware, and there was a `tenant_asset()` helper that followed the second option in the list above (a link to a controller). For backwards compatibility, that helper remains intact. > Note: In 1.x, the `asset()` helper was not tenant-aware, and there was a `tenant_asset()` helper that followed the second option in the list above (a link to a controller). For backwards compatibility, that helper remains intact.
> If you have some non-tenant-specific assets, you may use the pakage's `global_asset()` helper. > If you have some non-tenant-specific assets, you may use the package's `global_asset()` helper.
Note that all tenant assets have to be in the `app/public/` subdirectory of the tenant's storage directory, as shown in the image above. Note that all tenant assets have to be in the `app/public/` subdirectory of the tenant's storage directory, as shown in the image above.

View file

@ -15,7 +15,7 @@ A customer has signed up on your website, you have created a new tenant and now
```php ```php
// tenant sign up controller // tenant sign up controller
return redirect()->route('dashboard')->tenant($tenant['domain']); return redirect()->route('dashboard')->tenant($domain);
``` ```
## Custom ID scheme ## Custom ID scheme
@ -23,13 +23,13 @@ return redirect()->route('dashboard')->tenant($tenant['domain']);
If you don't want to use UUIDs and want to use something more human-readable (even domain concatenated with uuid, for example), you can create a custom class for this: If you don't want to use UUIDs and want to use something more human-readable (even domain concatenated with uuid, for example), you can create a custom class for this:
```php ```php
use Stancl\Tenancy\Interfaces\UniqueIdentifierGenerator; use Stancl\Tenancy\Contracts\UniqueIdentifierGenerator;
class MyUniqueIDGenerator implements UniqueIdentifierGenerator class MyUniqueIDGenerator implements UniqueIdentifierGenerator
{ {
public static function handle(string $domain, array $data): string public static function handle(string $domain, array $data): string
{ {
return $domain . \Webpatser\Uuid\Uuid::generate(1, $domain); return $domain . \Ramsey\Uuid\Uuid::uuid4()->toString();
} }
} }
``` ```

View file

@ -0,0 +1,36 @@
---
title: Nova Integration
description: Nova Integration
extends: _layouts.documentation
section: content
---
# Nova Integration {#nova-integration}
To make Nova part of your tenant application, do the following:
- Publish the Nova migrations and move them to the `database/migrations/tenant` directory.
```none
php artisan vendor:publish --tag=nova-migrations
```
> Note: Unfortunately, Nova will still be adding its migrations to your central migrations. This is something we'd like to solve in the future.
- Add the `'tenancy'` middleware group to your `nova.middleware` config. Example:
```php
'middleware' => [
'tenancy',
'web',
Authenticate::class,
DispatchServingNovaEvent::class,
BootTools::class,
Authorize::class,
],
```
- In your `NovaServiceProvider`'s `routes()` method, replace the following lines:
```php
->withAuthenticationRoutes()
->withPasswordResetRoutes()
```
with these lines:
```php
->withAuthenticationRoutes(['web', 'tenancy'])
->withPasswordResetRoutes(['web', 'tenancy'])
```

View file

@ -1,37 +1,49 @@
--- ---
title: Tenant Routes title: Tenant Routes
description: Tenant routes.. description: Tenant Routes
extends: _layouts.documentation extends: _layouts.documentation
section: content section: content
--- ---
# Tenant Routes {#tenant-routes} # Tenant Routes {#tenant-routes}
Routes within `routes/tenant.php` will have the `web` middleware group and the `IntializeTenancy` middleware automatically applied on them. Routes within `routes/tenant.php` will have the `web` and `tenancy` middleware groups automatically applied on them.
The `IntializeTenancy` middleware attempts to identify the tenant based on the current hostname. Once the tenant is identified, the database connection, cache, filesystem root paths and, optionally, Redis connection, will be switched.
Just like `routes/web.php`, these routes use the `App\Http\Controllers` namespace (you can [configure this]({{ $page->link('configuration#tenant-route-namespace') }})) Just like `routes/web.php`, these routes use the `App\Http\Controllers` namespace (you can [configure this]({{ $page->link('configuration#tenant-route-namespace') }}))
> If a tenant cannot be identified, an exception will be thrown. If you want to change this behavior (to a redirect, for example) read the [Middleware Configuration]({{ $page->link('middleware-configuration') }}) page. > If a tenant cannot be identified, an exception will be thrown. If you want to change this behavior (to a redirect, for example) read the [Middleware Configuration]({{ $page->link('middleware-configuration') }}) page.
## Exempt routes {#exempt-routes} ## Middleware {#middleware}
Routes outside the `routes/tenant.php` file will not have the tenancy middleware automatically applied on them. You can apply this middleware manually, though. The package automatically adds the `InitializeTenancy` middleware to the global middleware stack. This middleware checks if the current domain is not part of `tenancy.exempt_domains`. If not, it attempts to identify the tenant based on the current hostname. Once the tenant is identified, the database connection, cache, filesystem root paths and, optionally, Redis connection, will be switched.
If you want certain routes (perhaps API routes) to be multi-tenant, wrap them in a Route group with this middleware: After the *global* middleware is executed, the controllers are constructed.
After that, the *route* middleware is executed.
All route groups in your application should have the `\Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware applied on them, to prevent access from tenant domains to central routes and vice versa. See below for more detail about the `PreventAccessFromTenantDomains` middleware.
All tenant routes in your application should have the `tenancy` middleware group applied on them.
The `tenancy` middleware group marks the route as a tenant route. That middleware functions as a "flag" for the `PreventAccessFromTenantDomains`, telling it that the route is a tenant route, since the middleware has no other way of distingushing central from tenant routes.
In previous versions, the `InitializeTenancy` middleware was applied only on tenant routes. However, that lead to tenancy not being initialized in controller constructors, which could cause bugs. So from 2.1.0 on, tenancy is initialized on all routes on non-exempt domains, and if the route is not tenant, the request gets aborted by the `PreventAccessFromTenantDomains` once Laravel reaches the route middleware step.
## Central routes {#central-routes}
Routes in files other than `routes/tenant.php` will not have the `tenancy` middleware automatically applied on them, so they will be central routes. If you want these routes to be tenant routes, you can apply the `tenancy` middleware manually, as described in custom route groups below.
## API routes / custom route groups {#custom-groups} ## API routes / custom route groups {#custom-groups}
```php If you want certain routes (perhaps API routes) to be multi-tenant, wrap them in a Route group with this middleware:
use Stancl\Tenancy\Middleware\InitializeTenancy;
Route::middleware(InitializeTenancy::class)->group(function () { ```php
Route::middleware('tenancy')->group(function () {
// Route::get('/', 'HelloWorld'); // Route::get('/', 'HelloWorld');
}); });
``` ```
and apply the `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware on the *entire* group: and make sure the `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware is applied on the *entire* group:
```php ```php
// app/Http/Kernel.php // app/Http/Kernel.php
@ -52,4 +64,4 @@ Suggestion: Since you probably want cleaner URLs on your non-tenant part of the
The `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware prevents access to non-tenant routes from tenant domains by returning a redirect to the tenant app's home page ([`tenancy.home_url`]({{ $page->link('configuration#home-url') }})). Conversely, it returns a 404 when a user attempts to visit a tenant route on a web (exempt) domain. The `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware prevents access to non-tenant routes from tenant domains by returning a redirect to the tenant app's home page ([`tenancy.home_url`]({{ $page->link('configuration#home-url') }})). Conversely, it returns a 404 when a user attempts to visit a tenant route on a web (exempt) domain.
The `tenancy:install` command applies this middleware to the `web` group. If you want apply it for another route group, add this middleware manually to that group. You can do this in `app/Http/Kernel.php`. The `tenancy:install` command applies this middleware to the `web` and `api` groups. To apply it for another route group, add this middleware manually to that group. You can do this in `app/Http/Kernel.php`.

View file

@ -36,6 +36,35 @@ $tenant->removeDomains('foo.yourapp.com')->save();
> Don't forget to `->save()` after modifying the domains! > Don't forget to `->save()` after modifying the domains!
## `run()` {#run}
The `$tenant->run()` command lets you execute a closure inside a tenant's "environment".
```php
$tenant->run(function ($tenant) {
User::create(['name' => 'Admin', 'email' => 'admin@yourapp.com', ...]);
});
```
It also lets you get data from the tenant's environment:
```php
$tenantsUserCount = $tenant->run(function ($tenant) {
return User::count();
});
```
If you need access to the tenant within the closure, it's passed as the first argument.
This feature is a safe alternative to:
```php
tenancy()->initialize($tenant);
// make some changes
tenancy()->end();
```
and it also checks if tenancy was initialized. If it was, it returns to the original tenant after running the closure.
## `$persisted` {#persisted} ## `$persisted` {#persisted}
This property says whether the model has saved to the storage yet. In other words, if it's `false`, it's a new instance that has not been `->save()`d yet. This property says whether the model has saved to the storage yet. In other words, if it's `false`, it's a new instance that has not been `->save()`d yet.

View file

@ -3,6 +3,9 @@ environment = { PHP_VERSION = "7.2" }
publish = "/dist" publish = "/dist"
command = "./build.sh" command = "./build.sh"
[context.deploy-preview]
command = "./staging.sh"
[[redirects]] [[redirects]]
from = "/docs/master/*" from = "/docs/master/*"
to = "/docs/v2/:splat" to = "/docs/v2/:splat"

10
staging.sh Executable file
View file

@ -0,0 +1,10 @@
set -e
npm install
npm run production
cd docs
composer install
npm install
npm run staging
mkdir -p ../dist/docs
cp -R build_staging/* ../dist/docs