Improved Documentation Search (#61)

* chore: (Improve documentation search for V3)

* fix: checks for mispelled words

* fix: mispelled words

* fix spaces and syntax

* Update multi-database-tenancy.blade.md

Co-authored-by: Samuel Stancl <samuel.stancl@gmail.com>
This commit is contained in:
Adebayo Ilerioluwa 2020-07-21 19:18:29 +01:00 committed by GitHub
parent 0549607899
commit a4645b3abd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 158 additions and 158 deletions

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Automatic mode
# Automatic mode {#automatic-mode}
By default, the package bootstraps tenancy automatically in the background. This means that when a tenant is identified (usually using middleware), the default database/cache/filesystem/etc is switched to that tenant's context. You can read more about this on the [Tenancy bootstrappers]({{ $page->link('tenancy-bootstrappers') }}) page.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Cached lookup
# Cached lookup {#cached-lookup}
If you're using multiple databases, you may want to avoid making a query to the central database on **each tenant request** — for tenant identification. Even though the queries are very simple, the app has to establish a connection with the central database which is expensive.

View file

@ -4,11 +4,11 @@ extends: _layouts.documentation
section: content
---
# Configuration
# Configuration {#configuration}
The package is highly configurable. This page covers what you can configure in the `config/tenancy.php` file, but note that many more things are configurable. Some things can be changed by extending classes (e.g. the `Tenant` model), and **many** things can be changed using static properties. These things will *usually* be mentioned on the respective pages of the documentation, but not every time. For this reason, don't be afraid to dive into the package's source code — whenever the class you're using has a `public static` property, **it's intended to be configured**.
## Static properties
## Static properties {#static-properties}
You can set static properties like this (example):
@ -20,13 +20,13 @@ You can set static properties like this (example):
A good place to put these calls is your `app/Providers/TenancyServiceProvider`'s `boot()` method.
### Tenant model
### Tenant model {#tenant-model}
`tenancy.tenant_model`
This config specifies what `Tenant` model should be used by the package. There's a high chance you're using a custom model, as instructed to by the [Tenants]({{ $page->link('tenants') }}) page, so be sure to change it in the config.
### Unique ID generator
### Unique ID generator {#unique-id-generator}
`tenancy.id_generator`
@ -37,13 +37,13 @@ If you wish to use autoincrement ids instead of uuids:
1. set this config key to null, or create a custom tenant model that doesn't use this trait
2. update the `tenants` table migration to use an incrementing column type instead of `string`
### Domain model
### Domain model {#domain-model}
`tenancy.domain_model`
Similar to the Tenant model config. If you're using a custom model for domains, change it in this config. If you're not using domains (e.g. if you're using path or request data identification) at all, ignore this config key altogether.
### Central domains
### Central domains {#central-domains}
`tenancy.central_domains`
@ -52,13 +52,13 @@ The list of domains that host your [central app]({{ $page->link('the-two-applica
- the `PreventAccessFromCentralDomains` middleware, to prevent access from central domains to tenant routes,
- the `InitializeTenancyBySubdomain` middleware, to check whether the current hostname is a subdomain on one of your central domains.
### Bootstrappers
### Bootstrappers {#bootstrappers}
`tenancy.bootstrappers`
This config array lets you enable, disable or add your own [tenancy bootstrappers]({{ $page->link('tenancy-bootstrappers') }}).
### Database
### Database {#database}
`tenancy.database.*`
@ -66,7 +66,7 @@ This section is relevant to the multi-database tenancy, specifically, to the `Da
See this section in the config, it's documented with comments.
### Cache
### Cache {#cache}
`tenancy.cache.*`
@ -76,7 +76,7 @@ Note: To use the cache separation, you need to use a cache store that supports t
See this section in the config, it's documented with comments.
### Filesystem
### Filesystem {#filesystem}
`tenancy.filesystem.*`
@ -84,7 +84,7 @@ This section is relevant to cache separation, specifically, to the `FilesystemTe
See this section in the config, it's documented with comments.
### Redis
### Redis {#redis}
`tenancy.redis.*`
@ -94,19 +94,19 @@ Note: To use the this bootstrapper, you need phpredis.
See this section in the config, it's documented with comments.
### Features
### Features {#features}
`tenancy.features`
This config array lets you enable, disable or add your own [feature classes]({{ $page->link('optional-features') }}).
### Migration parameters
### Migration parameters {#migration-parameters}
`tenancy.migration_parameters`
This config array lets you set parameters used by default when running the `tenants:migrate` command (or when this command is executed using the `MigrateDatabase` job). Of course, all of these parameters can be overridden by passing them directly in the command call, be it in CLI or using `Artisan::call()`.
### Seeder parameters
### Seeder parameters {#seeder-parameters}
`tenancy.seeder_parameters`

View file

@ -5,11 +5,11 @@ section: content
---
# Console commands
# Console commands {#console-commands}
The package comes with some useful artisan commands.
## **Migrate**
## **Migrate** {#migrate}
The most important command. To use tenants, you have to be able to migrate their databases.
@ -23,14 +23,14 @@ You may use multiple `--tenants=<...>` options.
> Note: By default, the migrations should be in database/migrations/tenant. If you wish to use a different path, you may use the `--path` argument.
## **Rollback & seed**
## **Rollback & seed** {#rollback-and-seed}
- Rollback: `tenants:rollback`
- Seed: `tenants:seed`
Similarly to `migrate`, these commands accept a `--tenants` option.
## **Migrate fresh**
## **Migrate fresh** {#migrate-fresh}
This package also offers a simplified, tenant-aware version of `migrate:fresh`. It runs `db:wipe` and `tenants:migrate` on the tenant's database.
@ -40,7 +40,7 @@ You may use it like this:
php artisan tenants:migrate-fresh --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23
```
## **Run**
## **Run** {#run}
You can use the `tenants:run` command to run your own commands for tenants.
@ -50,7 +50,7 @@ If your command's signature were `email:send {--queue} {--subject=} {body}`, yo
php 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. ..."
```
## **Tenant list**
## **Tenant list** {#tenant-list}
```
php artisan tenants:list
@ -59,7 +59,7 @@ Listing all tenants.
[Tenant] id: 49670df0-1a87-11e9-b7ba-cf5353777957 @ dev.localhost
```
## **Selectively clearing tenant cache**
## **Selectively clearing tenant cache** {#selectively-clearing-tenant-cache}
You can delete specific tenants' cache by using the `--tags` option on `cache:clear`:

View file

@ -4,13 +4,13 @@ extends: _layouts.documentation
section: content
---
# Customizing databases
# Customizing databases {#customizing-databases}
You may customize how a tenant's DB connection is constructed by storing specific internal keys on the tenant.
If you changed the internal prefix on the tenant model, then use that instead of `tenancy_`.
## Specifying database names
## Specifying database names {#specifying-database-names}
You may specify the tenant's database name by setting the `tenancy_db_name` key when creating the tenant.
@ -32,7 +32,7 @@ Tenant::create([
]);
```
## Specifying database credentials
## Specifying database credentials {#specifying-database-credentials}
Database user & password are only created when you use the permission controlled MySQL database manager. See the database config for more info.
@ -49,11 +49,11 @@ The user will be given the grants specified in the `PermissionControlledMySQLDat
Note that you don't want to grant the users the ability to grant themselves more grants.
## Specifying template connections
## Specifying template connections {#specifying-the-template-connections}
To specify the connection that should be used to construct this tenant's database connection (the array like you'd find in `config/database.php`, set the `tenancy_db_connection` key. Otherwise, the connection whose name is in the `tenancy.database.template_connection` config will be used. If that key is null, the central connection will be used.
## Specifying other connection details
## Specifying other connection details {#specifyng-other-connection-details}
You may also set specific connection details without necessarily creating a new connection. The final "connection array" will be constructed by merging the following:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Domains
# Domains {#domains}
Note: Domains are optional. If you're using path or request data identification, you don't need to worry about them.
@ -18,7 +18,7 @@ $tenant->domains()->create([
If you use the subdomain identification middleware, the example above will work for `acme.{any of your central domains}`. If you use the domain identification middleware, use the full hostname like `acme.com`. If you use the combined domain/subdomain identification middleware, `acme` will work as a subdomain and `acme.com` will work as a domain.
## Local development
## Local development {#local-development}
For local development, you may use `*.localhost` domains (like `foo.localhost`) for tenants. On many operating systems, these work the same way as `localhost`.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Early identification
# Early identification {#early-identification}
A slight "gotcha" with using the automatic approach to transition the application's context based on a route middleware is that **route-level middleware is executed after controller constructors.**
@ -12,7 +12,7 @@ The implication of this is if you're using dependency injection to inject some s
There are two ways to solve it, the former of which is preferable.
## Not using constructor DI
## Not using constructor DI {#not-using-constructor-di}
You can inject dependencies in route **actions**, meaning: If you have a route that binds a `Post` model, you can still inject dependencies like this:
@ -58,7 +58,7 @@ class PostController
}
```
## Using a more complex middleware setup
## Using a more complex middleware setup {#using-a-more-complex-middleware-setup}
> Note: There's a new MW in v3 for preventing access from central domains. v2 was doing this a bit differently.

View file

@ -5,7 +5,7 @@ section: content
---
# Event system
# Event system {#event-system}
This package is heavily based around events, which makes it incredibly flexible.
@ -28,7 +28,7 @@ $this->tenancy->initialize($tenant);
Again, all of the above is configurable. You might even disable all tenancy bootstrappers, and just use tenant identification and scope your app manually around the tenant stored in `Stancl\Tenancy\Tenancy`. The choice is yours.
# TenancyServiceProvider
# TenancyServiceProvider {#tenancyserviceprovider}
This package comes with a very convenient service provider that's added to your application when you install the package. This service provider is used for mapping listeners to events specific to the package and is the place where you should put any tenancy-specific service container calls — to not pollute your AppServiceProvider.
@ -38,13 +38,13 @@ Note that you can register listeners to this package's events **anywhere you wan
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
```
# Bootstrapping tenancy
# Bootstrapping tenancy {#bootstrapping-tenancy}
By default, the `BootstrapTenancy` class is listening to the `TenancyInitialized` event (exactly as you can see in the example above). That listener will execute the configured tenancy bootstrappers to transition the application into the tenant's context. You can read more about this on the [tenancy bootstrappers]({{ $page->link('tenancy-bootstrappers') }}) page.
Conversely, when the `TenancyEnded` event fires, the `RevertToCentralContext` event transitions the app back into the central context.
# Job pipelines
# Job pipelines {#job-pipelines}
You may want to use job pipelines even in projects that don't use this package — we think they're a cool concept so they're extracted into a separate package: [github.com/stancl/jobpipeline](https://github.com/stancl/jobpipeline)
@ -52,7 +52,7 @@ The `JobPipeline` is a simple, yet **extremely powerful** class that lets you **
You may use a job pipeline like any other listener, so you can register it in the `TenancyServiceProvider`, `EventServiceProvider` using the `$listen` array, or in any other place using `Event::listen()` — up to you.
## Creating job pipelines
## Creating job pipelines {creating-job-pipelines}
To create a job pipeline, start by specifying the jobs you want to use:
@ -121,13 +121,13 @@ Event::listen(TenantCreated::class, JobPipeline::make([
Note that you can use job pipelines even for converting single jobs to event listeners. That's useful if you have some logic in job classes and don't want to create listener classes just to be able to run these jobs as a result of an event being fired.
# Available events
# Available events {#available-events}
Note: Some database events (`DatabaseMigrated`, `DatabaseSeeded`, `DatabaseRolledback` and possibly others) are **fired in the tenant context.** Depending on how your application bootstraps tenancy, you might need to be specific about interacting with the central database in these events' listeners — that is, if you need to.
Note: All events are located in the `Stancl\Tenancy\Events` namespace.
### **Tenancy**
### **Tenancy** {#tenancy}
- `InitializingTenancy`
- `TenancyInitialized`
@ -140,7 +140,7 @@ Note: All events are located in the `Stancl\Tenancy\Events` namespace.
Note the difference between *initializing tenancy and bootstrapping* tenancy. Tenancy is initialized when a tenant is loaded into the `Tenancy` object. Whereas boostrapping happens **as a result of initialization** — if you're using automatic tenancy, the `BootstrapTenancy` class is listening to the `TenancyInitialized` event and after it's done executing bootstrappers, it fires an event saying that tenancy was bootstrapped. You want to use the bootstrapped event if you want to execute something **after the app has been transitioned to the tenant context.**
### Tenant
### Tenant {#tenant}
The following events are dispatched as a result of Eloquent events being fired in the default `Tenant` implementation (the most often used events are bold):
@ -153,7 +153,7 @@ The following events are dispatched as a result of Eloquent events being fired i
- `DeletingTenant`
- **`TenantDeleted`**
### Domain
### Domain {#domain}
These events are optional. They're only relevant to you if you're using domains for your tenants.
@ -166,7 +166,7 @@ These events are optional. They're only relevant to you if you're using domains
- `DeletingDomain`
- **`DomainDeleted`**
### Database
### Database {#database}
These events are also optional. They're relevant to you if you're using multi-database tenancy:
@ -181,7 +181,7 @@ These events are also optional. They're relevant to you if you're using multi-da
- `DeletingDatabase`
- **`DatabaseDeleted`**
### Resource syncing
### Resource syncing {#resource-syncing}
- **`SyncedResourceSaved`**
- `SyncedResourceChangedInForeignDatabase`

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Cross-domain redirect
# Cross-domain redirect {#cross-domain-redirect}
To enable this feature, uncomment the `Stancl\Tenancy\Features\CrossDomainRedirect::class` line in your `tenancy.features` config.

View file

@ -4,6 +4,6 @@ extends: _layouts.documentation
section: content
---
# Telescope tags
# Telescope tags {#telescope-tags}
TODO

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Tenant config
# Tenant config {#tenant-config}
It's likely you will need to use tenant-specific config in your application. That config could be API keys, things like "products per page" and many other things.
@ -13,7 +13,7 @@ You could just use the the tenant model to get these values, but you may still
- separation of concerns — if you just write tenancy implementation-agnostic `config('shop.products_per_page')`, you will have a much better time changing tenancy implementations
- default values — you may want to use the tenant storage only to override values in your config file
## **Enabling the feature**
## **Enabling the feature** {#enabling-the-feature}
Uncomment the following line in your `tenancy.features` config:
@ -21,7 +21,7 @@ Uncomment the following line in your `tenancy.features` config:
// Stancl\Tenancy\Features\TenantConfig::class,
```
## **Configuring the mappings**
## **Configuring the mappings** {#configuring-the-mappings}
This feature maps keys in the tenant storage to config keys based on the `$storageToConfigMap` public property.
@ -35,7 +35,7 @@ For example, if your `$storageToConfigMap` looked like this:
the value of `paypal_api_key` in tenant model would be copied to the `services.paypal.api_key` config when tenancy is initialized.
## Mapping the value to multiple config keys
## Mapping the value to multiple config keys {#mapping-the-value-to-multiple-config-keys}
Sometimes you may want to copy the value to multiple config keys. To do that, specify the config keys as an array:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Universal Routes
# Universal Routes {#universal-routes}
> Note: If you need custom [onFail]({{ $page->link('tenant-identification') }}) logic, you cannot use this feature as it will override any of your changes to that logic. Instead, look into the source code of this feature and make your onFail logic implement universal routes too.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# User impersonation
# User impersonation {#user-impersonation}
This package comes with a feature that lets you impersonate users inside tenant databases. This feature works with **any identification method** and **any auth guard** — even if you use multiple.
@ -48,7 +48,7 @@ And finally, run the migration:
php artisan migrate
```
## Usage
## Usage {#usage}
First, you need to create a tenant route that looks like this:
@ -79,7 +79,7 @@ $token = tenancy()->impersonate($tenant, $user->id, $redirectUrl);
And redirect the user (or, presumably an "admin") to the route you created.
### Domain identification
### Domain identification {#domain-identification}
```jsx
// Note: This is not part of the package, it's up to you to implement
@ -89,7 +89,7 @@ $domain = $tenant->primary_domain;
return redirect("https://$domain/impersonate/{$token->token}");
```
### Path identification
### Path identification {#path-identification}
```jsx
// Make sure you use the correct prefix for your routes.
@ -98,7 +98,7 @@ return redirect("{$tenant->id}/impersonate/{$token->token}");
And that's it. The user will be redirected to your impersonation route, logged in as the impersonated user, and finally redirected to your redirect URL.
### Custom auth guards
### Custom auth guards {#custom-auth-guards}
If you're using multiple auth guards, you may want to specify what auth guard the impersonation logic should use.
@ -108,7 +108,7 @@ To do this, simply pass the auth guard name as the fourth argument to the `imper
tenancy()->impersonate($tenant, $user->id, $redirectUrl, 'jwt');
```
## Customization
## Customization {#customization}
You may customize the TTL of impersonation tokens by setting the following static property to the amount of seconds you want to use:

View file

@ -5,7 +5,7 @@ section: content
---
# How it works
# How it works {how-it-works}
This package is very flexible and lets you use tenancy however you want. But it comes with sensible defaults that work like this out of the box:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Installation
# Installation {#installation}
Require the package using composer:

View file

@ -5,7 +5,7 @@ section: content
---
# Integrating with other packages
# Integrating with other packages {#integrating-with-other-packages}
If you're using the [automatic mode]({{ $page->link('features/automatic-mode') }}) & [multi-database tenancy]({{ $page->link('multi-database-tenancy') }}), you'll be able to integrate with other packages easily.

View file

@ -4,11 +4,11 @@ extends: _layouts.documentation
section: content
---
# Laravel Horizon
# Laravel Horizon {#laravel-horizon}
Make sure your [queues]({{ $page->link('queues') }}) are configured correctly before using this.
## Tags
## Tags {#tags}
You may add the current tenant's id to your job tags by defining a `tags` method on the class:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Livewire
# Livewire {#livewire}
Open the `config/livewire.php` file and change this:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Laravel Nova
# Laravel Nova {#laravel-nova}
## In the central app
@ -12,7 +12,7 @@ If you wish to use Laravel Nova in the central application (to manage tenants),
You can find the full Nova setup for managing tenants in the [SaaS boilerplate](/saas-boilerplate):
## In the tenant app
## In the tenant app {#in-the-tenant-app}
To use Nova inside of the tenant part of your application, do the following:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Laravel Passport
# Laravel Passport {#laravel-passport}
> If you just want to write an SPA, but don't need an API for some other use (e.g. mobile app), you can avoid a lot of the complexity of writing SPAs by using [Inertia.js](https://inertiajs.com/).
@ -29,7 +29,7 @@ If you want to use the same keypair for all tenants, do the following.
- Don't use `passport:install`, use just `passport:keys`. The install command creates keys & two clients. Instead of creating clients centrally, create `Client`s manually in your [tenant database seeder]({{ $page->link('configuration#seeder-params') }}).
## **Tenant-specific keys**
## **Tenant-specific keys** {#tenant-specific-keys}
If you want to use a unique keypair for each tenant, do the following. (Note: The security benefit of doing this isn't probably that big, since you're likely already using the same `APP_KEY` for all tenants.)
@ -47,7 +47,7 @@ There are multiple ways you can store & load tenant keys, but the most straightf
And again, you need to create clients in your tenant database seeding process.
## Using Passport in both the central & tenant app
## Using Passport in both the central & tenant app {#using-passport-in-both-the-central-and-tenant-app}
![Passport for both central & tenant app](/assets/images/passport_universal.png)

View file

@ -4,20 +4,20 @@ extends: _layouts.documentation
section: content
---
# Integration with Spatie packages
# Integration with Spatie packages {#integration-with-spatie-packages}
## **laravel-activitylog**
## **laravel-activitylog** {#laravel-activitylog}
### For the tenant app:
### For the tenant app: {#for-the-tenant-app}
- Set the `database_connection` key in `config/activitylog.php` to `null`. This makes activitylog use the default connection.
- Publish the migrations and move them to `database/migrations/tenant`. (And, of course, don't forget to run `artisan tenants:migrate`.)
### For the central app:
### For the central app: {#for-the-central-app}
- Set the `database_connection` key in `config/activitylog.php` to the name of your central database connection.
## **laravel-permission**
## **laravel-permission** {#laravel-permission}
Install the package like usual, but publish the migrations and move them to `migrations/tenant`:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Laravel Telescope
# Laravel Telescope {#laravel-telescope}
Enable the the *Telescope tags* feature to have all Telescope requests tagged with the current tenant's id.

View file

@ -4,9 +4,9 @@ extends: _layouts.documentation
section: content
---
# Introduction
# Introduction {#introduction}
## What is multi-tenancy?
## What is multi-tenancy? {#what-is-multi-tenancy}
Multi-tenancy is the ability to provide your service to multiple users (tenants) from a single hosted instance of the application. This is contrasted with deploying the application separately for each user.
@ -16,7 +16,7 @@ Note that if you just want to, say, scope todo tasks to the current user, there'
This package is built around the idea that multi-tenancy usually means letting tenants have their own users which have their own resources, e.g. todo tasks. Not just users having tasks.
## Types of multi-tenancy
## Types of multi-tenancy {#types-of-multi-tenancy}
There are two **types** of multi-tenancy:
@ -25,12 +25,12 @@ There are two **types** of multi-tenancy:
This package lets you do both, though it focuses more on multi-database tenancy because that type requires more work on the side of the package and less work on your side. Whereas for single-database tenancy you're provided with a class that keeps track of the current tenant and model traits — and the rest is up to you.
## Modes of multi-tenancy
## Modes of multi-tenancy {#modes-of-multi-tenancy}
The tenancy "mode" is a unique property of this package. In previous versions, this package was intended primarily for [automatic tenancy]({{ $page->link('automatic-mode') }}), which means that after a tenant was identified, things like database connections, caches, filesystems, queues etc were switched to that tenant's context — his data completely isolated from the rest.
In the current version, we're also making [manual tenancy]({{ $page->link('manual-mode') }}) a first-class feature. We provide you with things like model traits if you wish to scope the data yourself.
## Tenant identification
## Tenant identification {#tenant-identification}
For your application to be tenant-aware, a [tenant has to be identified]({{ $page->link('tenant-identification') }}). This package ships with a large number of identification middlewares. You may identify tenants by domain, subdomain, domain OR subdomain at the same time, path or request data.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Manual initialization
# Manual initialization {#manual-initialization}
Sometimes you may want to initialize tenancy manually — that is, not using web middleware, command traits, queue tenancy etc.

View file

@ -5,7 +5,7 @@ section: content
---
# Manual mode
# Manual mode {#manual-mode}
> See: [Automatic mode]({{ $page->link('automatic-mode') }})

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Migrations
# Migrations {#migrations}
Move your tenant migrations to the `database/migrations/tenant` directory. You can execute them using `php artisan tenants:migrate`.

View file

@ -4,6 +4,6 @@ extends: _layouts.documentation
section: content
---
# Multi-database tenancy
# Multi-database tenancy {#multi-database-tenancy}
TODO

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Optional features
# Optional features {#optional-features}
"Features" are classes that provide additional functionality that's not needed for the core tenancy logic. Out of the box, the package comes with these Features:

View file

@ -4,9 +4,9 @@ extends: _layouts.documentation
section: content
---
# Compared to other packages
# Compared to other packages {#compared-to-other-packages}
## hyn/multi-tenancy
## hyn/multi-tenancy {#hyn/multi-tenancy}
This package intends to provide you with the necessary tooling for adding multi-tenancy **manually** to your application. It will give you model traits, classes for creating tenant databases, and some additional tooling.
@ -25,7 +25,7 @@ It would have been a good option for when you want to implement multi-tenancy ma
I'm not sharing this to intentionally make hyn/multi-tenancy bad, but **be very careful if you decide to go with that package**.
## tenancy/tenancy
## tenancy/tenancy {#tenancy/tenancy}
This package intends to provide you with a framework for building your own multi-tenancy implementation. The documentation is quite lacking, so I couldn't get a too detailed idea of what it does, but from my understanding, it gives you things like events which you can use to build your own multi-tenancy logic.
@ -33,7 +33,7 @@ If you want the absolute highest flexibility and would otherwise build your own
However, if you're looking for a package that will help you make a multi-tenant project quickly, this is probably not the right choice.
## spatie/laravel-multitenancy
## spatie/laravel-multitenancy {#tenacy/laravel-multitenancy}
This package is a very simple implementation of multi-tenancy.
@ -43,7 +43,7 @@ The only benefit I see in this package compared to v2 of stancl/tenancy is that
So, I suggest you consider this package only if you value simplicity for some reason, and aren't building anything with any amount of complexity and need for "business features".
## stancl/tenancy
## stancl/tenancy {#stancl/tenancy}
In my — biased, of course, but likely true as well — opinion, this package is the absolute best choice for the vast majority of applications.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Queues
# Queues {#queues}
If you're using the `QueueTenancyBootstrapper`, queued jobs dispatched from the tenant context will be automatically tenant-aware. The jobs will be stored centrally — if you're using the database queue driver, they will be stored in the `jobs` table in the central database. And tenancy will be initialized for the current tenant prior to the job being processed.
@ -12,7 +12,7 @@ If you're using the `QueueTenancyBootstrapper`, queued jobs dispatched from the
Note: You cannot inject model **instances** with the `SerializesModels` trait, because it tries to hydrate the models before the `tenant` connection is created. Inject model ids instead and use `find()` in the handle method.
### Database queue driver
### Database queue driver {#database-queue-driver}
To force the database queue driver to use the central connection, open your `queue.connections.database` config and add the following line:
@ -22,11 +22,11 @@ To force the database queue driver to use the central connection, open your `que
(Replace `central` with the name of your central database connection.)
### Redis queue driver
### Redis queue driver {#redis-queue-driver}
Make sure the connection used by the queue is not in `tenancy.redis.prefixed_connections`.
## Central queues
## Central queues {#central-queues}
Jobs dispatched from the central context will remain central. However, it's recommended not to mix queue **connections** for central & tenant jobs due to potential leftover global state, e.g. central jobs thinking they're in the previous tenant's context.

View file

@ -4,13 +4,13 @@ extends: _layouts.documentation
section: content
---
# Quickstart Tutorial
# Quickstart Tutorial {#quickstart-tutorial}
This tutorial focuses on getting you started with stancl/tenancy 3.x quickly. It implements multi-database tenancy & domain identification. If you need a different implementation, then **that's absolutely possible with this package** and it's very easy to refactor to a different implementation.
We recommend following this tutorial just **to get things working** so that you can play with the package. Then if you need to, you can refactor the details of the multi-tenancy implementation (e.g. single-database tenancy, request data identification, etc).
## Installation
## Installation {#installation}
First, require the package using composer:
@ -46,7 +46,7 @@ App\Providers\RouteServiceProvider::class,
App\Providers\TenancyServiceProvider::class, // <-- here
```
## Creating a tenant model
## Creating a tenant model {#creating-a-tenant-model}
Now you need to create a Tenant model. The package comes with a default Tenant model that has many features, but it attempts to be mostly unopinonated and as such, we need to create a custom model to use domains & databases. Create `App\Tenant` like this:
@ -74,13 +74,13 @@ Now we need to tell the package to use this custom model:
'tenant_model' => \App\Tenant::class,
```
## Events
## Events {#events}
The defaults will work out of the box here, but a short explanation will be useful. The `TenancyServiceProvider` file in your `app/Providers` directory maps tenancy events to listeners. By default, when a tenant is created, it runs a `JobPipeline` (a smart thing that's part of this package) which makes sure that the `CreateDatabase`, `MigrateDatabase` and optionally other jobs (e.g. `SeedDatabase`) are ran sequentially.
In other words, it creates & migrates the tenant's database after he's created — and it does this in the correct order, because normal event-listener mapping would execute the listeners in some stupid order that would result in things like the database being migrated before it's created, or seeded before it's migrated.
## Central routes
## Central routes {#central-routes}
We'll make a small change to the `app/Providers/RouteServiceProvider.php` file. Specifically, we'll make sure that central routes are registered on central domains only.
@ -112,7 +112,7 @@ protected function centralDomains(): array
}
```
## Central domains
## Central domains {#central-domains}
Now we need to actually specify the central domains. A central domain is a domain that serves your "central app" content, e.g. the landing page where tenants sign up. Open the `config/tenancy.php` file and add them in:
@ -122,7 +122,7 @@ Now we need to actually specify the central domains. A central domain is a domai
],
```
## Tenant routes
## Tenant routes {#tenant-routes}
Your tenant routes will look like this by default:
@ -149,11 +149,11 @@ Route::get('/', function () {
});
```
## Migrations
## Migrations {#migrations}
To have users in tenant databases, let's move the `users` table migration to `database/migrations/tenant`. This will prevent the table from being created in the central database, and it will be instead created in the tenant database when a tenant is created — thanks to our event setup.
## Creating tenants
## Creating tenants {#creating-tenants}
For testing purposes, we'll create a tenant in `tinker` — no need to waste time creating controllers and views for now.
@ -174,6 +174,6 @@ App\Tenant::all()->runForEach(function () {
});
```
## Trying it out
## Trying it out {#trying-it-out}
Now we visit `foo.localhost` in our browser and we should see a dump of the users table where we see some user. If we visit `bar.localhost`, we should see a different user.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Real-time facades
# Real-time facades {#real-time-facades}
When using `storage_path()` suffixing (for local filesystem tenancy), each tenant gets a separate subdirectory in `storage/`.
@ -20,11 +20,11 @@ storage/app/tenant123/foo.png
This means that the other directories in `storage/` are also tenant-scoped. Importantly, the `framework` directory.
## The issue with real-time facades
## The issue with real-time facades {#the-issue-with-real-time-facades}
When real-time facades are used, Laravel creates a PHP file with facade-like code, stores it in `storage_path/framework/cache` and autoloads it.
## Creating framework directories for tenants
## Creating framework directories for tenants {#creating-framework-directories-for-tenants}
To solve this, you need to create these directories for tenants. But note that you only need this if:
1. you're using `storage_path()` suffixing (enabled in `tenancy` config)

View file

@ -4,11 +4,11 @@ extends: _layouts.documentation
section: content
---
# Routes
# Routes {#routes}
This package has a concept of central routes and tenant routes. Central routes are only available on central domains, and tenant routes are only available on tenant domains. If you don't use domain identification, then all routes are always available and you may skip the details about preventing access from other domains.
## Central routes
## Central routes {#central-routes}
You may register central routes in `routes/web.php` or `routes/api.php` like you're used to. However, you need to make one small change to your RouteServiceProvider.
@ -46,7 +46,7 @@ protected function centralDomains(): array
Note: If you're using multiple central domains, you can't use route names, because different routes (= different combinations of domains & paths) can't share the same name. If you need to use a different central domain for testing, use `config()->set()` in your TestCase `setUp()`.
## Tenant routes
## Tenant routes {#tenant-routes}
You may register tenant routes in `routes/tenant.php`. These routes have no middleware applied on them, and their controller namespace is specified in `app/Providers/TenancyServiceProvider`.
@ -70,12 +70,12 @@ You may do the same for the `api` route group, for instance.
Also, you may use different initialization middleware than the domain one. For a full list, see the [Tenant identification]({{ $page->link('tenant-identification') }}) page.
### Conflicting paths
### Conflicting paths {#conflicting-paths}
Due to the order in which the service providers (and as such, their routes) are registered, tenant routes will take precedence over central routes. So if you have a `/` route in your `routes/web.php` file but also `routes/tenant.php`, the tenant route will be used on tenant domains.
However, tenant routes that don't have their central counterpart will still be accessible on central domains and will result in a "Tenant could not be identified on domain ..." error. To avoid this, use the `Stancl\Tenancy\Middleware\PreventAccessFromCentralDomains` middleware on all of your tenant routes. This middleware will abort with a 404 if the user is trying to visit a tenant route on a central domain.
## Universal routes
## Universal routes {#universal-routes}
See the [Universal Routes feature]({{ $page->link('features/universal-routes') }}).

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Session scoping
# Session scoping {#session-scoping}
Session scoping is one thing that you might have to deal with yourself.
@ -12,15 +12,15 @@ The issue occurs when you're using multiple tenant domains and databases. Users
Here's how you can prevent this.
## Storing sessions in the database
## Storing sessions in the database {#storing-sessions-in-the-database}
Since the databases are automatically separated, simply using the database as the session driver will make this problem disappear altogether.
## Storing sessions in Redis
## Storing sessions in Redis {storing-sessions-in-redis}
This is the same solution as using the DB session driver. If you use the [`RedisTenancyBootstrapper`]({{ $page->link('tenancy-bootstrappers') }}), your Redis databases will be automatically separated for your tenants, and as such, any sessions stored in those Redis databases will be scoped correctly.
## Using a middleware to prevent session forgery
## Using a middleware to prevent session forgery {#using-a-middleware-to-prevent-session-forgery}
Alternatively, you may use the `Stancl\Tenancy\Middleware\ScopeSessions` middleware on your tenant routes to make sure that any attempts to manipulate the session will result in a 403 unauthorized response.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Single-database tenancy
# Single-database tenancy {#single-database-tenancy}
Single-database tenancy comes with lower devops complexity, but larger code complexity than multi-database tenancy, since you have to scope things manually, and won't be able to integrate some third-party packages.
@ -16,7 +16,7 @@ You can still use the other [tenancy bootstrappers]({{ $page->link('tenancy-boot
Also make sure you have disabled the database creation jobs (`CreateDatabase`, `MigrateDatabase`, `SeedDatabase` ...) from listening to the `TenantCreated` event.
# Concepts
# Concepts {#concepts}
In single-database tenancy, there are 4 types of models:
@ -86,9 +86,9 @@ class Comment extends Model
And this will automatically scope the `Comment::all()` call to the current tenant. Note that the limitation of this is that you **need to be able to define a relationship to a primary model**, so if you need to do this on the "Vote" in ***Vote** belongsTo **Comment** belongsTo **Post** belongsTo **Tenant**,* you need to define some strange relationship. Laravel supports `HasOneThrough`, but not `BelongsToThrough`, so you'd need to do some hacks around that. For that reason, I recommend avoiding these `Comment::all()`-type queries altogether.
# Database considerations
# Database considerations {#database-considerations}
### Unique indexes
### Unique indexes {unique-indexes}
If you'd have a unique index such as:
@ -112,7 +112,7 @@ $table->unique(['post_id', 'user_id']);
TODO: Unique constraints, validation, mention that eloquent models will be scoped, but DB calls won't
### Validation
### Validation {#validations}
The `unique` and `exists` validation rules of course aren't scoped to the current tenant, so you need to scope them manually like this:
@ -136,7 +136,7 @@ $rules = [
]
```
### Low-level database queries
### Low-level database queries {#low-level-database-queries}
And the final thing to keep in mind is that `DB` facade calls, or any other types of direct database queries, of course won't be scoped to the current tenant.
@ -144,11 +144,11 @@ The package can only provide scoping logic for the abstraction logic that Eloque
Be careful with using them.
## Making global queries
## Making global queries {#making-global-queries}
To disable the tenant scope, simply add `withoutTenancy()` to your query.
## Customizing the column name
## Customizing the column name {#customizing-the-column-name}
If you'd like to customize the column name to use e.g. `team_id` instead of `tenant_id` — if that makes more sense given your business terminology — you can do that by setting this static property in a service provider or some such class:

View file

@ -4,13 +4,13 @@ extends: _layouts.documentation
section: content
---
# Synced resources between tenants
# Synced resources between tenants {#synced-resources-between-tenants}
If you'd like to share certain resources, usually users, between tenant databases, you can use our resource syncing feature. This will let you **sync specific columns between specific tenant databases and the central database.**
This is a relatively complex feature, so before implementing it, make sure you really need it. You only need this feature if you're using multi-database tenancy and need to sync specific resources (like users) between different tenants' databases.
## Database
## Database {#database}
The resource exists in the central database, for example a `users` table. Another table exists in the tenants' databases. It can use the same name as the central database or a different name — up to you.
@ -18,7 +18,7 @@ Then there's a pivot table in the central database that maps the resource (`user
The resource isn't synced with all tenant databases — that would be unwanted, e.g. a user typically only exists in select few tenants.
## Concepts
## Concepts {#concepts}
You will need two models for the resource. One for the tenant database and one for the central database. The tenant model must implement the `Syncable` interface and the central model must implement the `SyncMaster` interface.
@ -30,7 +30,7 @@ An important requirement of the `Syncable` interface is the `getSyncedAttributeN
The resource needs to have the same global ID in the central database and in tenant databases.
## How it works
## How it works {#how-it-works}
Let's write an example implementation:
@ -199,7 +199,7 @@ If you create more tenants and create the user in those tenants' databases, the
Creating the user inside a tenant's database will copy the resource 1:1 to the central database, including the unsynced columns (here they act as default values).
## Attaching resources to tenants
## Attaching resources to tenants {#attaching-resources-to-tenants}
You can see that in the example above we're using the `TenantPivot` model for the BelongsToMany relationship. This lets us cascade synced resources from the central database to tenants:
@ -213,7 +213,7 @@ Attaching a tenant to a user will copy even the unsynced columns (they act as de
If you'd like to use a custom pivot model, look into the source code of `TenantPivot` to see what to copy (or extend it) if you want to preserve this behavior.
## Queueing
## Queueing {#queueing}
In production, you're almost certainly want to queue the listener that copies the changes to other databases. To do this, change the listener's static property:

View file

@ -4,13 +4,13 @@ extends: _layouts.documentation
section: content
---
# Tenancy bootstrappers
# Tenancy bootstrappers {#tenancy-bootstrappers}
Tenancy bootstrappers are classes which make your application tenant-aware in such a way that you don't have to change a line of your code, yet things will be scoped to the current tenant.
The package comes with these bootstrappers out of the box:
## Database tenancy bootstrapper
## Database tenancy bootstrapper {#database-tenancy-bootstrapper}
The database tenancy bootstrapper switches the **default** database connection to `tenant` after it constructs the connection for that tenant.
@ -18,7 +18,7 @@ The database tenancy bootstrapper switches the **default** database connection t
Note that only the **default** connection is switched. If you use another connection explicitly, be it using `DB::connection('...')`, a model `getConnectionName()` method, or a model trait like `CentralConnection`, **it will be respected.** The bootstrapper doesn't **force** any connections, it merely switches the default one.
## Cache tenancy bootstrapper
## Cache tenancy bootstrapper {#cache-tenancy-bootstrapper}
The cache tenancy bootstrapper replaces the Laravel's CacheManager instance with a custom CacheManager that adds tags with the current tenant's ids to each cache call. This scopes cache calls and lets you selectively clear tenants' caches:
@ -28,7 +28,7 @@ php artisan cache:clear --tag=tenant_123
Note that you must use a cache store that supports tagging, e.g. Redis.
## Filesystem tenancy bootstrapper
## Filesystem tenancy bootstrapper {#filesystem-tenancy-boostrapper}
This bootstrapper does the following things:
@ -41,7 +41,7 @@ This bootstrapper is the most complex one, by far. We will have a — better wri
If you don't want to bootstrap filesystem tenancy in this way, and want to — for example — provision an S3 bucket for each tenant, you can absolutely do that. Look at the package's bootstrappers to get an idea of how to write one yourself, and feel free to implement it any way you want.
## Queue tenancy bootstrapper
## Queue tenancy bootstrapper {#queue-tenancy-bootstrapper}
This bootstrapper adds the current tenant's ID to the queued job payloads, and initializes tenancy based on this ID when jobs are being processed.
@ -49,13 +49,13 @@ You can read more about this on the *Queues* page:
[Queues]({{ $page->link('queues') }})
## Redis tenancy bootstrapper
## Redis tenancy bootstrapper {#redis-tenancy-bootstrapper}
If you're using `Redis` calls (not cache calls, **direct** Redis calls) inside the tenant app, you will want to scope Redis data too. To do this, use this bootstrapper. It changes the Redis prefix for each tenant.
Note that you need phpredis, predis won't work.
## Writing custom bootstrappers
## Writing custom bootstrappers {#writing-custom-bootstrappers}
If you want to bootstrap tenancy for something not covered by this package — or something covered by this package, but you want different behavior — you can do that by creating a bootstrapper class.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Tenant identification
# Tenant identification {#tenant-identification}
The package lets you identify tenants using the following methods:
@ -18,7 +18,7 @@ However, **you're free to write additional tenant resolvers.**
All of the identification methods mentioned above come with their own middleware. You can read more about each identification method below.
## Domain identification
## Domain identification {#domain-identification}
To use this identification method, make sure your tenant model uses the `HasDomains` trait.
@ -28,7 +28,7 @@ The relationship is `Tenant hasMany Domain`. Store the hostnames in the `domain`
This identification method comes with the `Stancl\Tenancy\Middleware\InitializeTenancyByDomain` middleware.
## Subdomain identification
## Subdomain identification {#subdomain-identification}
This is the exact same as domain identification, except you store the subdomains in the `domain` column of the `domains` table.
@ -36,13 +36,13 @@ The benefit of this approach rather than storing the subdomain's full hostname i
The middleware for this method is `Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain`.
## Combined domain/subdomain identification
## Combined domain/subdomain identification {#combined-domain/subdomain-identification}
If you'd like to use subdomains and domains at the same time, use the `Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain` middleware.
Records that contain **dots** in the `domain` column will be treated as domains/hostnames (eg. `foo.bar.com`) and records that don't contain any dots will be treated as subdomains (eg. `foo`).
## Path identification
## Path identification {#path-identification}
Some applications will want to use a single domain, but use paths to identify the tenant. This would be when you want customers to use your branded product rather than giving them a whitelabel product that they can use on their own domains.
@ -61,7 +61,7 @@ Route::group([
If you'd like to customize the name of the argument (e.g. use `team` instead of `tenant`, look into the middleware for the public static property).
## Request data identification
## Request data identification {#request-data-identification}
You might want to identify tenants based on request data (headers or query parameters). Applications with SPA frontends and API backends may want to use this approach.
@ -93,11 +93,11 @@ use Stancl\Tenancy\Middleware\InitializeTenancyByRequestData;
InitializeTenancyByRequestData::$queryParameter = null;
```
## Manually identifying tenants
## Manually identifying tenants {#manually-identifying-tenants}
See the [manual initialization page]({{ $page->link('manual-initialization') }}) to see how to identify tenants manually.
## Customizing onFail logic
## Customizing onFail logic {#customizing-onfail-logic}
Each identification middleware has a static `$onFail` property that can be used to customize the behavior that should happen when a tenant couldn't be identified.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Tenants
# Tenants {#tenants}
A tenant can be any model that implements the `Stancl\Tenancy\Contracts\Tenant` interface.
@ -46,7 +46,7 @@ If you want to customize the `Domain` model, you can do that too.
**If you don't need domains or databases, ignore the steps above.** Everything will work just as well.
## Creating tenants
## Creating tenants {#creating-tenants}
You can create tenants like any other models:
@ -58,7 +58,7 @@ $tenant = Tenant::create([
After the tenant is created, an event will be fired. This will result in things like the database being created and migrated, depending on what jobs listen to the event.
## Custom columns
## Custom columns {#custom-columns}
Attributes of the tenant model which don't have their own column will be stored in the `data` JSON column.
@ -95,7 +95,7 @@ The data column is encoded/decoded only on model retrieval and saving.
Also a good rule of thumb is that when you need to query the data with `WHERE` clauses, it should have a dedicated column. This will improve performance and you won't have to think about the `data.` prefixing.
## Running commands in the tenant context
## Running commands in the tenant context {#running-commands-in-the-tenant-context}
You may run commands in a tenant's context and then return to the previous context (be it central, or another tenant's) by passing a callable to the `run()` method on the tenant object. For example:
@ -105,21 +105,21 @@ $tenant->run(function () {
});
```
## Internal keys
## Internal keys {#internal-keys}
Keys that start with the internal prefix (`tenancy_` by default, but you can customize this by overriding the `internalPrefix()` method) are for internal use, so don't start any attribute/column names with that.
## Events
## Events {#events}
The `Tenant` model dispatches Eloquent events, all of which have their own respective class. You can read more about this on the [Event system]({{ $page->link('event-system') }}) page.
## Accessing the current tenant
## Accessing the current tenant {#accessing-the-current-tenant}
You may access the current tenant using the `tenant()` helper. You can also pass an argument to get an attribute from that tenant model, e.g. `tenant('id')`.
Alternatively, you may typehint the `Stancl\Tenancy\Contracts\Tenant` interface to inject the model using the service container.
## Incrementing IDs
## Incrementing IDs {#incrementing-ids}
By default, the migration uses `string` for the `id` column, and the model generates UUIDs when you don't supply an `id` during tenant creation.

View file

@ -4,15 +4,15 @@ extends: _layouts.documentation
section: content
---
# Testing
# Testing {#testing}
TODO: Review
## Central app
## Central app {#central-app}
To test your central app, just write normal Laravel tests.
## Tenant app
## Tenant app {#central-app}
Note: If you're using multi-database tenancy & the automatic mode, it's not possible to use `:memory:` SQLite databases or the `RefreshDatabase` trait due to the switching of default database.

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# The two applications
# The two applications {#the-two-applications}
You will find these two terms a lot throughout this documentation:

View file

@ -4,7 +4,7 @@ extends: _layouts.documentation
section: content
---
# Upgrading from 2.x
# Upgrading from 2.x {upgrading-from-2.x}
Note that most of the package's code is different now, which means that you will have to update all of the places where you interact with the package's classes.