add landing page
7
docs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/build_local/
|
||||
/build_staging/
|
||||
/build_production/
|
||||
/build_staging/
|
||||
/node_modules/
|
||||
/vendor/
|
||||
.DS_Store
|
||||
21
docs/LICENSE.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) <Matt Stauffer, Anthony Terrell, Keith Damiani>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
20
docs/bootstrap.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
use App\Listeners\GenerateSitemap;
|
||||
use TightenCo\Jigsaw\Jigsaw;
|
||||
|
||||
/* @var $container \Illuminate\Container\Container */
|
||||
/* @var $events \TightenCo\Jigsaw\Events\EventBus */
|
||||
|
||||
/*
|
||||
* You can run custom code at different stages of the build process by
|
||||
* listening to the 'beforeBuild', 'afterCollections', and 'afterBuild' events.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* $events->beforeBuild(function (Jigsaw $jigsaw) {
|
||||
* // Your code here
|
||||
* });
|
||||
*/
|
||||
|
||||
$events->afterBuild(GenerateSitemap::class);
|
||||
12
docs/composer.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"require": {
|
||||
"tightenco/jigsaw": "^1.3",
|
||||
"tightenco/jigsaw-docs-template": "^1.0",
|
||||
"samdark/sitemap": "^2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\Listeners\\": "listeners/"
|
||||
}
|
||||
}
|
||||
}
|
||||
1882
docs/composer.lock
generated
Normal file
46
docs/config.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'baseUrl' => '',
|
||||
'production' => false,
|
||||
'siteName' => 'stancl/tenancy documentation',
|
||||
'siteDescription' => 'A Laravel multi-database tenancy package that respects your code.',
|
||||
|
||||
// key => display name
|
||||
'versions' => [
|
||||
'1.x' => '1.x',
|
||||
'2.x' => '2.x (beta)',
|
||||
],
|
||||
'defaultVersion' => '1.x',
|
||||
'prettyUrls' => true,
|
||||
|
||||
'version' => function ($page) {
|
||||
return explode('/', $page->getPath())[1];
|
||||
},
|
||||
|
||||
'link' => function ($page, $path) {
|
||||
return $page->baseUrl . '/' . $page->version() . '/' . $path . ($page->prettyUrls ? '' : '.html');
|
||||
},
|
||||
|
||||
// Algolia DocSearch credentials
|
||||
'docsearchApiKey' => '',
|
||||
'docsearchIndexName' => '',
|
||||
|
||||
// navigation menu
|
||||
'navigation' => require_once('navigation.php'),
|
||||
|
||||
// helpers
|
||||
'isActive' => function ($page, $path) {
|
||||
return ends_with(trimPath($page->getPath()), trimPath($path));
|
||||
},
|
||||
'isActiveParent' => function ($page, $menuItem) {
|
||||
if (is_object($menuItem) && $menuItem->children) {
|
||||
return $menuItem->children->contains(function ($child) use ($page) {
|
||||
return trimPath($page->getPath()) == trimPath($child);
|
||||
});
|
||||
}
|
||||
},
|
||||
'url' => function ($page, $path) {
|
||||
return starts_with($path, 'http') ? $path : '/'.trimPath($path);
|
||||
},
|
||||
];
|
||||
9
docs/config.production.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'baseUrl' => env('JIGSAW_BASE_URL') ?? 'https://tenancy.samuelstancl.me/docs',
|
||||
'production' => true,
|
||||
|
||||
'docsearchApiKey' => '53c5eaf88e819535d98f4a179c1802e1',
|
||||
'docsearchIndexName' => '',
|
||||
];
|
||||
10
docs/config.staging.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'baseUrl' => 'http://jigsaw-docs-staging.tighten.co',
|
||||
'production' => false,
|
||||
|
||||
// DocSearch credentials
|
||||
'docsearchApiKey' => '',
|
||||
'docsearchIndexName' => '',
|
||||
];
|
||||
42
docs/listeners/GenerateSitemap.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use samdark\sitemap\Sitemap;
|
||||
use TightenCo\Jigsaw\Jigsaw;
|
||||
|
||||
class GenerateSitemap
|
||||
{
|
||||
protected $exclude = [
|
||||
'/assets/*',
|
||||
'*/favicon.ico',
|
||||
'*/404',
|
||||
];
|
||||
|
||||
public function handle(Jigsaw $jigsaw)
|
||||
{
|
||||
$baseUrl = $jigsaw->getConfig('baseUrl');
|
||||
|
||||
if (!$baseUrl) {
|
||||
echo "\nTo generate a sitemap.xml file, please specify a 'baseUrl' in config.php.\n\n";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sitemap = new Sitemap($jigsaw->getDestinationPath().'/sitemap.xml');
|
||||
|
||||
collect($jigsaw->getOutputPaths())
|
||||
->reject(function ($path) {
|
||||
return $this->isExcluded($path);
|
||||
})->each(function ($path) use ($baseUrl, $sitemap) {
|
||||
$sitemap->addItem(rtrim($baseUrl, '/').$path, time(), Sitemap::DAILY);
|
||||
});
|
||||
|
||||
$sitemap->write();
|
||||
}
|
||||
|
||||
public function isExcluded($path)
|
||||
{
|
||||
return str_is($this->exclude, $path);
|
||||
}
|
||||
}
|
||||
104
docs/navigation.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'1.x' => [
|
||||
'Getting Started' => [
|
||||
'url' => 'getting-started',
|
||||
'children' => [
|
||||
'Installation' => 'installation',
|
||||
'Storage Drivers' => 'storage-drivers',
|
||||
'This Package vs Others' => 'difference-between-this-package-and-others',
|
||||
'Configuration' => 'configuration',
|
||||
],
|
||||
],
|
||||
'Usage' => [
|
||||
'url' => 'usage',
|
||||
'children' => [
|
||||
'Creating Tenants' => 'creating-tenants',
|
||||
'Tenant Routes' => 'tenant-routes',
|
||||
'Tenant Storage' => 'tenant-storage',
|
||||
'Tenant Manager' => 'tenant-manager',
|
||||
'Console Commands' => 'console-commands',
|
||||
],
|
||||
],
|
||||
'Digging Deeper' => [
|
||||
'url' => 'digging-deeper',
|
||||
'children' => [
|
||||
'Middleware Configuration' => 'middleware-configuration',
|
||||
'Custom Database Names' => 'custom-database-names',
|
||||
'Filesystem Tenancy' => 'filesystem-tenancy',
|
||||
'Jobs & Queues' => 'jobs-queues',
|
||||
'Event System' => 'event-system',
|
||||
'Tenancy Initialization' => 'tenancy-initialization',
|
||||
'Application Testing' => 'application-testing',
|
||||
'Writing Storage Drivers' => 'writing-storage-drivers',
|
||||
'Development' => 'development',
|
||||
],
|
||||
],
|
||||
'Integrations' => [
|
||||
'url' => 'integrations',
|
||||
'children' => [
|
||||
'Telescope' => 'telescope',
|
||||
'Horizon' => 'horizon',
|
||||
],
|
||||
],
|
||||
'Tips' => [
|
||||
'children' => [
|
||||
'HTTPS Certificates' => 'https-certificates',
|
||||
'Misc' => 'misc-tips',
|
||||
],
|
||||
],
|
||||
'Stay Updated' => 'stay-updated',
|
||||
'GitHub' => 'https://github.com/stancl/tenancy',
|
||||
],
|
||||
'2.x' => [
|
||||
'Getting Started' => [
|
||||
'url' => 'getting-started',
|
||||
'children' => [
|
||||
'Installation' => 'installation',
|
||||
'Storage Drivers' => 'storage-drivers',
|
||||
'This Package vs Others' => 'difference-between-this-package-and-others',
|
||||
'Configuration' => 'configuration',
|
||||
],
|
||||
],
|
||||
'Usage' => [
|
||||
'url' => 'usage',
|
||||
'children' => [
|
||||
'Creating Tenants' => 'creating-tenants',
|
||||
'Tenant Routes' => 'tenant-routes',
|
||||
'Tenant Storage' => 'tenant-storage',
|
||||
'Tenant Manager' => 'tenant-manager',
|
||||
'Console Commands' => 'console-commands',
|
||||
],
|
||||
],
|
||||
'Digging Deeper' => [
|
||||
'url' => 'digging-deeper',
|
||||
'children' => [
|
||||
'Middleware Configuration' => 'middleware-configuration',
|
||||
'Custom Database Names' => 'custom-database-names',
|
||||
'Filesystem Tenancy' => 'filesystem-tenancy',
|
||||
'Jobs & Queues' => 'jobs-queues',
|
||||
'Event System' => 'event-system',
|
||||
'Tenancy Initialization' => 'tenancy-initialization',
|
||||
'Application Testing' => 'application-testing',
|
||||
'Writing Storage Drivers' => 'writing-storage-drivers',
|
||||
'Development' => 'development',
|
||||
],
|
||||
],
|
||||
'Integrations' => [
|
||||
'url' => 'integrations',
|
||||
'children' => [
|
||||
'Telescope' => 'telescope',
|
||||
'Horizon' => 'horizon',
|
||||
],
|
||||
],
|
||||
'Tips' => [
|
||||
'children' => [
|
||||
'HTTPS Certificates' => 'https-certificates',
|
||||
'Misc' => 'misc-tips',
|
||||
],
|
||||
],
|
||||
'Stay Updated' => 'stay-updated',
|
||||
'GitHub' => 'https://github.com/stancl/tenancy',
|
||||
],
|
||||
];
|
||||
13944
docs/package-lock.json
generated
Normal file
27
docs/package.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"local": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --env=local --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"staging": "cross-env NODE_ENV=staging node_modules/webpack/bin/webpack.js --progress --hide-modules --env=staging --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --env=production --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"dev": "npm run local",
|
||||
"watch": "npm run local -- --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browser-sync": "^2.26.3",
|
||||
"browser-sync-webpack-plugin": "^2.0.1",
|
||||
"cross-env": "^3.2.3",
|
||||
"docsearch.js": "^2.6.2",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"hasbin": "^1.2.3",
|
||||
"highlight.js": "^9.13.1",
|
||||
"laravel-mix": "^4.0.0",
|
||||
"laravel-mix-purgecss": "^3.0.0",
|
||||
"node-cmd": "^3.0.0",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"tailwindcss": "^0.7.2",
|
||||
"vue-template-compiler": "^2.5.21",
|
||||
"yargs": "^4.6.0"
|
||||
}
|
||||
}
|
||||
95
docs/readme.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Jigsaw Docs Starter Template
|
||||
|
||||
This is a starter template for creating a beautiful, customizable documentation site for your project with minimal effort. You’ll only have to change a few settings and you’re ready to go.
|
||||
|
||||
[View a preview of the docs template.](http://jigsaw-docs-staging.tighten.co/)
|
||||
|
||||
## Installation
|
||||
|
||||
After installing Jigsaw, run the following command from your project directory:
|
||||
|
||||
```bash
|
||||
./vendor/bin/jigsaw init docs
|
||||
```
|
||||
|
||||
This starter template includes samples of common page types, and comes pre-configured with:
|
||||
|
||||
- A fully responsive navigation bar
|
||||
- A sidebar navigation menu
|
||||
- [Tailwind CSS](https://tailwindcss.com/), a utility CSS framework that allows you to customize your design without touching a line of CSS
|
||||
- [Purgecss](https://www.purgecss.com/) to remove unused selectors from your CSS, resulting in smaller CSS files
|
||||
- Syntax highlighting using [highlight.js](https://highlightjs.org/)
|
||||
- A script that automatically generates a `sitemap.xml` file
|
||||
- A search bar powered by [Algolia DocSearch](https://community.algolia.com/docsearch/), and instructions on how to get started with their free indexing service
|
||||
- A custom 404 page
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### Configuring your new site
|
||||
|
||||
As with all Jigsaw sites, configuration settings can be found in `config.php`; you can update the variables in that file with settings specific to your project. You can also add new configuration variables there to use across your site; take a look at the [Jigsaw documentation](http://jigsaw.tighten.co/docs/site-variables/) to learn more.
|
||||
|
||||
```php
|
||||
// config.php
|
||||
return [
|
||||
'baseUrl' => 'https://my-awesome-jigsaw-site.com/',
|
||||
'production' => false,
|
||||
'siteName' => 'My Site',
|
||||
'siteDescription' => 'Give your documentation a boost with Jigsaw.',
|
||||
'docsearchApiKey' => '',
|
||||
'docsearchIndexName' => '',
|
||||
'navigation' => require_once('navigation.php'),
|
||||
];
|
||||
```
|
||||
|
||||
> Tip: This configuration file is also where you’ll define any "collections" (for example, a collection of the contributors to your site, or a collection of blog posts). Check out the official [Jigsaw documentation](https://jigsaw.tighten.co/docs/collections/) to learn more.
|
||||
|
||||
---
|
||||
|
||||
### Adding Content
|
||||
|
||||
You can write your content using a [variety of file types](http://jigsaw.tighten.co/docs/content-other-file-types/). By default, this starter template expects your content to be located in the `source/docs` folder. If you change this, be sure to update the URL references in `navigation.php`.
|
||||
|
||||
The first section of each content page contains a YAML header that specifies how it should be rendered. The `title` attribute is used to dynamically generate HTML `title` and OpenGraph tags for each page. The `extends` attribute defines which parent Blade layout this content file will render with (e.g. `_layouts.documentation` will render with `source/_layouts/documentation.blade.php`), and the `section` attribute defines the Blade "section" that expects this content to be placed into it.
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Navigation
|
||||
description: Building a navigation menu for your site
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
```
|
||||
|
||||
[Read more about Jigsaw layouts.](https://jigsaw.tighten.co/docs/content-blade/)
|
||||
|
||||
---
|
||||
|
||||
### Adding Assets
|
||||
|
||||
Any assets that need to be compiled (such as JavaScript, Less, or Sass files) can be added to the `source/_assets/` directory, and Laravel Mix will process them when running `npm run local` or `npm run production`. The processed assets will be stored in `/source/assets/build/` (note there is no underscore on this second `assets` directory).
|
||||
|
||||
Then, when Jigsaw builds your site, the entire `/source/assets/` directory containing your built files (and any other directories containing static assets, such as images or fonts, that you choose to store there) will be copied to the destination build folders (`build_local`, on your local machine).
|
||||
|
||||
Files that don't require processing (such as images and fonts) can be added directly to `/source/assets/`.
|
||||
|
||||
[Read more about compiling assets in Jigsaw using Laravel Mix.](http://jigsaw.tighten.co/docs/compiling-assets/)
|
||||
|
||||
---
|
||||
|
||||
## Building Your Site
|
||||
|
||||
Now that you’ve edited your configuration variables and know how to customize your styles and content, let’s build the site.
|
||||
|
||||
```bash
|
||||
# build static files with Jigsaw
|
||||
./vendor/bin/jigsaw build
|
||||
|
||||
# compile assets with Laravel Mix
|
||||
# options: dev, staging, production
|
||||
npm run dev
|
||||
```
|
||||
51
docs/source/1.x/application-testing.blade.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: Application Testing
|
||||
description: Application Testing with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Application Testing {#application-testing}
|
||||
|
||||
To test your application with this package installed, you can create tenants in the `setUp()` method of your test case:
|
||||
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the database storage driver, you will also need to run the `create_tenants_table` migration:
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->call('migrate', [
|
||||
'--path' => database_path('migrations'),
|
||||
'--database' => 'sqlite',
|
||||
]);
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the Redis storage driver, flush the database in `setUp()`:
|
||||
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// make sure you're using a different connection for testing to avoid losing data
|
||||
Redis::connection('tenancyTesting')->flushdb();
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
56
docs/source/1.x/configuration.blade.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: Configuration
|
||||
description: Configuring stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Configuration {#configuration}
|
||||
|
||||
The `config/tenancy.php` file lets you configure how the package behaves.
|
||||
|
||||
> **Note:** If the `tenancy.php` file doesn't exist in your `config` directory, you can publish it by running `php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=config`
|
||||
|
||||
### `storage_driver, storage` {#storage}
|
||||
|
||||
This lets you configure the driver for tenant storage, i.e. what will be used to store information about your tenants. You can read more about this on the [Storage Drivers]({{ $page->link('storage-drivers') }}) page.
|
||||
|
||||
Available storage drivers:
|
||||
- `Stancl\Tenancy\StorageDrivers\RedisStorageDriver`
|
||||
- `Stancl\Tenancy\StorageDrivers\DatabaseStorageDriver`
|
||||
|
||||
### `tenant_route_namespace` {#tenant-route-namespace}
|
||||
|
||||
Controller namespace used for routes in `routes/tenant.php`. The default value is the same as the namespace for `web.php` routes.
|
||||
|
||||
### `exempt_domains` {#exempt-domains}
|
||||
|
||||
If a hostname from this array is visited, the `tenant.php` routes won't be registered, letting you use the same routes as in that file.
|
||||
|
||||
### `database` {#database}
|
||||
|
||||
The application's default connection will be switched to a new one — `tenant`. This connection will be based on the connection specified in `tenancy.database.based_on`. The database name will be `tenancy.database.prefix + tenant UUID + tenancy.database.suffix`.
|
||||
|
||||
You can set the suffix to `.sqlite` if you're using sqlite and want the files to be with the `.sqlite` extension. Conversely, you can leave the suffix empty if you're using MySQL, for example.
|
||||
|
||||
### `redis` {#redis}
|
||||
|
||||
If `tenancy.redis.tenancy` is set to true, connections listed in `tenancy.redis.prefixed_connections` will be prefixed with `config('tenancy.redis.prefix_base') . $uuid`.
|
||||
|
||||
> Note: You need phpredis for multi-tenant Redis.
|
||||
|
||||
### `cache` {#cache}
|
||||
|
||||
The `CacheManager` instance that's resolved when you use the `Cache` or the `cache()` helper will be replaced by `Stancl\Tenancy\CacheManager`. This class automatically uses [tags](https://laravel.com/docs/master/cache#cache-tags). The tag will look like `config('tenancy.cache.tag_base') . $uuid`.
|
||||
|
||||
If you need to store something in global, non-tenant cache,
|
||||
|
||||
### `filesystem` {#filesystem}
|
||||
|
||||
The `storage_path()` will be suffixed with a directory named `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
The root of each disk listed in `tenancy.filesystem.disks` will be suffixed with `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
For disks listed in `root_override`, the root will be that string with `%storage_path%` replaced by `storage_path()` *after* tenancy has been initialized. All other disks will be simply suffixed with `tenancy.filesystem.suffix_base` + the tenant UUID.
|
||||
|
||||
Read more about this on the [Filesystem Tenancy]({{ $page->link('filesystem-tenancy') }}) page.
|
||||
64
docs/source/1.x/console-commands.blade.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
title: Console Commands
|
||||
description: Console commands with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Console Commands {#console-commands}
|
||||
|
||||
The package comes with some artisan commands that will help you during development.
|
||||
|
||||
## Migrate {#migrate}
|
||||
|
||||
The most important command. To use tenants, you have to be able to migrate their databases.
|
||||
|
||||
You can use the `tenants:migrate` command to migrate tenant's databases. You can also specify which tenants' databases should be migrated using the `--tenants` option.
|
||||
```
|
||||
php artisan tenants:migrate --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23
|
||||
```
|
||||
|
||||
> Note: Tenant migrations must be located in `database/migrations/tenant`.
|
||||
|
||||
You can use these commands outside the command line as well. If you want to migrate a tenant's database in a controller, you can use the `Artisan` facade.
|
||||
```php
|
||||
$tenant = tenant()->create('tenant1.localhost');
|
||||
|
||||
\Artisan::call('tenants:migrate', [
|
||||
'--tenants' => [$tenant['uuid']]
|
||||
]);
|
||||
```
|
||||
|
||||
## Rollback & seed {#rollback}
|
||||
|
||||
- Rollback: `tenants:rollback`
|
||||
- Seed: `tenants:seed`
|
||||
|
||||
Similarly to [migrate](#migrate), these commands accept a `--tenants` option.
|
||||
|
||||
## Run {#run}
|
||||
|
||||
You can use the tenants:run command to run your own commands for tenants.
|
||||
|
||||
If your command's signature were `email:send {--queue} {--subject=} {body}`, you would run this command like this:
|
||||
```
|
||||
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}
|
||||
|
||||
```none
|
||||
php artisan tenants:list
|
||||
Listing all tenants.
|
||||
[Tenant] uuid: dbe0b330-1a6e-11e9-b4c3-354da4b4f339 @ localhost
|
||||
[Tenant] uuid: 49670df0-1a87-11e9-b7ba-cf5353777957 @ dev.localhost
|
||||
```
|
||||
|
||||
## Selectively clearing tenant cache {#selectively-clearing-tenant-cache}
|
||||
|
||||
You can delete specific tenants' cache by using the `--tags` option on `cache:clear`:
|
||||
```
|
||||
php artisan cache:clear --tags=tenantdbe0b330-1a6e-11e9-b4c3-354da4b4f339
|
||||
```
|
||||
|
||||
The tag is `config('tenancy.cache.tag_base') . $uuid`.
|
||||
30
docs/source/1.x/creating-tenants.blade.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
title: Creating Tenants
|
||||
description: Creating tenants with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Creating Tenants {#creating-tenants}
|
||||
|
||||
> **Make sure your database is correctly [configured]({{ $page->link('configuration/#database') }}) before creating tenants.**
|
||||
|
||||
To create a tenant, you can use
|
||||
|
||||
```php
|
||||
tenant()->create('tenant1.yourapp.com');
|
||||
```
|
||||
|
||||
> Tip: All domains under `.localhost` are routed to 127.0.0.1 on most operating systems. This is useful for development.
|
||||
|
||||
If you want to set some data while creating the tenant, you can pass an array with the data as the second argument:
|
||||
|
||||
```php
|
||||
tenant()->create('tenant2.yourapp.com', [
|
||||
'plan' => 'free'
|
||||
]);
|
||||
```
|
||||
|
||||
The `create` method returns an array with tenant information (`uuid`, `domain` and whatever else you supplied).
|
||||
|
||||
> Note: Creating a tenant doesn't run [migrations](https://stancl-tenancy.netlify.com/docs/console-commands/#migrate) automatically. You have to do that yourself.
|
||||
21
docs/source/1.x/custom-database-names.blade.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
title: Custom Database Names
|
||||
description: Custom Database Names with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Custom Database Names {#custom-database-names}
|
||||
|
||||
If you want to specify the tenant's database name, set the `tenancy.database_name_key` configuration key to the name of the key that is used to specify the database name in the tenant storage. You must use a name that you won't use for storing other data, so it's recommended to avoid names like `database` and use names like `_stancl_tenancy_database_name` instead. Then just give the key a value during the tenant creation process:
|
||||
|
||||
```php
|
||||
>>> tenant()->create('example.com', [
|
||||
'_stancl_tenancy_database_name' => 'example_com'
|
||||
])
|
||||
=> [
|
||||
"uuid" => "49670df0-1a87-11e9-b7ba-cf5353777957",
|
||||
"domain" => "example.com",
|
||||
"_stancl_tenancy_database_name" => "example_com",
|
||||
]
|
||||
```
|
||||
18
docs/source/1.x/development.blade.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Development
|
||||
description: Development | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Development {#development}
|
||||
|
||||
## Running tests {#running-tests}
|
||||
|
||||
### With Docker {#with-docker}
|
||||
If you have Docker installed, simply run ./test. When you're done testing, run docker-compose down to shut down the containers.
|
||||
|
||||
### Without Docker {#without-docker}
|
||||
If you run the tests of this package, please make sure you don't store anything in Redis @ 127.0.0.1:6379 db#14. The contents of this database are flushed everytime the tests are run.
|
||||
|
||||
Some tests are run only if the CI, TRAVIS and CONTINUOUS_INTEGRATION environment variables are set to true. This is to avoid things like bloating your MySQL instance with test databases.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: Difference Between This Package And Others
|
||||
description: Difference Between This Package And Others | with stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Difference Between This Package And Others
|
||||
|
||||
A frequently asked question is the difference between this package and [tenancy/multi-tenant](https://github.com/tenancy/multi-tenant).
|
||||
|
||||
Packages like tenancy/multi-tenant and tenancy/tenancy give you an API for making your application multi-tenant. They give you a tenant DB connection, traits to apply on your models, a guide on creating your own tenant-aware cache, etc.
|
||||
|
||||
This package makes your application multi-tenant automatically and attempts to make you not have to change (m)any things in your code.
|
||||
|
||||
## Which one should you use?
|
||||
|
||||
Depends on what you prefer.
|
||||
|
||||
If you want full control and make your application multi-tenant yourself, use tenancy/multi-tenant.
|
||||
|
||||
If you want to focus on writing your application instead of tenancy implementations, use stancl/tenancy.
|
||||
9
docs/source/1.x/digging-deeper.blade.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Digging Deeper
|
||||
description: Digging Deeper | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Digging Deeper {#digging-deeper}
|
||||
|
||||
47
docs/source/1.x/event-system.blade.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
title: The Event System
|
||||
description: The Event System | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# The Event System
|
||||
|
||||
You can use event hooks to change the behavior of the tenancy boostrapping and tenancy ending processes.
|
||||
|
||||
The following events are available:
|
||||
- `boostrapping`
|
||||
- `boostrapped`
|
||||
- `ending`
|
||||
- `ended`
|
||||
|
||||
### Tenant-specific database connection example {#tenant-specific-database-connection-example}
|
||||
|
||||
You can hook into these events using `Tenancy::<eventName>`:
|
||||
```php
|
||||
\Tenancy::boostrapping(function ($tenantManager) {
|
||||
if ($tenantManager->tenant['uuid'] === 'someUUID') {
|
||||
config(['database.connections.someDatabaseConnection' => $tenantManager->tenant['databaseConnection']]);
|
||||
$tenantManager->database->useConnection('someDatabaseConnection');
|
||||
|
||||
return ['database'];
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The example above checks whether the current tenant has an uuid of `someUUID`. If yes, it creates a new database connection based on data stored in the tenant's storage. Then it changes the default database connection. Finally, it returns an array of the events that this callback prevents.
|
||||
|
||||
The following actions can be prevented:
|
||||
- database connection switch: `database`
|
||||
- Redis prefix: `redis`
|
||||
- CacheManager switch: `cache`
|
||||
- Filesystem changes: `filesystem`
|
||||
|
||||
### Tenant-specific configuration example {#tenant-specific-configuration-example}
|
||||
|
||||
Another common use case for events is tenant-specific config:
|
||||
```php
|
||||
\Tenancy::bootstrapped(function ($tenantManager) {
|
||||
config(['some.api.key' => $tenantManager->tenant['api_key']);
|
||||
});
|
||||
```
|
||||
52
docs/source/1.x/filesystem-tenancy.blade.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Filesystem Tenancy
|
||||
description: Filesystem Tenancy with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Filesystem Tenancy {#filesystem-tenancy}
|
||||
|
||||
> Note: It's important to differentiate between storage_path() and the Storage facade. The Storage facade is what you use to put files into storage, i.e. `Storage::disk('local')->put()`. `storage_path()` is used to get the path to the storage directory.
|
||||
|
||||
The `storage_path()` will be suffixed with a directory named `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
The root of each disk listed in `tenancy.filesystem.disks` will be suffixed with `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
**However, this alone would cause unwanted behavior.** It would work for S3 and similar disks, but for local disks, this would result in `/path_to_your_application/storage/app/tenant1e22e620-1cb8-11e9-93b6-8d1b78ac0bcd/`. That's not what we want. We want `/path_to_your_application/storage/tenant1e22e620-1cb8-11e9-93b6-8d1b78ac0bcd/app/`.
|
||||
|
||||
That's what the `root_override` section is for. `%storage_path%` gets replaced by `storage_path()` *after* tenancy has been initialized. The roots of disks listed in the `root_override` section of the config will be replaced accordingly. All other disks will be simply suffixed with `tenancy.filesystem.suffix_base` + the tenant UUID.
|
||||
|
||||
Since `storage_path()` will be suffixed, your folder structure will look like this:
|
||||
|
||||

|
||||
|
||||
If you write to these directories, you will need to create them after you create the tenant. See the docs for [PHP's mkdir](http://php.net/function.mkdir).
|
||||
|
||||
Logs will be saved to `storage/logs` regardless of any changes to `storage_path()`.
|
||||
|
||||
One thing that you **will** have to change if you use storage similarly to the example on the image is your use of the helper function `asset()` (that is, if you use it).
|
||||
|
||||
You need to make this change to your code:
|
||||
|
||||
```diff
|
||||
- asset("storage/images/products/$product_id.png");
|
||||
+ tenant_asset("images/products/$product_id.png");
|
||||
```
|
||||
|
||||
Note that all (public) tenant assets have to be in the `app/public/` subdirectory of the tenant's storage directory, as shown in the image above.
|
||||
|
||||
This is what the backend of `tenant_asset()` returns:
|
||||
```php
|
||||
// TenantAssetsController
|
||||
return response()->file(storage_path('app/public/' . $path));
|
||||
```
|
||||
|
||||
With default filesystem configuration, these two commands are equivalent:
|
||||
|
||||
```php
|
||||
Storage::disk('public')->put($filename, $data);
|
||||
Storage::disk('local')->put("public/$filename", $data);
|
||||
```
|
||||
|
||||
If you want to store something globally, simply create a new disk and *don't* add it to the `tenancy.filesystem.disks` config.
|
||||
34
docs/source/1.x/getting-started.blade.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: Getting Started
|
||||
description: Getting started with stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Getting Started {#getting-started}
|
||||
|
||||
[**stancl/tenancy**](https://github.com/stancl/tenancy) is a Laravel multi-database tenancy package. It is designed in a way that requires you to make no changes to your codebase. Instead of applying traits on models and replacing every single reference to cache by a reference to a tenant-aware cache, the package lets you write your app without thinking about tenancy. It handles tenancy automatically.
|
||||
|
||||
> Note: Filesystem is the only thing that can be a little problematic. Be sure to read [that page]({{ $page->link('filesystem-tenancy') }}).
|
||||
|
||||
## How does it work? {#how-does-it-work}
|
||||
|
||||
A user visits `client1.yourapp.com`. The package identifies the tenant who this domain belongs to, and automatically does the following:
|
||||
- switches database connection
|
||||
- replaces the default cache manager
|
||||
- switches Redis connection
|
||||
- changes filesystem root paths
|
||||
|
||||
The benefits of this being taken care of by the package are:
|
||||
- separation of concerns: you should write your app, not tenancy implementations
|
||||
- reliability: you won't have to fear that you forgot to replace a reference to cache by a tenant-aware cache call. This is something you might worry about if you're implementing tenancy into an existing application.
|
||||
|
||||
## What is multi-tenancy? {#what-is-multi-tenancy}
|
||||
|
||||
Multi-tenancy is the ability to provide your application to multiple customers (who have their own users and other resources) from a single instance of your application. Think Slack, Shopify, etc.
|
||||
|
||||
Multi-tenancy can be single-database and multi-database.
|
||||
|
||||
**Single-database tenancy** means that your application uses only a single database. The way this is usually implemented is that instead of having the `id`, `title`, `user_id` and `body` columns in your `posts` table, you will also have a `tenant_id` column. This approach works until you need custom databases for your clients. It's also easy to implement, it basically boils down to having your models use a trait which adds a [global scope](https://laravel.com/docs/master/eloquent#global-scopes).
|
||||
|
||||
**Multi-database tenancy**, the type that this package provides, lets you use a separate database for each tenant. The benefits of this approach are scalability, compliance (some clients need to have the database on their server) and mitigation of risks such as showing the wrong tenant's data to a user. The downside is that this model is harder to implement, which is why this package exists.
|
||||
18
docs/source/1.x/horizon.blade.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Horizon Integration
|
||||
description: Horizon Integration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Horizon Integration
|
||||
|
||||
> Make sure your queue is [correctly configured]({{ $page->link('jobs-queues') }}) before using Horizon.
|
||||
|
||||
Jobs are automatically tagged with the tenant's uuid and domain:
|
||||
|
||||

|
||||
|
||||
You can use these tags to monitor specific tenants' jobs:
|
||||
|
||||

|
||||
40
docs/source/1.x/https-certificates.blade.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: HTTPS Certificates
|
||||
description: HTTPS Certificates with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# HTTPS certificates
|
||||
|
||||
HTTPS certificates are very easy to deal with if you use the `yourclient1.yourapp.com`, `yourclient2.yourapp.com` model. You can use a wildcard HTTPS certificate.
|
||||
|
||||
If you use the model where second level domains are used, there are multiple ways you can solve this.
|
||||
|
||||
This guide focuses on nginx.
|
||||
|
||||
### 1. Use nginx with the lua module
|
||||
|
||||
Specifically, you're interested in the [`ssl_certificate_by_lua_block`](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_block) directive. Nginx doesn't support using variables such as the hostname in the `ssl_certificate` directive, which is why the lua module is needed.
|
||||
|
||||
This approach lets you use one server block for all tenants.
|
||||
|
||||
### 2. Add a simple server block for each tenant
|
||||
|
||||
You can store most of your config in a file, such as `/etc/nginx/includes/tenant`, and include this file into tenant server blocks.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
include includes/tenant;
|
||||
server_name foo.bar;
|
||||
# ssl_certificate /etc/foo/...;
|
||||
}
|
||||
```
|
||||
|
||||
### Generating certificates
|
||||
|
||||
You can generate a certificate using certbot. If you use the `--nginx` flag, you will need to run certbot as root. If you use the `--webroot` flag, you only need the user that runs it to have write access to the webroot directory (or perhaps webroot/.well-known is enough) and some certbot files (you can specify these using --work-dir, --config-dir and --logs-dir).
|
||||
|
||||
Creating this config dynamically from PHP is not easy, but is probably feasible. Giving `www-data` write access to `/etc/nginx/sites-available/tenants.conf` should work.
|
||||
|
||||
However, you still need to reload nginx configuration to apply the changes to configuration. This is problematic and I'm not sure if there is a simple and secure way to do this from PHP.
|
||||
8
docs/source/1.x/index.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0; url=getting-started">
|
||||
<title>stancl/tenancy</title>
|
||||
</head>
|
||||
</html>
|
||||
67
docs/source/1.x/installation.blade.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: Installation
|
||||
description: Installing stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Installation {#getting-started}
|
||||
|
||||
Laravel 5.8 or higher is needed.
|
||||
|
||||
### Require the package via composer
|
||||
|
||||
First you need to require the package using composer:
|
||||
|
||||
```
|
||||
composer require stancl/tenancy
|
||||
```
|
||||
|
||||
### Automatic installation {#automatic-installation}
|
||||
|
||||
To install the package, simply run
|
||||
|
||||
```
|
||||
php artisan tenancy:install
|
||||
```
|
||||
|
||||
You will be asked if you want to store your data in Redis or a relational database. You can read more about this on the [Storage Drivers]({{ $page->link('storage-drivers') }}) page.
|
||||
|
||||
This will do all the steps listed in the [Manual installation](#manual-installation) section for you.
|
||||
|
||||
The only thing you have to do now is create a database/Redis connection. Read the [Storage Drivers]({{ $page->link('storage-drivers') }}) page for information about that.
|
||||
|
||||
### Manual installation {#manual-installation}
|
||||
|
||||
If you prefer installing the package manually, you can do that too. It shouldn't take more than a minute either way.
|
||||
|
||||
#### Setting up middleware
|
||||
|
||||
Now open `app/Http/Kernel.php` and make the `InitializeTenancy` middleware top priority, so that it gets executed before anything else, making sure things like the database switch connections soon enough:
|
||||
|
||||
```php
|
||||
protected $middlewarePriority = [
|
||||
\Stancl\Tenancy\Middleware\InitializeTenancy::class,
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
#### Creating routes
|
||||
|
||||
The package lets you have tenant routes and "exempt" routes. Tenant routes are your application's routes. Exempt routes are routes exempt from tenancy — landing pages, sign up forms, and routes for managing tenants.
|
||||
|
||||
Routes in `routes/web.php` are exempt, whereas routes in `routes/tenant.php` have the `InitializeTenancy` middleware automatically applied on them.
|
||||
|
||||
So, to create tenant routes, put those routes in a new file called `routes/tenant.php`.
|
||||
|
||||
#### Configuration
|
||||
|
||||
Run the following:
|
||||
|
||||
```
|
||||
php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=config
|
||||
```
|
||||
|
||||
This creates a `config/tenancy.php`. You can use it to configure how the package works.
|
||||
|
||||
Configuration is explained in detail on the [Configuration]({{ $page->link('configuration') }}) page.
|
||||
12
docs/source/1.x/integrations.blade.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Integrations
|
||||
description: Integrating stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Integrations {#integrations}
|
||||
|
||||
This package naturally integrates well with Laravel packages, since it does not rely on you explicitly specifying database connections.
|
||||
|
||||
There are some exceptions, though. [Telescope integration]({{ $page->link('telescope') }}), for example, requires you to change the database connection in `config/telescope.php` to a non-default one, because the default connection is switched to the tenant connection. Some packages should use a central connection for data storage.
|
||||
23
docs/source/1.x/jobs-queues.blade.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Jobs & Queues
|
||||
description: Jobs & Queues with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Jobs & Queues {#jobs-queues}
|
||||
|
||||
Jobs are automatically multi-tenant, which means that if a job is dispatched while tenant A is initialized, the job will operate with tenant A's database, cache, filesystem, and Redis.
|
||||
|
||||
**However**, if you're using the `database` or `redis` queue driver, you have to make a small tweak to your queue configuration.
|
||||
|
||||
Open `config/queue.php` and make sure your queue driver has an explicitly set connection. Otherwise it would use the default one, which would cause issues, since `database.default` is changed by the package and Redis connections are prefixed.
|
||||
|
||||
**If you're using `database`, add a new line to `queue.connections.database`:**
|
||||
```php
|
||||
'connection' => 'mysql',
|
||||
```
|
||||
|
||||
where `'mysql'` is the name of your non-tenant database connection with a `jobs` table.
|
||||
|
||||
**If you're using Redis, make sure its `'connection'` is not in `tenancy.redis.prefixed_connections`.**
|
||||
20
docs/source/1.x/middleware-configuration.blade.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Middleware Configuration
|
||||
description: Middleware Configuration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Middleware Configuration {#middleware-configuration}
|
||||
|
||||
When a tenant route is visited and the tenant can't be identified, an exception is thrown. If you want to change this behavior, to a redirect for example, add this to your `app/Providers/AppServiceProvider.php`'s `boot()` method:
|
||||
|
||||
```php
|
||||
// use Stancl\Tenancy\Middleware\InitializeTenancy;
|
||||
|
||||
$this->app->bind(InitializeTenancy::class, function ($app) {
|
||||
return new InitializeTenancy(function ($exception) {
|
||||
// return redirect()->route('foo');
|
||||
});
|
||||
});
|
||||
```
|
||||
36
docs/source/1.x/misc-tips.blade.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
title: Miscellaneous Tips
|
||||
description: Miscellaneous Tips | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Miscellaneous Tips {#misc-tips}
|
||||
|
||||
## Tenant Redirect {#tenant-redirect}
|
||||
|
||||
A customer has signed up on your website, you have created a new tenant and now you want to redirect the customer to their website. You can use the `tenant()` method on Redirect, like this:
|
||||
|
||||
```php
|
||||
// tenant sign up controller
|
||||
return redirect()->route('dashboard')->tenant($tenant['domain']);
|
||||
```
|
||||
|
||||
## Custom ID scheme
|
||||
|
||||
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
|
||||
use Stancl\Tenancy\Interfaces\UniqueIdentifierGenerator;
|
||||
|
||||
class MyUniqueIDGenerator implements UniqueIdentifierGenerator
|
||||
{
|
||||
public static function handle(string $domain, array $data): string
|
||||
{
|
||||
return $domain . \Webpatser\Uuid\Uuid::generate(1, $domain);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and then set the `tenancy.unique_id_generator` config to the full path to your class.
|
||||
|
||||
17
docs/source/1.x/stay-updated.blade.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
title: Stay Updated
|
||||
description: Stay Updated | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Stay Updated {#stay-updated}
|
||||
|
||||
If you'd like to be notified about new versions, you can [sign up for e-mail notifications](https://stancl.github.io/tenancy/#stay-updated) or join our [Telegram channel](https://t.me/joinchat/AAAAAFjdrbSJg0ZCHTzxLA).
|
||||
|
||||
You can choose whether you want to receive emails about major versions and/or minor versions.
|
||||
|
||||
- Major versions include breaking changes. Composer won't know about these versions and won't update to them. Major versions will be released about once every 6 months.
|
||||
- Minor versions include backwards-compatible features and bug fixes.
|
||||
|
||||
<!-- todo mailchimp dialog -->
|
||||
61
docs/source/1.x/storage-drivers.blade.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
title: Storage Drivers
|
||||
description: Storage Drivers of stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Storage Drivers {#storage-drivers}
|
||||
|
||||
Storage drivers are used to store a list of all tenants, their domains and any extra information you store about your tenants (e.g. their plan).
|
||||
|
||||
Currently, database and Redis storage drivers are available as part of the package. However, you can [write your own]({{ $page->link('writing-storage-drivers') }}) (and contribute ❤️) storage drivers.
|
||||
|
||||
## Database {#database}
|
||||
|
||||
The database storage driver lets you store tenant information in a relational database like MySQL, PostgreSQL and SQLite.
|
||||
|
||||
The benefit of this storage driver is that you don't have to use both Redis and a database for your data. Also you don't have to do as much configuration.
|
||||
|
||||
To use this driver, you need to have a `tenants` table. You may also use a custom database connection. By default, `tenancy.storage.db.connection` is set to `central`, which means that the `central` database connection will be used to store tenants. This connection is not automatically created, so you'd have to create it manually. You can create database connections in the `config/database.php` file.
|
||||
|
||||
If you'd like to use an existing connection, you can set this config to the name of the connection, e.g. `mysql`.
|
||||
|
||||
To create the `tenants` table, you can use the migration that comes with this package. If you haven't published it during installation, publish it now:
|
||||
```
|
||||
php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=migrations
|
||||
```
|
||||
|
||||
By default, all of your data will be stored in the JSON column `data`. If you want to store some data in a dedicated column (to leverage indexing, for example), add the column to the migration and to `tenancy.custom_columns` config.
|
||||
|
||||
Finally, run the migration:
|
||||
```
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
> If you use a non-default connection, such as `central`, you have to specify which DB to migrate using the `--database` option.
|
||||
>
|
||||
> If you have existing migrations related to your app in `database/migrations`, move them to `database/migrations/tenant`. You can read more about tenant migrations [here]({{ $page->link('console-commands/#migrate') }}).
|
||||
|
||||
## Redis {#redis}
|
||||
|
||||
The Redis storage driver lets you store tenant information in Redis, a high-performance key-value store.
|
||||
|
||||
The benefit of this storage driver is its performance.
|
||||
|
||||
**Note that you need to configure persistence on your Redis instance if you don't want to lose all information about tenants.**
|
||||
|
||||
Read the [Redis documentation page on persistence](https://redis.io/topics/persistence). You should definitely use AOF and if you want to be even more protected from data loss, you can use RDB **in conjunction with AOF**.
|
||||
|
||||
If your cache driver is Redis and you don't want to use AOF with it, run two Redis instances. Otherwise, just make sure you use a different database (number) for tenancy and for anything else.
|
||||
|
||||
To use this driver, create a new Redis connection in the `database.redis` configuration (`config/database.php`) called `tenancy`.
|
||||
|
||||
```php
|
||||
'tenancy' => [
|
||||
'host' => env('TENANCY_REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('TENANCY_REDIS_PASSWORD', null),
|
||||
'port' => env('TENANCY_REDIS_PORT', 6380), // different port = separate Redis instance
|
||||
'database' => env('TENANCY_REDIS_DB', 3), // alternatively, different database number
|
||||
],
|
||||
```
|
||||
29
docs/source/1.x/telescope.blade.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: Telescope Integration
|
||||
description: Telescope Integration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Telescope Integration
|
||||
|
||||
Requests in Telescope are automatically tagged with the tenant uuid and domain:
|
||||
|
||||

|
||||
|
||||
This lets you filter requests by uuid and domain:
|
||||
|
||||

|
||||

|
||||
|
||||
If you'd like to set Telescope tags in your own code, e.g. in your `AppServiceProvider`, replace your `Telescope::tag()` call like this:
|
||||
```php
|
||||
\Tenancy::integrationEvent('telescope', function ($entry) {
|
||||
return ['abc']; // your logic
|
||||
});
|
||||
```
|
||||

|
||||
|
||||
Once Telescope 3 is released, you won't have to do this.
|
||||
|
||||
To have Telescope working, make sure your `telescope.storage.database.connection` points to a non-tenant connection. It's that way by default, so for most projects, Telescope should work out of the box.
|
||||
152
docs/source/1.x/tenancy-initialization.blade.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
---
|
||||
title: Tenancy Initialization
|
||||
description: Tenancy Initialization with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenancy Initialization {#tenancy-initialization}
|
||||
|
||||
Tenancy can be initialized by calling `tenancy()->init()`. The `InitializeTenancy` middleware calls this method automatically.
|
||||
|
||||
You can end a tenancy session using `tenancy()->end()`. This is useful if you need to run multiple tenant sessions or a mixed tenant/non-tenant session in a single request/command.
|
||||
|
||||
The `tenancy()->init()` method calls `bootstrap()`.
|
||||
|
||||
This method switches database connection, Redis connection (if Redis tenancy is enabled), cache and filesystem root paths.
|
||||
|
||||
This page goes through the code that actually makes this happen. You don't have to read this page to use the package, but it will give you insight into the magic that's happening in the background, so that you can be more confident in it.
|
||||
|
||||
## Database tenancy {#database-tenancy}
|
||||
|
||||
`bootstrap()` runs the following method:
|
||||
|
||||
```php
|
||||
public function switchDatabaseConnection()
|
||||
{
|
||||
$this->database->connect($this->getDatabaseName());
|
||||
}
|
||||
```
|
||||
|
||||
If `tenancy.database_name_key` is set and present in the current tenant's data, the `getDatabaseName()` returns the stored database_name. Otherwise it returns the prefix + uuid + suffix.
|
||||
|
||||
```php
|
||||
public function getDatabaseName($tenant = []): string
|
||||
{
|
||||
$tenant = $tenant ?: $this->tenant;
|
||||
if ($key = $this->app['config']['tenancy.database_name_key']) {
|
||||
if (isset($tenant[$key])) {
|
||||
return $tenant[$key];
|
||||
}
|
||||
}
|
||||
return $this->app['config']['tenancy.database.prefix'] . $tenant['uuid'] . $this->app['config']['tenancy.database.suffix'];
|
||||
}
|
||||
```
|
||||
|
||||
This is passed as an argument to the `connect()` method. This method creates a new database connection and sets it as the default one.
|
||||
```php
|
||||
public function connect(string $database)
|
||||
{
|
||||
$this->createTenantConnection($database);
|
||||
$this->useConnection('tenant');
|
||||
}
|
||||
|
||||
public function createTenantConnection(string $database_name)
|
||||
{
|
||||
// Create the `tenant` database connection.
|
||||
$based_on = config('tenancy.database.based_on') ?: config('database.default');
|
||||
config()->set([
|
||||
'database.connections.tenant' => config('database.connections.' . $based_on),
|
||||
]);
|
||||
// Change DB name
|
||||
$database_name = $this->getDriver() === 'sqlite' ? database_path($database_name) : $database_name;
|
||||
config()->set(['database.connections.tenant.database' => $database_name]);
|
||||
}
|
||||
|
||||
public function useConnection(string $connection)
|
||||
{
|
||||
// $this->database = Illuminate\Database\DatabaseManager
|
||||
$this->database->setDefaultConnection($connection);
|
||||
$this->database->reconnect($connection);
|
||||
}
|
||||
```
|
||||
|
||||
## Redis tenancy {#redis-tenancy}
|
||||
|
||||
The `bootstrap()` method calls `setPhpRedisPrefix()` if `tenancy.redis.tenancy` is `true`.
|
||||
|
||||
This method cycles through the `tenancy.redis.prefixed_connections` and sets their prefix to `tenancy.redis.prefix_base` + uuid.
|
||||
```php
|
||||
public function setPhpRedisPrefix($connections = ['default'])
|
||||
{
|
||||
// [...]
|
||||
foreach ($connections as $connection) {
|
||||
$prefix = $this->app['config']['tenancy.redis.prefix_base'] . $this->tenant['uuid'];
|
||||
$client = Redis::connection($connection)->client();
|
||||
try {
|
||||
// [...]
|
||||
$client->setOption($client::OPT_PREFIX, $prefix);
|
||||
} catch (\Throwable $t) {
|
||||
throw new PhpRedisNotInstalledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cache tenancy {#cache-tenancy}
|
||||
|
||||
`bootstrap()` calls `tagCache()` which replaces the `'cache'` key in the service container with a different `CacheManager`.
|
||||
```php
|
||||
public function tagCache()
|
||||
{
|
||||
// [...]
|
||||
$this->app->extend('cache', function () {
|
||||
return new \Stancl\Tenancy\CacheManager($this->app);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This `CacheManager` forwards all calls to the inner store, but also adds tag which "scope" the cache and allow for selective cache clearing:
|
||||
```php
|
||||
class CacheManager extends BaseCacheManager
|
||||
{
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$tags = [config('tenancy.cache.tag_base') . tenant('uuid')];
|
||||
if ($method === 'tags') {
|
||||
if (\count($parameters) !== 1) {
|
||||
throw new \Exception("Method tags() takes exactly 1 argument. {count($parameters)} passed.");
|
||||
}
|
||||
$names = $parameters[0];
|
||||
$names = (array) $names; // cache()->tags('foo') https://laravel.com/docs/5.7/cache#removing-tagged-cache-items
|
||||
return $this->store()->tags(\array_merge($tags, $names));
|
||||
}
|
||||
return $this->store()->tags($tags)->$method(...$parameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Filesystem tenancy {#filesystem-tenancy}
|
||||
|
||||
`bootstrap()` calls `suffiexFilesystemRootPaths()`. This method changes `storage_path()` and the roots of disks listed in `config('tenancy.filesystem.disks)`. You can read more about this on the [Filesystem Tenancy]({{ $page->link('filesystem-tenancy') }}) page.
|
||||
|
||||
```php
|
||||
public function suffixFilesystemRootPaths()
|
||||
{
|
||||
// [...]
|
||||
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . tenant('uuid');
|
||||
// storage_path()
|
||||
$this->app->useStoragePath($old['path'] . "/{$suffix}");
|
||||
// Storage facade
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||
// [...]
|
||||
if ($root = \str_replace('%storage_path%', storage_path(), $this->app['config']["tenancy.filesystem.root_override.{$disk}"])) {
|
||||
Storage::disk($disk)->getAdapter()->setPathPrefix($root);
|
||||
} else {
|
||||
$root = $this->app['config']["filesystems.disks.{$disk}.root"];
|
||||
Storage::disk($disk)->getAdapter()->setPathPrefix($root . "/{$suffix}");
|
||||
}
|
||||
}
|
||||
// [...]
|
||||
}
|
||||
```
|
||||
112
docs/source/1.x/tenant-manager.blade.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: Tenant Manager
|
||||
description: Tenant Manager | stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Manager {#tenant-manager}
|
||||
|
||||
This page documents a couple of other `TenantManager` methods you may find useful.
|
||||
|
||||
### Finding tenant using UUID
|
||||
|
||||
`find()` is an alias for `getTenantById()`. You may use the second argument to specify the key(s) as a string/array.
|
||||
|
||||
```php
|
||||
>>> tenant()->find('dbe0b330-1a6e-11e9-b4c3-354da4b4f339');
|
||||
=> [
|
||||
"uuid" => "dbe0b330-1a6e-11e9-b4c3-354da4b4f339",
|
||||
"domain" => "localhost",
|
||||
"foo" => "bar",
|
||||
]
|
||||
>>> tenant()->find('dbe0b330-1a6e-11e9-b4c3-354da4b4f339', 'foo');
|
||||
=> [
|
||||
"foo" => "bar",
|
||||
]
|
||||
>>> tenant()->getTenantById('dbe0b330-1a6e-11e9-b4c3-354da4b4f339', ['foo', 'domain']);
|
||||
=> [
|
||||
"foo" => "bar",
|
||||
"domain" => "localhost",
|
||||
]
|
||||
```
|
||||
|
||||
### Getting tenant ID by domain
|
||||
|
||||
```php
|
||||
>>> tenant()->getTenantIdByDomain('localhost');
|
||||
=> "b3ce3f90-1a88-11e9-a6b0-038c6337ae50"
|
||||
>>> tenant()->getIdByDomain('localhost');
|
||||
=> "b3ce3f90-1a88-11e9-a6b0-038c6337ae50"
|
||||
```
|
||||
|
||||
### Finding tenant by domain
|
||||
|
||||
You may use the second argument to specify the key(s) as a string/array.
|
||||
|
||||
```php
|
||||
>>> tenant()->findByDomain('localhost');
|
||||
=> [
|
||||
"uuid" => "b3ce3f90-1a88-11e9-a6b0-038c6337ae50",
|
||||
"domain" => "localhost",
|
||||
]
|
||||
```
|
||||
|
||||
### Accessing the array
|
||||
|
||||
You can access the public array tenant of TenantManager like this:
|
||||
|
||||
```php
|
||||
tenancy()->tenant
|
||||
```
|
||||
|
||||
which is an array. If you want to get the value of a specific key from the array, you can use one of the helpers the key on the tenant array as an argument.
|
||||
|
||||
```php
|
||||
tenant('uuid'); // Does the same thing as tenant()->tenant['uuid']
|
||||
```
|
||||
|
||||
### Getting all tenants
|
||||
|
||||
This method returns a collection of arrays.
|
||||
|
||||
```php
|
||||
>>> tenant()->all();
|
||||
=> Illuminate\Support\Collection {#2980
|
||||
all: [
|
||||
[
|
||||
"uuid" => "32e20780-1a88-11e9-a051-4b6489a7edac",
|
||||
"domain" => "localhost",
|
||||
],
|
||||
[
|
||||
"uuid" => "49670df0-1a87-11e9-b7ba-cf5353777957",
|
||||
"domain" => "dev.localhost",
|
||||
],
|
||||
],
|
||||
}
|
||||
>>> tenant()->all()->pluck('domain');
|
||||
=> Illuminate\Support\Collection {#2983
|
||||
all: [
|
||||
"localhost",
|
||||
"dev.localhost",
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting a tenant
|
||||
|
||||
```php
|
||||
>>> tenant()->delete('dbe0b330-1a6e-11e9-b4c3-354da4b4f339');
|
||||
=> true
|
||||
>>> tenant()->delete(tenant()->getTenantIdByDomain('dev.localhost'));
|
||||
=> true
|
||||
>>> tenant()->delete(tenant()->findByDomain('localhost')['uuid']);
|
||||
=> true
|
||||
```
|
||||
|
||||
This doesn't delete the tenant's database. If you want to delete it, save the database name prior to deleting the tenant. You can get the database name using `getDatabaseName()`
|
||||
|
||||
```php
|
||||
>>> tenant()->getDatabaseName(tenant()->findByDomain('laravel.localhost'))
|
||||
=> "tenant67412a60-1c01-11e9-a9e9-f799baa56fd9"
|
||||
```
|
||||
34
docs/source/1.x/tenant-routes.blade.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: Tenant Routes
|
||||
description: Tenant routes with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Routes {#tenant-routes}
|
||||
|
||||
Routes within `routes/tenant.php` will have the `web` middleware group and the `IntializeTenancy` middleware automatically applied on them. This 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.
|
||||
|
||||
> If a tenant cannot be identified, anexception 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}
|
||||
|
||||
Routes outside the `routes/tenant.php` file will not have the tenancy middleware automatically applied on them. You can apply this middleware manually, though.
|
||||
|
||||
If you want some of your, say, API routes to be multi-tenant, simply wrap them in a Route group with this middleware:
|
||||
|
||||
```php
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancy;
|
||||
|
||||
Route::middleware(InitializeTenancy::class)->group(function () {
|
||||
// Route::get('/', 'HelloWorld');
|
||||
});
|
||||
```
|
||||
|
||||
## Using the same routes for tenant and non-tenant parts of the application {#using-the-same-routes-for-tenant-and-non-tenant-parts-of-the-application}
|
||||
|
||||
The `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware makes sure 404 is returned when a user attempts to visit a web route on a tenant (non-exempt) domain.
|
||||
|
||||
The install command applies this middleware to the `web` group. If you want to do this for another route group, add this middleware manually to that group. You can do this in `app/Http/Kernel.php`.
|
||||
48
docs/source/1.x/tenant-storage.blade.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: Tenant Storage
|
||||
description: Tenant storage with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Storage {#tenant-storage}
|
||||
|
||||
Tenant storage is where tenants' uuids and domains are stored. You can store things like the tenant's plan, subscription information, and tenant-specific application configuration in tenant storage. You can use these functions:
|
||||
```php
|
||||
get (string|array $key, string $uuid = null) // $uuid defaults to the current tenant's UUID
|
||||
put (string|array $key, mixed $value = null, string $uuid = null) // if $key is array, make sure $value is null
|
||||
```
|
||||
|
||||
To put something into the tenant storage, you can use `put()` or `set()`.
|
||||
```php
|
||||
tenancy()->put($key, $value);
|
||||
tenancy()->set($key, $value); // alias for put()
|
||||
tenancy()->put($key, $value, $uuid);
|
||||
tenancy()->put(['key1' => 'value1', 'key2' => 'value2']);
|
||||
tenancy()->put(['key1' => 'value1', 'key2' => 'value2'], null, $uuid);
|
||||
```
|
||||
|
||||
To get something from the storage, you can use `get()`:
|
||||
|
||||
```php
|
||||
tenancy()->get($key);
|
||||
tenancy()->get($key, $uuid);
|
||||
tenancy()->get(['key1', 'key2']);
|
||||
```
|
||||
|
||||
> Note: `tenancy()->get(['key1', 'key2'])` returns an array with values only
|
||||
|
||||
Note that $key has to be a string or an array with string keys. The value(s) can be of any data type. Example with arrays:
|
||||
|
||||
```php
|
||||
>>> tenant()->put('foo', ['a' => 'b', 'c' => 'd']);
|
||||
=> [ // put() returns the supplied value(s)
|
||||
"a" => "b",
|
||||
"c" => "d",
|
||||
]
|
||||
>>> tenant()->get('foo');
|
||||
=> [
|
||||
"a" => "b",
|
||||
"c" => "d",
|
||||
]
|
||||
```
|
||||
20
docs/source/1.x/usage.blade.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Usage
|
||||
description: Usage | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Usage {#usage}
|
||||
|
||||
This chapter describes usage of the package. That includes creating tenants, deleting tenants, storing data in the tenant storage.
|
||||
|
||||
Most pages will use the `tenancy()` helper function. This package comes with two helpers - `tenancy()` and `tenant()`. They do the same thing, so you can use the one that reads better given its context.
|
||||
|
||||
`tenant()->create()` reads better than `tenancy()->create()`, but `tenancy()->init()` reads better than `tenant()->init()`.
|
||||
|
||||
You can pass an argument to the helper function to get a value out of the tenant storage. `tenant('plan')` is identical to [`tenant()->get('plan')`]({{ $page->link('tenant-storage') }}).
|
||||
|
||||
The package also comes with two facades. `Tenancy` and `Tenant`. Use what feels the best.
|
||||
|
||||
Both the helpers and the facades resolve the `TenantManager` from the service container.
|
||||
80
docs/source/1.x/writing-storage-drivers.blade.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
title: Writing Storage Drivers
|
||||
description: Writing Storage Drivers with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Writing Storage Drivers
|
||||
|
||||
If you don't want to use the provided DB/Redis storage drivers, you can write your own driver.
|
||||
|
||||
To create a driver, create a class that implements the `Stancl\Tenancy\Interfaces\StorageDriver` interface.
|
||||
|
||||
For historical reasons, the `TenantManager` will try to json encode/decode data coming from the storage driver. If you want to avoid this, set `public $useJson = false;`. That will make `TenantManager` encode/decode only `put()` and `get()` data, so that data types can be stored correctly.
|
||||
|
||||
The DB storage driver has `public $useJson = false;`, while the Redis storage driver doesn't use this property, so it's false by default.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```php
|
||||
|
||||
namespace App\StorageDrivers\MongoDBStorageDriver;
|
||||
|
||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||
|
||||
class MongoDBStorageDriver implements StorageDriver
|
||||
{
|
||||
public $useJson = false;
|
||||
|
||||
public function identifyTenant(string $domain): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getAllTenants(array $uuids = []): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getTenantById(string $uuid, array $fields = []): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getTenantIdByDomain(string $domain): ?string
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function createTenant(string $domain, string $uuid): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function deleteTenant(string $uuid): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function get(string $uuid, string $key)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getMany(string $uuid, array $keys): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function put(string $uuid, string $key, $value)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function putMany(string $uuid, array $values): array
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
```
|
||||
51
docs/source/2.x/application-testing.blade.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
title: Application Testing
|
||||
description: Application Testing with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Application Testing {#application-testing}
|
||||
|
||||
To test your application with this package installed, you can create tenants in the `setUp()` method of your test case:
|
||||
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the database storage driver, you will also need to run the `create_tenants_table` migration:
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->call('migrate', [
|
||||
'--path' => database_path('migrations'),
|
||||
'--database' => 'sqlite',
|
||||
]);
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
|
||||
If you're using the Redis storage driver, flush the database in `setUp()`:
|
||||
|
||||
```php
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// make sure you're using a different connection for testing to avoid losing data
|
||||
Redis::connection('tenancyTesting')->flushdb();
|
||||
|
||||
tenant()->create('test.localhost');
|
||||
tenancy()->init('test.localhost');
|
||||
}
|
||||
```
|
||||
56
docs/source/2.x/configuration.blade.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
title: Configuration
|
||||
description: Configuring stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Configuration {#configuration}
|
||||
|
||||
The `config/tenancy.php` file lets you configure how the package behaves.
|
||||
|
||||
> **Note:** If the `tenancy.php` file doesn't exist in your `config` directory, you can publish it by running `php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=config`
|
||||
|
||||
### `storage_driver, storage` {#storage}
|
||||
|
||||
This lets you configure the driver for tenant storage, i.e. what will be used to store information about your tenants. You can read more about this on the [Storage Drivers]({{ $page->link('storage-drivers') }}) page.
|
||||
|
||||
Available storage drivers:
|
||||
- `Stancl\Tenancy\StorageDrivers\RedisStorageDriver`
|
||||
- `Stancl\Tenancy\StorageDrivers\DatabaseStorageDriver`
|
||||
|
||||
### `tenant_route_namespace` {#tenant-route-namespace}
|
||||
|
||||
Controller namespace used for routes in `routes/tenant.php`. The default value is the same as the namespace for `web.php` routes.
|
||||
|
||||
### `exempt_domains` {#exempt-domains}
|
||||
|
||||
If a hostname from this array is visited, the `tenant.php` routes won't be registered, letting you use the same routes as in that file.
|
||||
|
||||
### `database` {#database}
|
||||
|
||||
The application's default connection will be switched to a new one — `tenant`. This connection will be based on the connection specified in `tenancy.database.based_on`. The database name will be `tenancy.database.prefix + tenant UUID + tenancy.database.suffix`.
|
||||
|
||||
You can set the suffix to `.sqlite` if you're using sqlite and want the files to be with the `.sqlite` extension. Conversely, you can leave the suffix empty if you're using MySQL, for example.
|
||||
|
||||
### `redis` {#redis}
|
||||
|
||||
If `tenancy.redis.tenancy` is set to true, connections listed in `tenancy.redis.prefixed_connections` will be prefixed with `config('tenancy.redis.prefix_base') . $uuid`.
|
||||
|
||||
> Note: You need phpredis for multi-tenant Redis.
|
||||
|
||||
### `cache` {#cache}
|
||||
|
||||
The `CacheManager` instance that's resolved when you use the `Cache` or the `cache()` helper will be replaced by `Stancl\Tenancy\CacheManager`. This class automatically uses [tags](https://laravel.com/docs/master/cache#cache-tags). The tag will look like `config('tenancy.cache.tag_base') . $uuid`.
|
||||
|
||||
If you need to store something in global, non-tenant cache,
|
||||
|
||||
### `filesystem` {#filesystem}
|
||||
|
||||
The `storage_path()` will be suffixed with a directory named `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
The root of each disk listed in `tenancy.filesystem.disks` will be suffixed with `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
For disks listed in `root_override`, the root will be that string with `%storage_path%` replaced by `storage_path()` *after* tenancy has been initialized. All other disks will be simply suffixed with `tenancy.filesystem.suffix_base` + the tenant UUID.
|
||||
|
||||
Read more about this on the [Filesystem Tenancy]({{ $page->link('filesystem-tenancy') }}) page.
|
||||
64
docs/source/2.x/console-commands.blade.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
title: Console Commands
|
||||
description: Console commands with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Console Commands {#console-commands}
|
||||
|
||||
The package comes with some artisan commands that will help you during development.
|
||||
|
||||
## Migrate {#migrate}
|
||||
|
||||
The most important command. To use tenants, you have to be able to migrate their databases.
|
||||
|
||||
You can use the `tenants:migrate` command to migrate tenant's databases. You can also specify which tenants' databases should be migrated using the `--tenants` option.
|
||||
```
|
||||
php artisan tenants:migrate --tenants=8075a580-1cb8-11e9-8822-49c5d8f8ff23
|
||||
```
|
||||
|
||||
> Note: Tenant migrations must be located in `database/migrations/tenant`.
|
||||
|
||||
You can use these commands outside the command line as well. If you want to migrate a tenant's database in a controller, you can use the `Artisan` facade.
|
||||
```php
|
||||
$tenant = tenant()->create('tenant1.localhost');
|
||||
|
||||
\Artisan::call('tenants:migrate', [
|
||||
'--tenants' => [$tenant['uuid']]
|
||||
]);
|
||||
```
|
||||
|
||||
## Rollback & seed {#rollback}
|
||||
|
||||
- Rollback: `tenants:rollback`
|
||||
- Seed: `tenants:seed`
|
||||
|
||||
Similarly to [migrate](#migrate), these commands accept a `--tenants` option.
|
||||
|
||||
## Run {#run}
|
||||
|
||||
You can use the tenants:run command to run your own commands for tenants.
|
||||
|
||||
If your command's signature were `email:send {--queue} {--subject=} {body}`, you would run this command like this:
|
||||
```
|
||||
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}
|
||||
|
||||
```none
|
||||
php artisan tenants:list
|
||||
Listing all tenants.
|
||||
[Tenant] uuid: dbe0b330-1a6e-11e9-b4c3-354da4b4f339 @ localhost
|
||||
[Tenant] uuid: 49670df0-1a87-11e9-b7ba-cf5353777957 @ dev.localhost
|
||||
```
|
||||
|
||||
## Selectively clearing tenant cache {#selectively-clearing-tenant-cache}
|
||||
|
||||
You can delete specific tenants' cache by using the `--tags` option on `cache:clear`:
|
||||
```
|
||||
php artisan cache:clear --tags=tenantdbe0b330-1a6e-11e9-b4c3-354da4b4f339
|
||||
```
|
||||
|
||||
The tag is `config('tenancy.cache.tag_base') . $uuid`.
|
||||
31
docs/source/2.x/creating-tenants.blade.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: Creating Tenants
|
||||
description: Creating tenants with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Creating Tenants {#creating-tenants}
|
||||
|
||||
> **Make sure your database is correctly [configured]({{ $page->link('configuration/#database') }}) before creating tenants.**
|
||||
|
||||
To create a tenant, you can use
|
||||
|
||||
```php
|
||||
Tenant::new()->withDomains(['tenant1.yourapp.com'])->withData(['plan' => 'free'])->save();
|
||||
```
|
||||
|
||||
> Tip: All domains under `.localhost` are routed to 127.0.0.1 on most operating systems. This is useful for development.
|
||||
|
||||
The `withDomains()` and `withData()` methods are optional.
|
||||
|
||||
You can also create a tenant using the `Tenant::create` method:
|
||||
|
||||
```php
|
||||
$domains = ['tenant1.myapp.com', 'tenant1.com'];
|
||||
Tenant::create($domains, [
|
||||
'plan' => 'free',
|
||||
]);
|
||||
```
|
||||
|
||||
> Note: Creating a tenant doesn't run [migrations](https://stancl-tenancy.netlify.com/docs/console-commands/#migrate) automatically. You have to do that yourself. <!-- TODO auto migrate after creation -->
|
||||
21
docs/source/2.x/custom-database-names.blade.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
title: Custom Database Names
|
||||
description: Custom Database Names with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Custom Database Names {#custom-database-names}
|
||||
|
||||
If you want to specify the tenant's database name, set the `tenancy.database_name_key` configuration key to the name of the key that is used to specify the database name in the tenant storage. You must use a name that you won't use for storing other data, so it's recommended to avoid names like `database` and use names like `_stancl_tenancy_database_name` instead. Then just give the key a value during the tenant creation process:
|
||||
|
||||
```php
|
||||
>>> tenant()->create('example.com', [
|
||||
'_stancl_tenancy_database_name' => 'example_com'
|
||||
])
|
||||
=> [
|
||||
"uuid" => "49670df0-1a87-11e9-b7ba-cf5353777957",
|
||||
"domain" => "example.com",
|
||||
"_stancl_tenancy_database_name" => "example_com",
|
||||
]
|
||||
```
|
||||
18
docs/source/2.x/development.blade.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Development
|
||||
description: Development | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Development {#development}
|
||||
|
||||
## Running tests {#running-tests}
|
||||
|
||||
### With Docker {#with-docker}
|
||||
If you have Docker installed, simply run ./test. When you're done testing, run docker-compose down to shut down the containers.
|
||||
|
||||
### Without Docker {#without-docker}
|
||||
If you run the tests of this package, please make sure you don't store anything in Redis @ 127.0.0.1:6379 db#14. The contents of this database are flushed everytime the tests are run.
|
||||
|
||||
Some tests are run only if the CI, TRAVIS and CONTINUOUS_INTEGRATION environment variables are set to true. This is to avoid things like bloating your MySQL instance with test databases.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
title: Difference Between This Package And Others
|
||||
description: Difference Between This Package And Others | with stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Difference Between This Package And Others
|
||||
|
||||
A frequently asked question is the difference between this package and [tenancy/multi-tenant](https://github.com/tenancy/multi-tenant).
|
||||
|
||||
Packages like tenancy/multi-tenant and tenancy/tenancy give you an API for making your application multi-tenant. They give you a tenant DB connection, traits to apply on your models, a guide on creating your own tenant-aware cache, etc.
|
||||
|
||||
This package makes your application multi-tenant automatically and attempts to make you not have to change (m)any things in your code.
|
||||
|
||||
## Which one should you use?
|
||||
|
||||
Depends on what you prefer.
|
||||
|
||||
If you want full control and make your application multi-tenant yourself, use tenancy/multi-tenant.
|
||||
|
||||
If you want to focus on writing your application instead of tenancy implementations, use stancl/tenancy.
|
||||
9
docs/source/2.x/digging-deeper.blade.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Digging Deeper
|
||||
description: Digging Deeper | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Digging Deeper {#digging-deeper}
|
||||
|
||||
47
docs/source/2.x/event-system.blade.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
title: The Event System
|
||||
description: The Event System | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# The Event System
|
||||
|
||||
You can use event hooks to change the behavior of the tenancy boostrapping and tenancy ending processes.
|
||||
|
||||
The following events are available:
|
||||
- `boostrapping`
|
||||
- `boostrapped`
|
||||
- `ending`
|
||||
- `ended`
|
||||
|
||||
### Tenant-specific database connection example {#tenant-specific-database-connection-example}
|
||||
|
||||
You can hook into these events using `Tenancy::<eventName>`:
|
||||
```php
|
||||
\Tenancy::boostrapping(function ($tenantManager) {
|
||||
if ($tenantManager->tenant['uuid'] === 'someUUID') {
|
||||
config(['database.connections.someDatabaseConnection' => $tenantManager->tenant['databaseConnection']]);
|
||||
$tenantManager->database->useConnection('someDatabaseConnection');
|
||||
|
||||
return ['database'];
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The example above checks whether the current tenant has an uuid of `someUUID`. If yes, it creates a new database connection based on data stored in the tenant's storage. Then it changes the default database connection. Finally, it returns an array of the events that this callback prevents.
|
||||
|
||||
The following actions can be prevented:
|
||||
- database connection switch: `database`
|
||||
- Redis prefix: `redis`
|
||||
- CacheManager switch: `cache`
|
||||
- Filesystem changes: `filesystem`
|
||||
|
||||
### Tenant-specific configuration example {#tenant-specific-configuration-example}
|
||||
|
||||
Another common use case for events is tenant-specific config:
|
||||
```php
|
||||
\Tenancy::bootstrapped(function ($tenantManager) {
|
||||
config(['some.api.key' => $tenantManager->tenant['api_key']);
|
||||
});
|
||||
```
|
||||
52
docs/source/2.x/filesystem-tenancy.blade.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Filesystem Tenancy
|
||||
description: Filesystem Tenancy with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Filesystem Tenancy {#filesystem-tenancy}
|
||||
|
||||
> Note: It's important to differentiate between storage_path() and the Storage facade. The Storage facade is what you use to put files into storage, i.e. `Storage::disk('local')->put()`. `storage_path()` is used to get the path to the storage directory.
|
||||
|
||||
The `storage_path()` will be suffixed with a directory named `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
The root of each disk listed in `tenancy.filesystem.disks` will be suffixed with `config('tenancy.filesystem.suffix_base') . $uuid`.
|
||||
|
||||
**However, this alone would cause unwanted behavior.** It would work for S3 and similar disks, but for local disks, this would result in `/path_to_your_application/storage/app/tenant1e22e620-1cb8-11e9-93b6-8d1b78ac0bcd/`. That's not what we want. We want `/path_to_your_application/storage/tenant1e22e620-1cb8-11e9-93b6-8d1b78ac0bcd/app/`.
|
||||
|
||||
That's what the `root_override` section is for. `%storage_path%` gets replaced by `storage_path()` *after* tenancy has been initialized. The roots of disks listed in the `root_override` section of the config will be replaced accordingly. All other disks will be simply suffixed with `tenancy.filesystem.suffix_base` + the tenant UUID.
|
||||
|
||||
Since `storage_path()` will be suffixed, your folder structure will look like this:
|
||||
|
||||

|
||||
|
||||
If you write to these directories, you will need to create them after you create the tenant. See the docs for [PHP's mkdir](http://php.net/function.mkdir).
|
||||
|
||||
Logs will be saved to `storage/logs` regardless of any changes to `storage_path()`.
|
||||
|
||||
One thing that you **will** have to change if you use storage similarly to the example on the image is your use of the helper function `asset()` (that is, if you use it).
|
||||
|
||||
You need to make this change to your code:
|
||||
|
||||
```diff
|
||||
- asset("storage/images/products/$product_id.png");
|
||||
+ tenant_asset("images/products/$product_id.png");
|
||||
```
|
||||
|
||||
Note that all (public) tenant assets have to be in the `app/public/` subdirectory of the tenant's storage directory, as shown in the image above.
|
||||
|
||||
This is what the backend of `tenant_asset()` returns:
|
||||
```php
|
||||
// TenantAssetsController
|
||||
return response()->file(storage_path('app/public/' . $path));
|
||||
```
|
||||
|
||||
With default filesystem configuration, these two commands are equivalent:
|
||||
|
||||
```php
|
||||
Storage::disk('public')->put($filename, $data);
|
||||
Storage::disk('local')->put("public/$filename", $data);
|
||||
```
|
||||
|
||||
If you want to store something globally, simply create a new disk and *don't* add it to the `tenancy.filesystem.disks` config.
|
||||
34
docs/source/2.x/getting-started.blade.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: Getting Started
|
||||
description: Getting started with stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Getting Started {#getting-started}
|
||||
|
||||
[**stancl/tenancy**](https://github.com/stancl/tenancy) is a Laravel multi-database tenancy package. It is designed in a way that requires you to make no changes to your codebase. Instead of applying traits on models and replacing every single reference to cache by a reference to a tenant-aware cache, the package lets you write your app without thinking about tenancy. It handles tenancy automatically.
|
||||
|
||||
> Note: Filesystem is the only thing that can be a little problematic. Be sure to read [that page]({{ $page->link('filesystem-tenancy') }}).
|
||||
|
||||
## How does it work? {#how-does-it-work}
|
||||
|
||||
A user visits `client1.yourapp.com`. The package identifies the tenant who this domain belongs to, and automatically does the following:
|
||||
- switches database connection
|
||||
- replaces the default cache manager
|
||||
- switches Redis connection
|
||||
- changes filesystem root paths
|
||||
|
||||
The benefits of this being taken care of by the package are:
|
||||
- separation of concerns: you should write your app, not tenancy implementations
|
||||
- reliability: you won't have to fear that you forgot to replace a reference to cache by a tenant-aware cache call. This is something you might worry about if you're implementing tenancy into an existing application.
|
||||
|
||||
## What is multi-tenancy? {#what-is-multi-tenancy}
|
||||
|
||||
Multi-tenancy is the ability to provide your application to multiple customers (who have their own users and other resources) from a single instance of your application. Think Slack, Shopify, etc.
|
||||
|
||||
Multi-tenancy can be single-database and multi-database.
|
||||
|
||||
**Single-database tenancy** means that your application uses only a single database. The way this is usually implemented is that instead of having the `id`, `title`, `user_id` and `body` columns in your `posts` table, you will also have a `tenant_id` column. This approach works until you need custom databases for your clients. It's also easy to implement, it basically boils down to having your models use a trait which adds a [global scope](https://laravel.com/docs/master/eloquent#global-scopes).
|
||||
|
||||
**Multi-database tenancy**, the type that this package provides, lets you use a separate database for each tenant. The benefits of this approach are scalability, compliance (some clients need to have the database on their server) and mitigation of risks such as showing the wrong tenant's data to a user. The downside is that this model is harder to implement, which is why this package exists.
|
||||
18
docs/source/2.x/horizon.blade.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
title: Horizon Integration
|
||||
description: Horizon Integration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Horizon Integration
|
||||
|
||||
> Make sure your queue is [correctly configured]({{ $page->link('jobs-queues') }}) before using Horizon.
|
||||
|
||||
Jobs are automatically tagged with the tenant's uuid and domain:
|
||||
|
||||

|
||||
|
||||
You can use these tags to monitor specific tenants' jobs:
|
||||
|
||||

|
||||
40
docs/source/2.x/https-certificates.blade.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: HTTPS Certificates
|
||||
description: HTTPS Certificates with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# HTTPS certificates
|
||||
|
||||
HTTPS certificates are very easy to deal with if you use the `yourclient1.yourapp.com`, `yourclient2.yourapp.com` model. You can use a wildcard HTTPS certificate.
|
||||
|
||||
If you use the model where second level domains are used, there are multiple ways you can solve this.
|
||||
|
||||
This guide focuses on nginx.
|
||||
|
||||
### 1. Use nginx with the lua module
|
||||
|
||||
Specifically, you're interested in the [`ssl_certificate_by_lua_block`](https://github.com/openresty/lua-nginx-module#ssl_certificate_by_lua_block) directive. Nginx doesn't support using variables such as the hostname in the `ssl_certificate` directive, which is why the lua module is needed.
|
||||
|
||||
This approach lets you use one server block for all tenants.
|
||||
|
||||
### 2. Add a simple server block for each tenant
|
||||
|
||||
You can store most of your config in a file, such as `/etc/nginx/includes/tenant`, and include this file into tenant server blocks.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
include includes/tenant;
|
||||
server_name foo.bar;
|
||||
# ssl_certificate /etc/foo/...;
|
||||
}
|
||||
```
|
||||
|
||||
### Generating certificates
|
||||
|
||||
You can generate a certificate using certbot. If you use the `--nginx` flag, you will need to run certbot as root. If you use the `--webroot` flag, you only need the user that runs it to have write access to the webroot directory (or perhaps webroot/.well-known is enough) and some certbot files (you can specify these using --work-dir, --config-dir and --logs-dir).
|
||||
|
||||
Creating this config dynamically from PHP is not easy, but is probably feasible. Giving `www-data` write access to `/etc/nginx/sites-available/tenants.conf` should work.
|
||||
|
||||
However, you still need to reload nginx configuration to apply the changes to configuration. This is problematic and I'm not sure if there is a simple and secure way to do this from PHP.
|
||||
8
docs/source/2.x/index.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0; url=getting-started">
|
||||
<title>stancl/tenancy</title>
|
||||
</head>
|
||||
</html>
|
||||
67
docs/source/2.x/installation.blade.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
title: Installation
|
||||
description: Installing stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Installation {#getting-started}
|
||||
|
||||
Laravel 5.8 or higher is needed.
|
||||
|
||||
### Require the package via composer
|
||||
|
||||
First you need to require the package using composer:
|
||||
|
||||
```
|
||||
composer require stancl/tenancy
|
||||
```
|
||||
|
||||
### Automatic installation {#automatic-installation}
|
||||
|
||||
To install the package, simply run
|
||||
|
||||
```
|
||||
php artisan tenancy:install
|
||||
```
|
||||
|
||||
You will be asked if you want to store your data in Redis or a relational database. You can read more about this on the [Storage Drivers]({{ $page->link('storage-drivers') }}) page.
|
||||
|
||||
This will do all the steps listed in the [Manual installation](#manual-installation) section for you.
|
||||
|
||||
The only thing you have to do now is create a database/Redis connection. Read the [Storage Drivers]({{ $page->link('storage-drivers') }}) page for information about that.
|
||||
|
||||
### Manual installation {#manual-installation}
|
||||
|
||||
If you prefer installing the package manually, you can do that too. It shouldn't take more than a minute either way.
|
||||
|
||||
#### Setting up middleware
|
||||
|
||||
Now open `app/Http/Kernel.php` and make the `InitializeTenancy` middleware top priority, so that it gets executed before anything else, making sure things like the database switch connections soon enough:
|
||||
|
||||
```php
|
||||
protected $middlewarePriority = [
|
||||
\Stancl\Tenancy\Middleware\InitializeTenancy::class,
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
#### Creating routes
|
||||
|
||||
The package lets you have tenant routes and "exempt" routes. Tenant routes are your application's routes. Exempt routes are routes exempt from tenancy — landing pages, sign up forms, and routes for managing tenants.
|
||||
|
||||
Routes in `routes/web.php` are exempt, whereas routes in `routes/tenant.php` have the `InitializeTenancy` middleware automatically applied on them.
|
||||
|
||||
So, to create tenant routes, put those routes in a new file called `routes/tenant.php`.
|
||||
|
||||
#### Configuration
|
||||
|
||||
Run the following:
|
||||
|
||||
```
|
||||
php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=config
|
||||
```
|
||||
|
||||
This creates a `config/tenancy.php`. You can use it to configure how the package works.
|
||||
|
||||
Configuration is explained in detail on the [Configuration]({{ $page->link('configuration') }}) page.
|
||||
12
docs/source/2.x/integrations.blade.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
title: Integrations
|
||||
description: Integrating stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Integrations {#integrations}
|
||||
|
||||
This package naturally integrates well with Laravel packages, since it does not rely on you explicitly specifying database connections.
|
||||
|
||||
There are some exceptions, though. [Telescope integration]({{ $page->link('telescope') }}), for example, requires you to change the database connection in `config/telescope.php` to a non-default one, because the default connection is switched to the tenant connection. Some packages should use a central connection for data storage.
|
||||
23
docs/source/2.x/jobs-queues.blade.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Jobs & Queues
|
||||
description: Jobs & Queues with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Jobs & Queues {#jobs-queues}
|
||||
|
||||
Jobs are automatically multi-tenant, which means that if a job is dispatched while tenant A is initialized, the job will operate with tenant A's database, cache, filesystem, and Redis.
|
||||
|
||||
**However**, if you're using the `database` or `redis` queue driver, you have to make a small tweak to your queue configuration.
|
||||
|
||||
Open `config/queue.php` and make sure your queue driver has an explicitly set connection. Otherwise it would use the default one, which would cause issues, since `database.default` is changed by the package and Redis connections are prefixed.
|
||||
|
||||
**If you're using `database`, add a new line to `queue.connections.database`:**
|
||||
```php
|
||||
'connection' => 'mysql',
|
||||
```
|
||||
|
||||
where `'mysql'` is the name of your non-tenant database connection with a `jobs` table.
|
||||
|
||||
**If you're using Redis, make sure its `'connection'` is not in `tenancy.redis.prefixed_connections`.**
|
||||
20
docs/source/2.x/middleware-configuration.blade.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Middleware Configuration
|
||||
description: Middleware Configuration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Middleware Configuration {#middleware-configuration}
|
||||
|
||||
When a tenant route is visited and the tenant can't be identified, an exception is thrown. If you want to change this behavior, to a redirect for example, add this to your `app/Providers/AppServiceProvider.php`'s `boot()` method:
|
||||
|
||||
```php
|
||||
// use Stancl\Tenancy\Middleware\InitializeTenancy;
|
||||
|
||||
$this->app->bind(InitializeTenancy::class, function ($app) {
|
||||
return new InitializeTenancy(function ($exception) {
|
||||
// return redirect()->route('foo');
|
||||
});
|
||||
});
|
||||
```
|
||||
36
docs/source/2.x/misc-tips.blade.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
title: Miscellaneous Tips
|
||||
description: Miscellaneous Tips | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Miscellaneous Tips {#misc-tips}
|
||||
|
||||
## Tenant Redirect {#tenant-redirect}
|
||||
|
||||
A customer has signed up on your website, you have created a new tenant and now you want to redirect the customer to their website. You can use the `tenant()` method on Redirect, like this:
|
||||
|
||||
```php
|
||||
// tenant sign up controller
|
||||
return redirect()->route('dashboard')->tenant($tenant['domain']);
|
||||
```
|
||||
|
||||
## Custom ID scheme
|
||||
|
||||
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
|
||||
use Stancl\Tenancy\Interfaces\UniqueIdentifierGenerator;
|
||||
|
||||
class MyUniqueIDGenerator implements UniqueIdentifierGenerator
|
||||
{
|
||||
public static function handle(string $domain, array $data): string
|
||||
{
|
||||
return $domain . \Webpatser\Uuid\Uuid::generate(1, $domain);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and then set the `tenancy.unique_id_generator` config to the full path to your class.
|
||||
|
||||
17
docs/source/2.x/stay-updated.blade.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
title: Stay Updated
|
||||
description: Stay Updated | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Stay Updated {#stay-updated}
|
||||
|
||||
If you'd like to be notified about new versions, you can [sign up for e-mail notifications](https://stancl.github.io/tenancy/#stay-updated) or join our [Telegram channel](https://t.me/joinchat/AAAAAFjdrbSJg0ZCHTzxLA).
|
||||
|
||||
You can choose whether you want to receive emails about major versions and/or minor versions.
|
||||
|
||||
- Major versions include breaking changes. Composer won't know about these versions and won't update to them. Major versions will be released about once every 6 months.
|
||||
- Minor versions include backwards-compatible features and bug fixes.
|
||||
|
||||
<!-- todo mailchimp dialog -->
|
||||
61
docs/source/2.x/storage-drivers.blade.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
title: Storage Drivers
|
||||
description: Storage Drivers of stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Storage Drivers {#storage-drivers}
|
||||
|
||||
Storage drivers are used to store a list of all tenants, their domains and any extra information you store about your tenants (e.g. their plan).
|
||||
|
||||
Currently, database and Redis storage drivers are available as part of the package. However, you can [write your own]({{ $page->link('writing-storage-drivers') }}) (and contribute ❤️) storage drivers.
|
||||
|
||||
## Database {#database}
|
||||
|
||||
The database storage driver lets you store tenant information in a relational database like MySQL, PostgreSQL and SQLite.
|
||||
|
||||
The benefit of this storage driver is that you don't have to use both Redis and a database for your data. Also you don't have to do as much configuration.
|
||||
|
||||
To use this driver, you need to have a `tenants` table. You may also use a custom database connection. By default, `tenancy.storage.db.connection` is set to `central`, which means that the `central` database connection will be used to store tenants. This connection is not automatically created, so you'd have to create it manually. You can create database connections in the `config/database.php` file.
|
||||
|
||||
If you'd like to use an existing connection, you can set this config to the name of the connection, e.g. `mysql`.
|
||||
|
||||
To create the `tenants` table, you can use the migration that comes with this package. If you haven't published it during installation, publish it now:
|
||||
```
|
||||
php artisan vendor:publish --provider='Stancl\Tenancy\TenancyServiceProvider' --tag=migrations
|
||||
```
|
||||
|
||||
By default, all of your data will be stored in the JSON column `data`. If you want to store some data in a dedicated column (to leverage indexing, for example), add the column to the migration and to `tenancy.custom_columns` config.
|
||||
|
||||
Finally, run the migration:
|
||||
```
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
> If you use a non-default connection, such as `central`, you have to specify which DB to migrate using the `--database` option.
|
||||
>
|
||||
> If you have existing migrations related to your app in `database/migrations`, move them to `database/migrations/tenant`. You can read more about tenant migrations [here]({{ $page->link('console-commands/#migrate') }}).
|
||||
|
||||
## Redis {#redis}
|
||||
|
||||
The Redis storage driver lets you store tenant information in Redis, a high-performance key-value store.
|
||||
|
||||
The benefit of this storage driver is its performance.
|
||||
|
||||
**Note that you need to configure persistence on your Redis instance if you don't want to lose all information about tenants.**
|
||||
|
||||
Read the [Redis documentation page on persistence](https://redis.io/topics/persistence). You should definitely use AOF and if you want to be even more protected from data loss, you can use RDB **in conjunction with AOF**.
|
||||
|
||||
If your cache driver is Redis and you don't want to use AOF with it, run two Redis instances. Otherwise, just make sure you use a different database (number) for tenancy and for anything else.
|
||||
|
||||
To use this driver, create a new Redis connection in the `database.redis` configuration (`config/database.php`) called `tenancy`.
|
||||
|
||||
```php
|
||||
'tenancy' => [
|
||||
'host' => env('TENANCY_REDIS_HOST', '127.0.0.1'),
|
||||
'password' => env('TENANCY_REDIS_PASSWORD', null),
|
||||
'port' => env('TENANCY_REDIS_PORT', 6380), // different port = separate Redis instance
|
||||
'database' => env('TENANCY_REDIS_DB', 3), // alternatively, different database number
|
||||
],
|
||||
```
|
||||
29
docs/source/2.x/telescope.blade.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
title: Telescope Integration
|
||||
description: Telescope Integration with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Telescope Integration
|
||||
|
||||
Requests in Telescope are automatically tagged with the tenant uuid and domain:
|
||||
|
||||

|
||||
|
||||
This lets you filter requests by uuid and domain:
|
||||
|
||||

|
||||

|
||||
|
||||
If you'd like to set Telescope tags in your own code, e.g. in your `AppServiceProvider`, replace your `Telescope::tag()` call like this:
|
||||
```php
|
||||
\Tenancy::integrationEvent('telescope', function ($entry) {
|
||||
return ['abc']; // your logic
|
||||
});
|
||||
```
|
||||

|
||||
|
||||
Once Telescope 3 is released, you won't have to do this.
|
||||
|
||||
To have Telescope working, make sure your `telescope.storage.database.connection` points to a non-tenant connection. It's that way by default, so for most projects, Telescope should work out of the box.
|
||||
152
docs/source/2.x/tenancy-initialization.blade.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
---
|
||||
title: Tenancy Initialization
|
||||
description: Tenancy Initialization with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenancy Initialization {#tenancy-initialization}
|
||||
|
||||
Tenancy can be initialized by calling `tenancy()->init()`. The `InitializeTenancy` middleware calls this method automatically.
|
||||
|
||||
You can end a tenancy session using `tenancy()->end()`. This is useful if you need to run multiple tenant sessions or a mixed tenant/non-tenant session in a single request/command.
|
||||
|
||||
The `tenancy()->init()` method calls `bootstrap()`.
|
||||
|
||||
This method switches database connection, Redis connection (if Redis tenancy is enabled), cache and filesystem root paths.
|
||||
|
||||
This page goes through the code that actually makes this happen. You don't have to read this page to use the package, but it will give you insight into the magic that's happening in the background, so that you can be more confident in it.
|
||||
|
||||
## Database tenancy {#database-tenancy}
|
||||
|
||||
`bootstrap()` runs the following method:
|
||||
|
||||
```php
|
||||
public function switchDatabaseConnection()
|
||||
{
|
||||
$this->database->connect($this->getDatabaseName());
|
||||
}
|
||||
```
|
||||
|
||||
If `tenancy.database_name_key` is set and present in the current tenant's data, the `getDatabaseName()` returns the stored database_name. Otherwise it returns the prefix + uuid + suffix.
|
||||
|
||||
```php
|
||||
public function getDatabaseName($tenant = []): string
|
||||
{
|
||||
$tenant = $tenant ?: $this->tenant;
|
||||
if ($key = $this->app['config']['tenancy.database_name_key']) {
|
||||
if (isset($tenant[$key])) {
|
||||
return $tenant[$key];
|
||||
}
|
||||
}
|
||||
return $this->app['config']['tenancy.database.prefix'] . $tenant['uuid'] . $this->app['config']['tenancy.database.suffix'];
|
||||
}
|
||||
```
|
||||
|
||||
This is passed as an argument to the `connect()` method. This method creates a new database connection and sets it as the default one.
|
||||
```php
|
||||
public function connect(string $database)
|
||||
{
|
||||
$this->createTenantConnection($database);
|
||||
$this->useConnection('tenant');
|
||||
}
|
||||
|
||||
public function createTenantConnection(string $database_name)
|
||||
{
|
||||
// Create the `tenant` database connection.
|
||||
$based_on = config('tenancy.database.based_on') ?: config('database.default');
|
||||
config()->set([
|
||||
'database.connections.tenant' => config('database.connections.' . $based_on),
|
||||
]);
|
||||
// Change DB name
|
||||
$database_name = $this->getDriver() === 'sqlite' ? database_path($database_name) : $database_name;
|
||||
config()->set(['database.connections.tenant.database' => $database_name]);
|
||||
}
|
||||
|
||||
public function useConnection(string $connection)
|
||||
{
|
||||
// $this->database = Illuminate\Database\DatabaseManager
|
||||
$this->database->setDefaultConnection($connection);
|
||||
$this->database->reconnect($connection);
|
||||
}
|
||||
```
|
||||
|
||||
## Redis tenancy {#redis-tenancy}
|
||||
|
||||
The `bootstrap()` method calls `setPhpRedisPrefix()` if `tenancy.redis.tenancy` is `true`.
|
||||
|
||||
This method cycles through the `tenancy.redis.prefixed_connections` and sets their prefix to `tenancy.redis.prefix_base` + uuid.
|
||||
```php
|
||||
public function setPhpRedisPrefix($connections = ['default'])
|
||||
{
|
||||
// [...]
|
||||
foreach ($connections as $connection) {
|
||||
$prefix = $this->app['config']['tenancy.redis.prefix_base'] . $this->tenant['uuid'];
|
||||
$client = Redis::connection($connection)->client();
|
||||
try {
|
||||
// [...]
|
||||
$client->setOption($client::OPT_PREFIX, $prefix);
|
||||
} catch (\Throwable $t) {
|
||||
throw new PhpRedisNotInstalledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cache tenancy {#cache-tenancy}
|
||||
|
||||
`bootstrap()` calls `tagCache()` which replaces the `'cache'` key in the service container with a different `CacheManager`.
|
||||
```php
|
||||
public function tagCache()
|
||||
{
|
||||
// [...]
|
||||
$this->app->extend('cache', function () {
|
||||
return new \Stancl\Tenancy\CacheManager($this->app);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
This `CacheManager` forwards all calls to the inner store, but also adds tag which "scope" the cache and allow for selective cache clearing:
|
||||
```php
|
||||
class CacheManager extends BaseCacheManager
|
||||
{
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
$tags = [config('tenancy.cache.tag_base') . tenant('uuid')];
|
||||
if ($method === 'tags') {
|
||||
if (\count($parameters) !== 1) {
|
||||
throw new \Exception("Method tags() takes exactly 1 argument. {count($parameters)} passed.");
|
||||
}
|
||||
$names = $parameters[0];
|
||||
$names = (array) $names; // cache()->tags('foo') https://laravel.com/docs/5.7/cache#removing-tagged-cache-items
|
||||
return $this->store()->tags(\array_merge($tags, $names));
|
||||
}
|
||||
return $this->store()->tags($tags)->$method(...$parameters);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Filesystem tenancy {#filesystem-tenancy}
|
||||
|
||||
`bootstrap()` calls `suffiexFilesystemRootPaths()`. This method changes `storage_path()` and the roots of disks listed in `config('tenancy.filesystem.disks)`. You can read more about this on the [Filesystem Tenancy]({{ $page->link('filesystem-tenancy') }}) page.
|
||||
|
||||
```php
|
||||
public function suffixFilesystemRootPaths()
|
||||
{
|
||||
// [...]
|
||||
$suffix = $this->app['config']['tenancy.filesystem.suffix_base'] . tenant('uuid');
|
||||
// storage_path()
|
||||
$this->app->useStoragePath($old['path'] . "/{$suffix}");
|
||||
// Storage facade
|
||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||
// [...]
|
||||
if ($root = \str_replace('%storage_path%', storage_path(), $this->app['config']["tenancy.filesystem.root_override.{$disk}"])) {
|
||||
Storage::disk($disk)->getAdapter()->setPathPrefix($root);
|
||||
} else {
|
||||
$root = $this->app['config']["filesystems.disks.{$disk}.root"];
|
||||
Storage::disk($disk)->getAdapter()->setPathPrefix($root . "/{$suffix}");
|
||||
}
|
||||
}
|
||||
// [...]
|
||||
}
|
||||
```
|
||||
112
docs/source/2.x/tenant-manager.blade.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
---
|
||||
title: Tenant Manager
|
||||
description: Tenant Manager | stancl/tenancy — A Laravel multi-database tenancy package that respects your code.
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Manager {#tenant-manager}
|
||||
|
||||
This page documents a couple of other `TenantManager` methods you may find useful.
|
||||
|
||||
### Finding tenant using UUID
|
||||
|
||||
`find()` is an alias for `getTenantById()`. You may use the second argument to specify the key(s) as a string/array.
|
||||
|
||||
```php
|
||||
>>> tenant()->find('dbe0b330-1a6e-11e9-b4c3-354da4b4f339');
|
||||
=> [
|
||||
"uuid" => "dbe0b330-1a6e-11e9-b4c3-354da4b4f339",
|
||||
"domain" => "localhost",
|
||||
"foo" => "bar",
|
||||
]
|
||||
>>> tenant()->find('dbe0b330-1a6e-11e9-b4c3-354da4b4f339', 'foo');
|
||||
=> [
|
||||
"foo" => "bar",
|
||||
]
|
||||
>>> tenant()->getTenantById('dbe0b330-1a6e-11e9-b4c3-354da4b4f339', ['foo', 'domain']);
|
||||
=> [
|
||||
"foo" => "bar",
|
||||
"domain" => "localhost",
|
||||
]
|
||||
```
|
||||
|
||||
### Getting tenant ID by domain
|
||||
|
||||
```php
|
||||
>>> tenant()->getTenantIdByDomain('localhost');
|
||||
=> "b3ce3f90-1a88-11e9-a6b0-038c6337ae50"
|
||||
>>> tenant()->getIdByDomain('localhost');
|
||||
=> "b3ce3f90-1a88-11e9-a6b0-038c6337ae50"
|
||||
```
|
||||
|
||||
### Finding tenant by domain
|
||||
|
||||
You may use the second argument to specify the key(s) as a string/array.
|
||||
|
||||
```php
|
||||
>>> tenant()->findByDomain('localhost');
|
||||
=> [
|
||||
"uuid" => "b3ce3f90-1a88-11e9-a6b0-038c6337ae50",
|
||||
"domain" => "localhost",
|
||||
]
|
||||
```
|
||||
|
||||
### Accessing the array
|
||||
|
||||
You can access the public array tenant of TenantManager like this:
|
||||
|
||||
```php
|
||||
tenancy()->tenant
|
||||
```
|
||||
|
||||
which is an array. If you want to get the value of a specific key from the array, you can use one of the helpers the key on the tenant array as an argument.
|
||||
|
||||
```php
|
||||
tenant('uuid'); // Does the same thing as tenant()->tenant['uuid']
|
||||
```
|
||||
|
||||
### Getting all tenants
|
||||
|
||||
This method returns a collection of arrays.
|
||||
|
||||
```php
|
||||
>>> tenant()->all();
|
||||
=> Illuminate\Support\Collection {#2980
|
||||
all: [
|
||||
[
|
||||
"uuid" => "32e20780-1a88-11e9-a051-4b6489a7edac",
|
||||
"domain" => "localhost",
|
||||
],
|
||||
[
|
||||
"uuid" => "49670df0-1a87-11e9-b7ba-cf5353777957",
|
||||
"domain" => "dev.localhost",
|
||||
],
|
||||
],
|
||||
}
|
||||
>>> tenant()->all()->pluck('domain');
|
||||
=> Illuminate\Support\Collection {#2983
|
||||
all: [
|
||||
"localhost",
|
||||
"dev.localhost",
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Deleting a tenant
|
||||
|
||||
```php
|
||||
>>> tenant()->delete('dbe0b330-1a6e-11e9-b4c3-354da4b4f339');
|
||||
=> true
|
||||
>>> tenant()->delete(tenant()->getTenantIdByDomain('dev.localhost'));
|
||||
=> true
|
||||
>>> tenant()->delete(tenant()->findByDomain('localhost')['uuid']);
|
||||
=> true
|
||||
```
|
||||
|
||||
This doesn't delete the tenant's database. If you want to delete it, save the database name prior to deleting the tenant. You can get the database name using `getDatabaseName()`
|
||||
|
||||
```php
|
||||
>>> tenant()->getDatabaseName(tenant()->findByDomain('laravel.localhost'))
|
||||
=> "tenant67412a60-1c01-11e9-a9e9-f799baa56fd9"
|
||||
```
|
||||
34
docs/source/2.x/tenant-routes.blade.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: Tenant Routes
|
||||
description: Tenant routes with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Routes {#tenant-routes}
|
||||
|
||||
Routes within `routes/tenant.php` will have the `web` middleware group and the `IntializeTenancy` middleware automatically applied on them. This 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.
|
||||
|
||||
> If a tenant cannot be identified, anexception 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}
|
||||
|
||||
Routes outside the `routes/tenant.php` file will not have the tenancy middleware automatically applied on them. You can apply this middleware manually, though.
|
||||
|
||||
If you want some of your, say, API routes to be multi-tenant, simply wrap them in a Route group with this middleware:
|
||||
|
||||
```php
|
||||
use Stancl\Tenancy\Middleware\InitializeTenancy;
|
||||
|
||||
Route::middleware(InitializeTenancy::class)->group(function () {
|
||||
// Route::get('/', 'HelloWorld');
|
||||
});
|
||||
```
|
||||
|
||||
## Using the same routes for tenant and non-tenant parts of the application {#using-the-same-routes-for-tenant-and-non-tenant-parts-of-the-application}
|
||||
|
||||
The `Stancl\Tenancy\Middleware\PreventAccessFromTenantDomains` middleware makes sure 404 is returned when a user attempts to visit a web route on a tenant (non-exempt) domain.
|
||||
|
||||
The install command applies this middleware to the `web` group. If you want to do this for another route group, add this middleware manually to that group. You can do this in `app/Http/Kernel.php`.
|
||||
48
docs/source/2.x/tenant-storage.blade.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
title: Tenant Storage
|
||||
description: Tenant storage with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Tenant Storage {#tenant-storage}
|
||||
|
||||
Tenant storage is where tenants' uuids and domains are stored. You can store things like the tenant's plan, subscription information, and tenant-specific application configuration in tenant storage. You can use these functions:
|
||||
```php
|
||||
get (string|array $key, string $uuid = null) // $uuid defaults to the current tenant's UUID
|
||||
put (string|array $key, mixed $value = null, string $uuid = null) // if $key is array, make sure $value is null
|
||||
```
|
||||
|
||||
To put something into the tenant storage, you can use `put()` or `set()`.
|
||||
```php
|
||||
tenancy()->put($key, $value);
|
||||
tenancy()->set($key, $value); // alias for put()
|
||||
tenancy()->put($key, $value, $uuid);
|
||||
tenancy()->put(['key1' => 'value1', 'key2' => 'value2']);
|
||||
tenancy()->put(['key1' => 'value1', 'key2' => 'value2'], null, $uuid);
|
||||
```
|
||||
|
||||
To get something from the storage, you can use `get()`:
|
||||
|
||||
```php
|
||||
tenancy()->get($key);
|
||||
tenancy()->get($key, $uuid);
|
||||
tenancy()->get(['key1', 'key2']);
|
||||
```
|
||||
|
||||
> Note: `tenancy()->get(['key1', 'key2'])` returns an array with values only
|
||||
|
||||
Note that $key has to be a string or an array with string keys. The value(s) can be of any data type. Example with arrays:
|
||||
|
||||
```php
|
||||
>>> tenant()->put('foo', ['a' => 'b', 'c' => 'd']);
|
||||
=> [ // put() returns the supplied value(s)
|
||||
"a" => "b",
|
||||
"c" => "d",
|
||||
]
|
||||
>>> tenant()->get('foo');
|
||||
=> [
|
||||
"a" => "b",
|
||||
"c" => "d",
|
||||
]
|
||||
```
|
||||
20
docs/source/2.x/usage.blade.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Usage
|
||||
description: Usage | stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Usage {#usage}
|
||||
|
||||
This chapter describes usage of the package. That includes creating tenants, deleting tenants, storing data in the tenant storage.
|
||||
|
||||
Most pages will use the `tenancy()` helper function. This package comes with two helpers - `tenancy()` and `tenant()`. They do the same thing, so you can use the one that reads better given its context.
|
||||
|
||||
`tenant()->create()` reads better than `tenancy()->create()`, but `tenancy()->init()` reads better than `tenant()->init()`.
|
||||
|
||||
You can pass an argument to the helper function to get a value out of the tenant storage. `tenant('plan')` is identical to [`tenant()->get('plan')`]({{ $page->link('tenant-storage') }}).
|
||||
|
||||
The package also comes with two facades. `Tenancy` and `Tenant`. Use what feels the best.
|
||||
|
||||
Both the helpers and the facades resolve the `TenantManager` from the service container.
|
||||
80
docs/source/2.x/writing-storage-drivers.blade.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
title: Writing Storage Drivers
|
||||
description: Writing Storage Drivers with stancl/tenancy — A Laravel multi-database tenancy package that respects your code..
|
||||
extends: _layouts.documentation
|
||||
section: content
|
||||
---
|
||||
|
||||
# Writing Storage Drivers
|
||||
|
||||
If you don't want to use the provided DB/Redis storage drivers, you can write your own driver.
|
||||
|
||||
To create a driver, create a class that implements the `Stancl\Tenancy\Interfaces\StorageDriver` interface.
|
||||
|
||||
For historical reasons, the `TenantManager` will try to json encode/decode data coming from the storage driver. If you want to avoid this, set `public $useJson = false;`. That will make `TenantManager` encode/decode only `put()` and `get()` data, so that data types can be stored correctly.
|
||||
|
||||
The DB storage driver has `public $useJson = false;`, while the Redis storage driver doesn't use this property, so it's false by default.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```php
|
||||
|
||||
namespace App\StorageDrivers\MongoDBStorageDriver;
|
||||
|
||||
use Stancl\Tenancy\Interfaces\StorageDriver;
|
||||
|
||||
class MongoDBStorageDriver implements StorageDriver
|
||||
{
|
||||
public $useJson = false;
|
||||
|
||||
public function identifyTenant(string $domain): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getAllTenants(array $uuids = []): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getTenantById(string $uuid, array $fields = []): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getTenantIdByDomain(string $domain): ?string
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function createTenant(string $domain, string $uuid): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function deleteTenant(string $uuid): bool
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function get(string $uuid, string $key)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function getMany(string $uuid, array $keys): array
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function put(string $uuid, string $key, $value)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function putMany(string $uuid, array $values): array
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
```
|
||||
15
docs/source/404.blade.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@extends('_layouts.master')
|
||||
|
||||
@section('body')
|
||||
<div class="flex flex-col items-center mt-32 text-grey-darker">
|
||||
<h1 class="text-6xl font-light leading-none mb-2">404</h1>
|
||||
|
||||
<h2 class="text-3xl">Page not found</h2>
|
||||
|
||||
<hr class="block w-full max-w-sm mx-auto my-8 border">
|
||||
|
||||
<p class="text-xl">
|
||||
Need to update this page? See the <a title="404 Page Documentation" href="https://jigsaw.tighten.co/docs/custom-404-page/"> Jigsaw documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
@endsection
|
||||
17
docs/source/_assets/js/main.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
window.docsearch = require('docsearch.js');
|
||||
|
||||
import hljs from 'highlight.js/lib/highlight';
|
||||
|
||||
hljs.registerLanguage('bash', require('highlight.js/lib/languages/bash'));
|
||||
hljs.registerLanguage('css', require('highlight.js/lib/languages/css'));
|
||||
hljs.registerLanguage('html', require('highlight.js/lib/languages/xml'));
|
||||
hljs.registerLanguage('javascript', require('highlight.js/lib/languages/javascript'));
|
||||
hljs.registerLanguage('json', require('highlight.js/lib/languages/json'));
|
||||
hljs.registerLanguage('markdown', require('highlight.js/lib/languages/markdown'));
|
||||
hljs.registerLanguage('php', require('highlight.js/lib/languages/php'));
|
||||
hljs.registerLanguage('scss', require('highlight.js/lib/languages/scss'));
|
||||
hljs.registerLanguage('yaml', require('highlight.js/lib/languages/yaml'));
|
||||
|
||||
document.querySelectorAll('pre code').forEach((block) => {
|
||||
hljs.highlightBlock(block);
|
||||
});
|
||||
130
docs/source/_assets/sass/_base.scss
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
body {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply .font-semibold;
|
||||
@apply .no-underline;
|
||||
@apply .text-blue-dark;
|
||||
|
||||
&:hover {
|
||||
@apply .text-blue-darker;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@apply .border-blue-light;
|
||||
@apply .border-l-4;
|
||||
@apply .font-normal;
|
||||
@apply .italic;
|
||||
@apply .my-8;
|
||||
@apply .pl-6;
|
||||
@apply .text-grey-darker;
|
||||
@apply .text-lg;
|
||||
}
|
||||
|
||||
code {
|
||||
@apply .bg-grey-light;
|
||||
@apply .px-2;
|
||||
@apply .py-px;
|
||||
@apply .rounded;
|
||||
@apply .text-sm;
|
||||
}
|
||||
|
||||
code.hljs {
|
||||
@apply .bg-transparent;
|
||||
@apply .p-0;
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-keyword,
|
||||
.hljs-meta {
|
||||
@apply .font-normal;
|
||||
@apply .roman;
|
||||
}
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply .leading-tight;
|
||||
@apply .mb-4;
|
||||
@apply .mt-8;
|
||||
@apply .text-black;
|
||||
|
||||
&:first-child {
|
||||
@apply .mt-0;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply .font-extrabold;
|
||||
@apply .text-5xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply .font-bold;
|
||||
@apply .text-4xl;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply .font-bold;
|
||||
@apply .text-3xl;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply .font-normal;
|
||||
@apply .text-2xl;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@apply .font-normal;
|
||||
@apply .text-xl;
|
||||
}
|
||||
|
||||
h6 {
|
||||
@apply .font-light;
|
||||
@apply .text-lg;
|
||||
}
|
||||
|
||||
hr {
|
||||
@apply .border-b;
|
||||
@apply .border-blue-lighter;
|
||||
@apply .my-12;
|
||||
@apply .rounded-full;
|
||||
}
|
||||
|
||||
li {
|
||||
ul,
|
||||
ol {
|
||||
@apply .my-0;
|
||||
}
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
@apply .my-4;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply .my-6;
|
||||
}
|
||||
|
||||
pre {
|
||||
@apply .bg-grey-lighter;
|
||||
@apply .leading-loose;
|
||||
@apply .my-6;
|
||||
@apply .overflow-x-auto;
|
||||
@apply .p-4;
|
||||
@apply .rounded;
|
||||
@apply .shadow;
|
||||
@apply .text-base;
|
||||
|
||||
code {
|
||||
@apply .bg-transparent;
|
||||
@apply .block;
|
||||
@apply .p-0;
|
||||
}
|
||||
}
|
||||
1
docs/source/_assets/sass/_documentation.scss
Normal file
|
|
@ -0,0 +1 @@
|
|||
// Add your custom styles here
|
||||
34
docs/source/_assets/sass/_navigation.scss
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
.nav-menu {
|
||||
@apply .-mt-12;
|
||||
@apply .-mx-8;
|
||||
@apply .bg-grey-lighter;
|
||||
@apply .mb-8;
|
||||
@apply .pb-4;
|
||||
@apply .pt-8;
|
||||
@apply .px-4;
|
||||
@apply .shadow;
|
||||
@apply .w-auto;
|
||||
|
||||
@screen lg {
|
||||
@apply .-mx-4;
|
||||
@apply .bg-transparent;
|
||||
@apply .block;
|
||||
@apply .border-b-0;
|
||||
@apply .mt-1;
|
||||
@apply .pl-0;
|
||||
@apply .pr-4;
|
||||
@apply .pt-0;
|
||||
@apply .shadow-none;
|
||||
@apply .w-1/4;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-menu__item {
|
||||
@apply .block;
|
||||
@apply .list-reset;
|
||||
@apply .no-underline;
|
||||
@apply .mb-3;
|
||||
@apply .mt-0;
|
||||
@apply .text-grey-darkest;
|
||||
@apply .text-sm;
|
||||
}
|
||||
76
docs/source/_assets/sass/_search.scss
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
.docsearch-input {
|
||||
background-image: url('/assets/img/magnifying-glass.svg');
|
||||
background-position: 0.8em;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: 1.2em;
|
||||
|
||||
@screen lg {
|
||||
&:focus {
|
||||
@apply .w-2/3;
|
||||
}
|
||||
}
|
||||
|
||||
@screen xl {
|
||||
&:focus {
|
||||
@apply .w-1/2;
|
||||
}
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
@apply absolute;
|
||||
@apply bg-white;
|
||||
@apply mt-7;
|
||||
@apply pin-l;
|
||||
@apply pin-t;
|
||||
@apply px-4;
|
||||
@apply w-full;
|
||||
@apply z-10;
|
||||
|
||||
@screen md {
|
||||
@apply mt-0;
|
||||
@apply px-0;
|
||||
@apply relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.algolia-autocomplete {
|
||||
@apply .text-right;
|
||||
@apply .w-full;
|
||||
|
||||
.ds-dropdown-menu {
|
||||
@apply .w-full;
|
||||
|
||||
max-width: 750px !important;
|
||||
min-width: auto !important;
|
||||
|
||||
.algolia-docsearch-suggestion {
|
||||
.algolia-docsearch-suggestion--content {
|
||||
@apply .w-full;
|
||||
|
||||
@screen md {
|
||||
@apply .w-2/3;
|
||||
}
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--text {
|
||||
@apply .font-normal;
|
||||
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--wrapper {
|
||||
@apply .py-3;
|
||||
}
|
||||
|
||||
.algolia-docsearch-suggestion--subcategory-column {
|
||||
@apply .hidden;
|
||||
|
||||
@screen md {
|
||||
@apply .w-1/3;
|
||||
@apply .inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
docs/source/_assets/sass/main.scss
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@tailwind preflight;
|
||||
@tailwind components;
|
||||
|
||||
// Code syntax highlighting,
|
||||
// powered by https://highlightjs.org
|
||||
@import '~highlight.js/styles/a11y-light.css';
|
||||
|
||||
@import 'base';
|
||||
@import 'navigation';
|
||||
@import 'documentation';
|
||||
@import 'search';
|
||||
|
||||
@tailwind utilities;
|
||||
19
docs/source/_layouts/documentation.blade.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
@extends('_layouts.master')
|
||||
|
||||
@section('nav-toggle')
|
||||
@include('_nav.menu-toggle')
|
||||
@endsection
|
||||
|
||||
@section('body')
|
||||
<section class="container max-w-4xl mx-auto px-6 md:px-8 py-4">
|
||||
<div class="flex flex-col lg:flex-row">
|
||||
<nav id="js-nav-menu" class="nav-menu hidden lg:block">
|
||||
@include('_nav.menu', ['items' => $page->navigation[$page->version()]])
|
||||
</nav>
|
||||
|
||||
<div class="w-full lg:w-3/5 break-words pb-16 lg:pl-4" v-pre>
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
104
docs/source/_layouts/master.blade.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="description" content="{{ $page->description ?? $page->siteDescription }}">
|
||||
|
||||
<meta property="og:site_name" content="{{ $page->siteName }}"/>
|
||||
<meta property="og:title" content="{{ $page->title ? $page->title . ' | ' : '' }}{{ $page->siteName }}"/>
|
||||
<meta property="og:description" content="{{ $page->description ?? $page->siteDescription }}"/>
|
||||
<meta property="og:url" content="{{ $page->getUrl() }}"/>
|
||||
<meta property="og:image" content="/assets/img/logo.png"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
|
||||
<meta name="twitter:image:alt" content="{{ $page->siteName }}">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
@if ($page->docsearchApiKey && $page->docsearchIndexName)
|
||||
<meta name="generator" content="tighten_jigsaw_doc">
|
||||
@endif
|
||||
|
||||
<title>{{ $page->title ? $page->title . ' | ' : '' }}{{ $page->siteName }}</title>
|
||||
|
||||
<link rel="home" href="{{ $page->baseUrl }}">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
|
||||
@stack('meta')
|
||||
|
||||
@if ($page->production)
|
||||
<!-- Insert analytics code here -->
|
||||
@endif
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito+Sans:300,300i,400,400i,700,700i,800,800i" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ mix('css/main.css', 'assets/build') }}">
|
||||
|
||||
@if ($page->docsearchApiKey && $page->docsearchIndexName)
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css" />
|
||||
@endif
|
||||
</head>
|
||||
|
||||
<body class="flex flex-col justify-between min-h-screen bg-grey-lightest text-grey-darkest leading-normal font-sans">
|
||||
<header class="flex items-center shadow bg-white border-b h-24 mb-8 py-4" role="banner">
|
||||
<div class="container flex items-center max-w-4xl mx-auto px-4 lg:px-8">
|
||||
<div class="flex items-center">
|
||||
<a href="/" title="{{ $page->siteName }} home" class="inline-flex items-center">
|
||||
{{-- <img class="h-8 md:h-10 mr-3" src="/assets/img/logo.svg" alt="{{ $page->siteName }} logo" /> --}}
|
||||
|
||||
<h1 class="text-lg md:text-2xl text-blue-darkest font-semibold hover:text-blue-dark my-0 pr-4">{{ $page->siteName }}</h1>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 justify-end items-center text-right md:pl-10">
|
||||
@if ($page->docsearchApiKey && $page->docsearchIndexName)
|
||||
@include('_nav.search-input')
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 justify-end items-center text-right md:pl-10">
|
||||
<select id="versionSelect">
|
||||
@foreach($page->versions as $version => $name)
|
||||
<option
|
||||
value="/{{ $page->baseUrl . $version }}"
|
||||
@if($page->version() === $version)
|
||||
selected
|
||||
@endif
|
||||
>
|
||||
{{ $name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@yield('nav-toggle')
|
||||
</header>
|
||||
|
||||
<main role="main" class="w-full flex-auto">
|
||||
@yield('body')
|
||||
</main>
|
||||
|
||||
<script src="{{ mix('js/main.js', 'assets/build') }}"></script>
|
||||
|
||||
@stack('scripts')
|
||||
|
||||
<footer class="bg-white text-center text-sm mt-12 py-4" role="contentinfo">
|
||||
<ul class="flex flex-col md:flex-row justify-center list-reset">
|
||||
<li class="md:mr-2">
|
||||
© <a href="https://github.com/stancl" title="Samuel Štancl">Samuel Štancl</a> {{ date('Y') }}.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Built with <a href="http://jigsaw.tighten.co" title="Jigsaw by Tighten">Jigsaw</a>
|
||||
and <a href="https://tailwindcss.com" title="Tailwind CSS, a utility-first CSS framework">Tailwind CSS</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
<script>
|
||||
document.getElementById('versionSelect').addEventListener('change', function () {
|
||||
window.location = document.getElementById('versionSelect').value;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
18
docs/source/_nav/menu-item.blade.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<li class="list-reset pl-4">
|
||||
@if ($url = is_string($item) ? $item : $item->url)
|
||||
{{-- Menu item with URL--}}
|
||||
<a href="{{ $page->link($url) }}"
|
||||
class="{{ 'lvl' . $level }} {{ $page->isActiveParent($item) ? 'lvl' . $level . '-active' : '' }} {{ $page->isActive($url) ? 'active font-semibold text-blue' : '' }} nav-menu__item hover:text-blue"
|
||||
>
|
||||
{{ $label }}
|
||||
</a>
|
||||
@else
|
||||
{{-- Menu item without URL--}}
|
||||
<p class="nav-menu__item text-grey-dark">{{ $label }}</p>
|
||||
@endif
|
||||
|
||||
@if (! is_string($item) && $item->children)
|
||||
{{-- Recursively handle children --}}
|
||||
@include('_nav.menu', ['items' => $item->children, 'level' => ++$level])
|
||||
@endif
|
||||
</li>
|
||||
29
docs/source/_nav/menu-toggle.blade.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<button class="flex justify-center items-center bg-blue border border-blue h-10 mr-4 px-5 rounded-full lg:hidden focus:outline-none"
|
||||
onclick="navMenu.toggle()"
|
||||
>
|
||||
<svg id="js-nav-menu-show" xmlns="http://www.w3.org/2000/svg"
|
||||
class="fill-current text-white h-9 w-4" viewBox="0 0 32 32"
|
||||
>
|
||||
<path d="M4,10h24c1.104,0,2-0.896,2-2s-0.896-2-2-2H4C2.896,6,2,6.896,2,8S2.896,10,4,10z M28,14H4c-1.104,0-2,0.896-2,2 s0.896,2,2,2h24c1.104,0,2-0.896,2-2S29.104,14,28,14z M28,22H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h24c1.104,0,2-0.896,2-2 S29.104,22,28,22z"/>
|
||||
</svg>
|
||||
|
||||
<svg id="js-nav-menu-hide" xmlns="http://www.w3.org/2000/svg"
|
||||
class="hidden fill-current text-white h-9 w-4" viewBox="0 0 36 30"
|
||||
>
|
||||
<polygon points="32.8,4.4 28.6,0.2 18,10.8 7.4,0.2 3.2,4.4 13.8,15 3.2,25.6 7.4,29.8 18,19.2 28.6,29.8 32.8,25.6 22.2,15 "/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
const navMenu = {
|
||||
toggle() {
|
||||
const menu = document.getElementById('js-nav-menu');
|
||||
menu.classList.toggle('hidden');
|
||||
menu.classList.toggle('lg:block');
|
||||
document.getElementById('js-nav-menu-hide').classList.toggle('hidden');
|
||||
document.getElementById('js-nav-menu-show').classList.toggle('hidden');
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
7
docs/source/_nav/menu.blade.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
@php $level = $level ?? 0 @endphp
|
||||
|
||||
<ul class="list-reset my-0">
|
||||
@foreach ($items as $label => $item)
|
||||
@include('_nav.menu-item')
|
||||
@endforeach
|
||||
</ul>
|
||||
47
docs/source/_nav/search-input.blade.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<button
|
||||
title="Start searching"
|
||||
type="button"
|
||||
class="flex md:hidden bg-grey-lightest hover:bg-blue-lightest justify-center items-center border border-grey rounded-full focus:outline-none h-10 px-3"
|
||||
onclick="searchInput.toggle()"
|
||||
>
|
||||
<img src="/assets/img/magnifying-glass.svg" alt="search icon" class="h-4 w-4 max-w-none">
|
||||
</button>
|
||||
|
||||
<div id="js-search-input" class="docsearch-input__wrapper hidden md:block">
|
||||
<label for="search" class="hidden">Search</label>
|
||||
|
||||
<input
|
||||
id="docsearch-input"
|
||||
class="docsearch-input relative block h-10 transition-fast w-full lg:w-1/2 xl:w-1/3 bg-grey-lightest outline-none rounded-full text-grey-darker border border-grey focus:border-blue-light ml-auto px-4 pb-0"
|
||||
name="docsearch"
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
>
|
||||
|
||||
<button
|
||||
class="md:hidden absolute pin-t pin-r h-full font-light text-3xl text-blue hover:text-blue-dark focus:outline-none -mt-px pr-7"
|
||||
onclick="searchInput.toggle()"
|
||||
>×</button>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
@if ($page->docsearchApiKey && $page->docsearchIndexName)
|
||||
<script type="text/javascript">
|
||||
docsearch({
|
||||
apiKey: '{{ $page->docsearchApiKey }}',
|
||||
indexName: '{{ $page->docsearchIndexName }}',
|
||||
inputSelector: '#docsearch-input',
|
||||
debug: false // Set debug to true if you want to inspect the dropdown
|
||||
});
|
||||
|
||||
const searchInput = {
|
||||
toggle() {
|
||||
const menu = document.getElementById('js-search-input');
|
||||
menu.classList.toggle('hidden');
|
||||
menu.classList.toggle('md:block');
|
||||
document.getElementById('docsearch-input').focus();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@endif
|
||||
@endpush
|
||||
31892
docs/source/assets/build/css/main.css
Normal file
1
docs/source/assets/build/css/main.css.map
Normal file
1036
docs/source/assets/build/js/main.js
Normal file
1
docs/source/assets/build/js/main.js.map
Normal file
4
docs/source/assets/build/mix-manifest.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"/js/main.js": "/js/main.js?id=c38a9bfc7b9c1a5210af",
|
||||
"/css/main.css": "/css/main.css?id=bfeb0e0f7abaed381134"
|
||||
}
|
||||
BIN
docs/source/assets/images/jigsaw.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/source/assets/img/docsearch.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
16
docs/source/assets/img/icon-stack.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="51px" height="44px" viewBox="0 0 51 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 7</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="04-docs-start-copy-3" transform="translate(-816.000000, -695.000000)">
|
||||
<g id="Group-7" transform="translate(816.000000, 695.000000)">
|
||||
<path d="M25.1572266,19.5799762 L1.12512399,31.5016008 L25.1572266,43.4232253 L49.1893291,31.5016008 L25.1572266,19.5799762 Z" id="Rectangle-3-Copy-3" stroke="#318AD3"></path>
|
||||
<path d="M25.1572266,10.5986098 L1.12512399,22.5202343 L25.1572266,34.4418588 L49.1893291,22.5202343 L25.1572266,10.5986098 Z" id="Rectangle-3-Copy-5" stroke="#748294"></path>
|
||||
<path d="M25.1572266,0.558141166 L1.12512399,12.4797657 L25.1572266,24.4013902 L49.1893291,12.4797657 L25.1572266,0.558141166 Z" id="Rectangle-3-Copy-6" stroke="#318AD3"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
15
docs/source/assets/img/icon-terminal.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="37px" height="34px" viewBox="0 0 37 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>terminal</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="04-docs-start-copy-3" transform="translate(-497.000000, -699.000000)">
|
||||
<g id="terminal" transform="translate(497.000000, 700.000000)">
|
||||
<polyline id="Shape" stroke="#318AD3" points="0 27 14 13.5 0 0"></polyline>
|
||||
<path d="M18,32 L36,32" id="Shape" stroke="#748294"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 856 B |
20
docs/source/assets/img/icon-window.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="38px" height="31px" viewBox="0 0 38 31" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 6</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="04-docs-start-copy-3" transform="translate(-170.000000, -702.000000)" fill-rule="nonzero">
|
||||
<g id="Group-6" transform="translate(170.000000, 702.000000)">
|
||||
<rect id="Rectangle-2" stroke="#318AD3" x="0.5" y="0.5" width="37" height="30" rx="3"></rect>
|
||||
<rect id="Rectangle-8" stroke="#318AD3" x="0.5" y="6.5" width="37" height="1"></rect>
|
||||
<rect id="Rectangle-8" stroke="#748294" x="8.5" y="23.5" width="9" height="1"></rect>
|
||||
<rect id="Rectangle-8" stroke="#748294" x="8.5" y="25.5" width="9" height="1"></rect>
|
||||
<rect id="Rectangle-8" stroke="#748294" x="21.5" y="23.5" width="9" height="1"></rect>
|
||||
<rect id="Rectangle-8" stroke="#748294" x="21.5" y="25.5" width="9" height="1"></rect>
|
||||
<rect id="Rectangle-9" stroke="#748294" x="5.5" y="11.5" width="26" height="6"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
34
docs/source/assets/img/logo-large.svg
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="318px" height="350px" viewBox="0 0 318 350" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group 2</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="-83.2348064%" y1="106.401351%" x2="140.023112%" y2="-13.2029998%" id="linearGradient-1">
|
||||
<stop stop-color="#F8FAFC" offset="0%"></stop>
|
||||
<stop stop-color="#F8FAFC" offset="21.7972142%"></stop>
|
||||
<stop stop-color="#BBDBF5" offset="100%"></stop>
|
||||
<stop stop-color="#B9DAF5" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="04-docs-start-copy-3" transform="translate(-714.000000, -205.000000)">
|
||||
<g id="Group-2" transform="translate(714.000000, 205.000000)">
|
||||
<path d="M159.9,41.23566 L29.5,106.216315 L29.5,268.671884 L159.9,333.652539 L290.3,268.671884 L290.3,106.216315 L159.9,41.23566 Z" id="Rectangle-3-Copy" stroke="#B9DAF5"></path>
|
||||
<polygon id="Rectangle-3-Copy-2" fill="url(#linearGradient-1)" points="129.8 186.334516 129.8 349.670807 0 284.336291 0 121"></polygon>
|
||||
<path d="M317.3,186.64261 L188.5,121.811441 L188.5,284.028197 L317.3,348.859366 L317.3,186.64261 Z" id="Rectangle-3-Copy-4" stroke="#338CD6" transform="translate(252.900000, 235.335404) scale(1, -1) translate(-252.900000, -235.335404) "></path>
|
||||
<path d="M159.9,41.2359725 L30.1185393,106.090062 L159.9,170.944152 L289.681461,106.090062 L159.9,41.2359725 Z" id="Rectangle-3" stroke="#B9DAF5"></path>
|
||||
<path d="M159.9,0.558953879 L30.1185393,65.4130435 L159.9,130.267133 L289.681461,65.4130435 L159.9,0.558953879 Z" id="Rectangle-3-Copy-3" stroke="#338CD6"></path>
|
||||
<path d="M160.175,170.953416 L160.175,332.562112" id="Line" stroke="#B9DAF5" stroke-linecap="square" fill-rule="nonzero"></path>
|
||||
<path d="M160.358333,130.484509 L160.358333,165.798482" id="Line-2-Copy-3" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero"></path>
|
||||
<path d="M290.158333,68.178117 L290.158333,103.49209" id="Line-2-Copy-4" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero"></path>
|
||||
<path d="M133.55,185.742236 L159.95,172.549689" id="Line-2" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero"></path>
|
||||
<path d="M133.55,348.742236 L159.95,335.549689" id="Line-2-Copy" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero"></path>
|
||||
<path d="M292.35,283.586957 L318.75,270.39441" id="Line-2" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero" transform="translate(305.550000, 276.990683) scale(1, -1) translate(-305.550000, -276.990683) "></path>
|
||||
<path d="M162.55,348.742236 L188.95,335.549689" id="Line-2-Copy-2" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero" transform="translate(175.750000, 342.145963) scale(1, -1) translate(-175.750000, -342.145963) "></path>
|
||||
<path d="M291.55,119.742236 L317.95,106.549689" id="Line-2-Copy" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero" transform="translate(304.750000, 113.145963) scale(1, -1) translate(-304.750000, -113.145963) "></path>
|
||||
<path d="M162.55,185.742236 L188.95,172.549689" id="Line-2-Copy-5" stroke="#C7CFDA" stroke-linecap="square" stroke-dasharray="2,4" fill-rule="nonzero" transform="translate(175.750000, 179.145963) scale(1, -1) translate(-175.750000, -179.145963) "></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
BIN
docs/source/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 551 B |
12
docs/source/assets/img/logo.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<svg width="40px" height="45px" viewBox="0 0 40 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="04-docs-start" transform="translate(-170.000000, -28.000000)">
|
||||
<g id="Group" transform="translate(170.000000, 28.000000)">
|
||||
<polygon id="Rectangle-3-Copy" fill="#338CD6" points="20 0 40 10 40 35 20 45 0 35 0 10"></polygon>
|
||||
<polygon id="Rectangle-3-Copy-2" fill="#1C3E5A" points="20 20 20 45 0 35 0 10"></polygon>
|
||||
<polygon id="Rectangle-3" fill="#B9DAF5" points="20 0 40 10 20 20 0 10"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 762 B |
8
docs/source/assets/img/magnifying-glass.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg width="13px" height="13px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs></defs>
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g transform="translate(-829.000000, -42.000000)" fill="#748294" fill-rule="nonzero">
|
||||
<path d="M843.319857,54.9056439 L848.707107,60.2928932 C849.097631,60.6834175 849.097631,61.3165825 848.707107,61.7071068 C848.316582,62.0976311 847.683418,62.0976311 847.292893,61.7071068 L841.905644,56.3198574 C840.55096,57.3729184 838.848711,58 837,58 C832.581722,58 829,54.418278 829,50 C829,45.581722 832.581722,42 837,42 C841.418278,42 845,45.581722 845,50 C845,51.8487115 844.372918,53.5509601 843.319857,54.9056439 Z M837,56 C840.313708,56 843,53.3137085 843,50 C843,46.6862915 840.313708,44 837,44 C833.686292,44 831,46.6862915 831,50 C831,53.3137085 833.686292,56 837,56 Z" id="Mask"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 971 B |
BIN
docs/source/favicon.ico
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
8
docs/source/index.blade.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0; url={{ $page->baseUrl . '/' . $page->defaultVersion }}">
|
||||
<title>stancl/tenancy</title>
|
||||
</head>
|
||||
</html>
|
||||
972
docs/tailwind.js
Normal file
|
|
@ -0,0 +1,972 @@
|
|||
/*
|
||||
|
||||
Tailwind - The Utility-First CSS Framework
|
||||
|
||||
A project by Adam Wathan (@adamwathan), Jonathan Reinink (@reinink),
|
||||
David Hemphill (@davidhemphill) and Steve Schoger (@steveschoger).
|
||||
|
||||
Welcome to the Tailwind config file. This is where you can customize
|
||||
Tailwind specifically for your project. Don't be intimidated by the
|
||||
length of this file. It's really just a big JavaScript object and
|
||||
we've done our very best to explain each section.
|
||||
|
||||
View the full documentation at https://tailwindcss.com.
|
||||
|
||||
|
||||
|-------------------------------------------------------------------------------
|
||||
| The default config
|
||||
|-------------------------------------------------------------------------------
|
||||
|
|
||||
| This variable contains the default Tailwind config. You don't have
|
||||
| to use it, but it can sometimes be helpful to have available. For
|
||||
| example, you may choose to merge your custom configuration
|
||||
| values with some of the Tailwind defaults.
|
||||
|
|
||||
*/
|
||||
|
||||
// let defaultConfig = require('tailwindcss/defaultConfig')()
|
||||
|
||||
|
||||
/*
|
||||
|-------------------------------------------------------------------------------
|
||||
| Colors https://tailwindcss.com/docs/colors
|
||||
|-------------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can specify the colors used in your project. To get you started,
|
||||
| we've provided a generous palette of great looking colors that are perfect
|
||||
| for prototyping, but don't hesitate to change them for your project. You
|
||||
| own these colors, nothing will break if you change everything about them.
|
||||
|
|
||||
| We've used literal color names ("red", "blue", etc.) for the default
|
||||
| palette, but if you'd rather use functional names like "primary" and
|
||||
| "secondary", or even a numeric scale like "100" and "200", go for it.
|
||||
|
|
||||
*/
|
||||
|
||||
let colors = {
|
||||
'transparent': 'transparent',
|
||||
|
||||
'black': '#1f2e41',
|
||||
'grey-darkest': '#3e4852',
|
||||
'grey-darker': '#606f7b',
|
||||
'grey-dark': '#8795a1',
|
||||
'grey': '#b8c2cc',
|
||||
'grey-light': '#e2e8ee',
|
||||
'grey-lighter': '#ecf0f3',
|
||||
'grey-lightest': '#f9f9f9',
|
||||
'white': '#ffffff',
|
||||
|
||||
'red-darkest': '#3b0d0c',
|
||||
'red-darker': '#621b18',
|
||||
'red-dark': '#cc1f1a',
|
||||
'red': '#e3342f',
|
||||
'red-light': '#ef5753',
|
||||
'red-lighter': '#f9acaa',
|
||||
'red-lightest': '#fcebea',
|
||||
|
||||
'orange-darkest': '#462a16',
|
||||
'orange-darker': '#613b1f',
|
||||
'orange-dark': '#de751f',
|
||||
'orange': '#f6993f',
|
||||
'orange-light': '#faad63',
|
||||
'orange-lighter': '#fcd9b6',
|
||||
'orange-lightest': '#fff5eb',
|
||||
|
||||
'yellow-darkest': '#453411',
|
||||
'yellow-darker': '#684f1d',
|
||||
'yellow-dark': '#f2d024',
|
||||
'yellow': '#ffed4a',
|
||||
'yellow-light': '#fff382',
|
||||
'yellow-lighter': '#fff9c2',
|
||||
'yellow-lightest': '#fcfbeb',
|
||||
|
||||
'green-darkest': '#0f2f21',
|
||||
'green-darker': '#1a4731',
|
||||
'green-dark': '#1f9d55',
|
||||
'green': '#38c172',
|
||||
'green-light': '#51d88a',
|
||||
'green-lighter': '#a2f5bf',
|
||||
'green-lightest': '#e3fcec',
|
||||
|
||||
'teal-darkest': '#0d3331',
|
||||
'teal-darker': '#20504f',
|
||||
'teal-dark': '#38a89d',
|
||||
'teal': '#4dc0b5',
|
||||
'teal-light': '#64d5ca',
|
||||
'teal-lighter': '#a0f0ed',
|
||||
'teal-lightest': '#e8fffe',
|
||||
|
||||
'blue-darkest': '#24548f',
|
||||
'blue-darker': '#1a4d8c',
|
||||
'blue-dark': '#0174d4',
|
||||
'blue': '#3490dc',
|
||||
'blue-light': '#6cb2eb',
|
||||
'blue-lighter': '#bcdefa',
|
||||
'blue-lightest': '#eff8ff',
|
||||
|
||||
'indigo-darkest': '#191e38',
|
||||
'indigo-darker': '#2f365f',
|
||||
'indigo-dark': '#5661b3',
|
||||
'indigo': '#6574cd',
|
||||
'indigo-light': '#7886d7',
|
||||
'indigo-lighter': '#b2b7ff',
|
||||
'indigo-lightest': '#e6e8ff',
|
||||
|
||||
'purple-darkest': '#21183c',
|
||||
'purple-darker': '#382b5f',
|
||||
'purple-dark': '#794acf',
|
||||
'purple': '#9561e2',
|
||||
'purple-light': '#a779e9',
|
||||
'purple-lighter': '#d6bbfc',
|
||||
'purple-lightest': '#f3ebff',
|
||||
|
||||
'pink-darkest': '#451225',
|
||||
'pink-darker': '#6f213f',
|
||||
'pink-dark': '#eb5286',
|
||||
'pink': '#f66d9b',
|
||||
'pink-light': '#fa7ea8',
|
||||
'pink-lighter': '#ffbbca',
|
||||
'pink-lightest': '#ffebef',
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Colors https://tailwindcss.com/docs/colors
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| The color palette defined above is also assigned to the "colors" key of
|
||||
| your Tailwind config. This makes it easy to access them in your CSS
|
||||
| using Tailwind's config helper. For example:
|
||||
|
|
||||
| .error { color: config('colors.red') }
|
||||
|
|
||||
*/
|
||||
|
||||
colors: colors,
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Screens https://tailwindcss.com/docs/responsive-design
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Screens in Tailwind are translated to CSS media queries. They define the
|
||||
| responsive breakpoints for your project. By default Tailwind takes a
|
||||
| "mobile first" approach, where each screen size represents a minimum
|
||||
| viewport width. Feel free to have as few or as many screens as you
|
||||
| want, naming them in whatever way you'd prefer for your project.
|
||||
|
|
||||
| Tailwind also allows for more complex screen definitions, which can be
|
||||
| useful in certain situations. Be sure to see the full responsive
|
||||
| documentation for a complete list of options.
|
||||
|
|
||||
| Class name: .{screen}:{utility}
|
||||
|
|
||||
*/
|
||||
|
||||
screens: {
|
||||
'sm': '576px',
|
||||
'md': '768px',
|
||||
'lg': '992px',
|
||||
'xl': '1200px',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Fonts https://tailwindcss.com/docs/fonts
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your project's font stack, or font families.
|
||||
| Keep in mind that Tailwind doesn't actually load any fonts for you.
|
||||
| If you're using custom fonts you'll need to import them prior to
|
||||
| defining them here.
|
||||
|
|
||||
| By default we provide a native font stack that works remarkably well on
|
||||
| any device or OS you're using, since it just uses the default fonts
|
||||
| provided by the platform.
|
||||
|
|
||||
| Class name: .font-{name}
|
||||
|
|
||||
*/
|
||||
|
||||
fonts: {
|
||||
'sans': [
|
||||
'Nunito Sans',
|
||||
'system-ui',
|
||||
'BlinkMacSystemFont',
|
||||
'-apple-system',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Oxygen',
|
||||
'Ubuntu',
|
||||
'Cantarell',
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
'sans-serif',
|
||||
],
|
||||
'serif': [
|
||||
'Constantia',
|
||||
'Lucida Bright',
|
||||
'Lucidabright',
|
||||
'Lucida Serif',
|
||||
'Lucida',
|
||||
'DejaVu Serif',
|
||||
'Bitstream Vera Serif',
|
||||
'Liberation Serif',
|
||||
'Georgia',
|
||||
'serif',
|
||||
],
|
||||
'mono': [
|
||||
'Menlo',
|
||||
'Monaco',
|
||||
'Consolas',
|
||||
'Liberation Mono',
|
||||
'Courier New',
|
||||
'monospace',
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Text sizes https://tailwindcss.com/docs/text-sizing
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your text sizes. Name these in whatever way
|
||||
| makes the most sense to you. We use size names by default, but
|
||||
| you're welcome to use a numeric scale or even something else
|
||||
| entirely.
|
||||
|
|
||||
| By default Tailwind uses the "rem" unit type for most measurements.
|
||||
| This allows you to set a root font size which all other sizes are
|
||||
| then based on. That said, you are free to use whatever units you
|
||||
| prefer, be it rems, ems, pixels or other.
|
||||
|
|
||||
| Class name: .text-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
textSizes: {
|
||||
'xs': '.8rem',
|
||||
'sm': '.925rem',
|
||||
'base': '1rem',
|
||||
'lg': '1.125rem',
|
||||
'xl': '1.25rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '1.75rem',
|
||||
'4xl': '2.125rem',
|
||||
'5xl': '2.625rem',
|
||||
'6xl': '10rem',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Font weights https://tailwindcss.com/docs/font-weight
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your font weights. We've provided a list of
|
||||
| common font weight names with their respective numeric scale values
|
||||
| to get you started. It's unlikely that your project will require
|
||||
| all of these, so we recommend removing those you don't need.
|
||||
|
|
||||
| Class name: .font-{weight}
|
||||
|
|
||||
*/
|
||||
|
||||
fontWeights: {
|
||||
'hairline': 100,
|
||||
'thin': 200,
|
||||
'light': 300,
|
||||
'normal': 400,
|
||||
'medium': 500,
|
||||
'semibold': 600,
|
||||
'bold': 700,
|
||||
'extrabold': 800,
|
||||
'black': 900,
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Leading (line height) https://tailwindcss.com/docs/line-height
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your line height values, or as we call
|
||||
| them in Tailwind, leadings.
|
||||
|
|
||||
| Class name: .leading-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
leading: {
|
||||
'none': 1,
|
||||
'tight': 1.25,
|
||||
'normal': 1.6,
|
||||
'loose': 1.75,
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Tracking (letter spacing) https://tailwindcss.com/docs/letter-spacing
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your letter spacing values, or as we call
|
||||
| them in Tailwind, tracking.
|
||||
|
|
||||
| Class name: .tracking-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
tracking: {
|
||||
'tight': '-0.05em',
|
||||
'normal': '0',
|
||||
'wide': '0.05em',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Text colors https://tailwindcss.com/docs/text-color
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your text colors. By default these use the
|
||||
| color palette we defined above, however you're welcome to set these
|
||||
| independently if that makes sense for your project.
|
||||
|
|
||||
| Class name: .text-{color}
|
||||
|
|
||||
*/
|
||||
|
||||
textColors: colors,
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Background colors https://tailwindcss.com/docs/background-color
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your background colors. By default these use
|
||||
| the color palette we defined above, however you're welcome to set
|
||||
| these independently if that makes sense for your project.
|
||||
|
|
||||
| Class name: .bg-{color}
|
||||
|
|
||||
*/
|
||||
|
||||
backgroundColors: colors,
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Background sizes https://tailwindcss.com/docs/background-size
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your background sizes. We provide some common
|
||||
| values that are useful in most projects, but feel free to add other sizes
|
||||
| that are specific to your project here as well.
|
||||
|
|
||||
| Class name: .bg-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
backgroundSize: {
|
||||
'auto': 'auto',
|
||||
'cover': 'cover',
|
||||
'contain': 'contain',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Border widths https://tailwindcss.com/docs/border-width
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your border widths. Take note that border
|
||||
| widths require a special "default" value set as well. This is the
|
||||
| width that will be used when you do not specify a border width.
|
||||
|
|
||||
| Class name: .border{-side?}{-width?}
|
||||
|
|
||||
*/
|
||||
|
||||
borderWidths: {
|
||||
default: '1px',
|
||||
'0': '0',
|
||||
'2': '2px',
|
||||
'4': '4px',
|
||||
'8': '8px',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Border colors https://tailwindcss.com/docs/border-color
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your border colors. By default these use the
|
||||
| color palette we defined above, however you're welcome to set these
|
||||
| independently if that makes sense for your project.
|
||||
|
|
||||
| Take note that border colors require a special "default" value set
|
||||
| as well. This is the color that will be used when you do not
|
||||
| specify a border color.
|
||||
|
|
||||
| Class name: .border-{color}
|
||||
|
|
||||
*/
|
||||
|
||||
borderColors: global.Object.assign({ default: colors['grey-light'] }, colors),
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Border radius https://tailwindcss.com/docs/border-radius
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your border radius values. If a `default` radius
|
||||
| is provided, it will be made available as the non-suffixed `.rounded`
|
||||
| utility.
|
||||
|
|
||||
| If your scale includes a `0` value to reset already rounded corners, it's
|
||||
| a good idea to put it first so other values are able to override it.
|
||||
|
|
||||
| Class name: .rounded{-side?}{-size?}
|
||||
|
|
||||
*/
|
||||
|
||||
borderRadius: {
|
||||
'none': '0',
|
||||
'sm': '.125rem',
|
||||
default: '.25rem',
|
||||
'lg': '.5rem',
|
||||
'full': '9999px',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Width https://tailwindcss.com/docs/width
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your width utility sizes. These can be
|
||||
| percentage based, pixels, rems, or any other units. By default
|
||||
| we provide a sensible rem based numeric scale, a percentage
|
||||
| based fraction scale, plus some other common use-cases. You
|
||||
| can, of course, modify these values as needed.
|
||||
|
|
||||
|
|
||||
| It's also worth mentioning that Tailwind automatically escapes
|
||||
| invalid CSS class name characters, which allows you to have
|
||||
| awesome classes like .w-2/3.
|
||||
|
|
||||
| Class name: .w-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
width: {
|
||||
'auto': 'auto',
|
||||
'px': '1px',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'8': '2rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
'48': '12rem',
|
||||
'64': '16rem',
|
||||
'1/2': '50%',
|
||||
'1/3': '33.33333%',
|
||||
'2/3': '66.66667%',
|
||||
'1/4': '25%',
|
||||
'3/4': '75%',
|
||||
'1/5': '20%',
|
||||
'2/5': '40%',
|
||||
'3/5': '60%',
|
||||
'4/5': '80%',
|
||||
'1/6': '16.66667%',
|
||||
'5/6': '83.33333%',
|
||||
'full': '100%',
|
||||
'screen': '100vw'
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Height https://tailwindcss.com/docs/height
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your height utility sizes. These can be
|
||||
| percentage based, pixels, rems, or any other units. By default
|
||||
| we provide a sensible rem based numeric scale plus some other
|
||||
| common use-cases. You can, of course, modify these values as
|
||||
| needed.
|
||||
|
|
||||
| Class name: .h-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
height: {
|
||||
'auto': 'auto',
|
||||
'px': '1px',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'8': '2rem',
|
||||
'9': '2.2rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
'48': '12rem',
|
||||
'64': '16rem',
|
||||
'full': '100%',
|
||||
'screen': '100vh'
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Minimum width https://tailwindcss.com/docs/min-width
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your minimum width utility sizes. These can
|
||||
| be percentage based, pixels, rems, or any other units. We provide a
|
||||
| couple common use-cases by default. You can, of course, modify
|
||||
| these values as needed.
|
||||
|
|
||||
| Class name: .min-w-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
minWidth: {
|
||||
'0': '0',
|
||||
'full': '100%',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Minimum height https://tailwindcss.com/docs/min-height
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your minimum height utility sizes. These can
|
||||
| be percentage based, pixels, rems, or any other units. We provide a
|
||||
| few common use-cases by default. You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .min-h-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
minHeight: {
|
||||
'0': '0',
|
||||
'full': '100%',
|
||||
'screen': '100vh'
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Maximum width https://tailwindcss.com/docs/max-width
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your maximum width utility sizes. These can
|
||||
| be percentage based, pixels, rems, or any other units. By default
|
||||
| we provide a sensible rem based scale and a "full width" size,
|
||||
| which is basically a reset utility. You can, of course,
|
||||
| modify these values as needed.
|
||||
|
|
||||
| Class name: .max-w-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
maxWidth: {
|
||||
'xs': '20rem',
|
||||
'sm': '30rem',
|
||||
'md': '40rem',
|
||||
'lg': '50rem',
|
||||
'xl': '60rem',
|
||||
'2xl': '70rem',
|
||||
'3xl': '80rem',
|
||||
'4xl': '90rem',
|
||||
'5xl': '100rem',
|
||||
'full': '100%',
|
||||
'none': 'none',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Maximum height https://tailwindcss.com/docs/max-height
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your maximum height utility sizes. These can
|
||||
| be percentage based, pixels, rems, or any other units. We provide a
|
||||
| couple common use-cases by default. You can, of course, modify
|
||||
| these values as needed.
|
||||
|
|
||||
| Class name: .max-h-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
maxHeight: {
|
||||
'full': '100%',
|
||||
'screen': '100vh',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Padding https://tailwindcss.com/docs/padding
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your padding utility sizes. These can be
|
||||
| percentage based, pixels, rems, or any other units. By default we
|
||||
| provide a sensible rem based numeric scale plus a couple other
|
||||
| common use-cases like "1px". You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .p{side?}-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
padding: {
|
||||
'px': '1px',
|
||||
'0': '0',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'7': '1.75rem',
|
||||
'8': '2rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'20': '5rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Margin https://tailwindcss.com/docs/margin
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your margin utility sizes. These can be
|
||||
| percentage based, pixels, rems, or any other units. By default we
|
||||
| provide a sensible rem based numeric scale plus a couple other
|
||||
| common use-cases like "1px". You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .m{side?}-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
margin: {
|
||||
'auto': 'auto',
|
||||
'px': '1px',
|
||||
'0': '0',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'7': '1.75rem',
|
||||
'8': '2rem',
|
||||
'9': '2.25rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'20': '5rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Negative margin https://tailwindcss.com/docs/negative-margin
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your negative margin utility sizes. These can
|
||||
| be percentage based, pixels, rems, or any other units. By default we
|
||||
| provide matching values to the padding scale since these utilities
|
||||
| generally get used together. You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .-m{side?}-{size}
|
||||
|
|
||||
*/
|
||||
|
||||
negativeMargin: {
|
||||
'px': '1px',
|
||||
'0': '0',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'8': '2rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'20': '5rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Shadows https://tailwindcss.com/docs/shadows
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your shadow utilities. As you can see from
|
||||
| the defaults we provide, it's possible to apply multiple shadows
|
||||
| per utility using comma separation.
|
||||
|
|
||||
| If a `default` shadow is provided, it will be made available as the non-
|
||||
| suffixed `.shadow` utility.
|
||||
|
|
||||
| Class name: .shadow-{size?}
|
||||
|
|
||||
*/
|
||||
|
||||
shadows: {
|
||||
default: '0 2px 4px 0 rgba(0,0,0,0.10)',
|
||||
'md': '0 4px 8px 0 rgba(0,0,0,0.12), 0 2px 4px 0 rgba(0,0,0,0.08)',
|
||||
'lg': '0 -1px 27px 0 rgba(0, 0, 0, 0.04), 0 4px 15px 0 rgba(0, 0, 0, 0.08)',
|
||||
'inner': 'inset 0 2px 4px 0 rgba(0,0,0,0.06)',
|
||||
'outline': '0 0 0 3px rgba(52,144,220,0.5)',
|
||||
'none': 'none',
|
||||
},
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Z-index https://tailwindcss.com/docs/z-index
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your z-index utility values. By default we
|
||||
| provide a sensible numeric scale. You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .z-{index}
|
||||
|
|
||||
*/
|
||||
|
||||
zIndex: {
|
||||
'auto': 'auto',
|
||||
'0': 0,
|
||||
'10': 10,
|
||||
'20': 20,
|
||||
'30': 30,
|
||||
'40': 40,
|
||||
'50': 50,
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Opacity https://tailwindcss.com/docs/opacity
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your opacity utility values. By default we
|
||||
| provide a sensible numeric scale. You can, of course, modify these
|
||||
| values as needed.
|
||||
|
|
||||
| Class name: .opacity-{name}
|
||||
|
|
||||
*/
|
||||
|
||||
opacity: {
|
||||
'0': '0',
|
||||
'25': '.25',
|
||||
'50': '.5',
|
||||
'75': '.75',
|
||||
'100': '1',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| SVG fill https://tailwindcss.com/docs/svg
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your SVG fill colors. By default we just provide
|
||||
| `fill-current` which sets the fill to the current text color. This lets you
|
||||
| specify a fill color using existing text color utilities and helps keep the
|
||||
| generated CSS file size down.
|
||||
|
|
||||
| Class name: .fill-{name}
|
||||
|
|
||||
*/
|
||||
|
||||
svgFill: {
|
||||
'current': 'currentColor',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| SVG stroke https://tailwindcss.com/docs/svg
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you define your SVG stroke colors. By default we just provide
|
||||
| `stroke-current` which sets the stroke to the current text color. This lets
|
||||
| you specify a stroke color using existing text color utilities and helps
|
||||
| keep the generated CSS file size down.
|
||||
|
|
||||
| Class name: .stroke-{name}
|
||||
|
|
||||
*/
|
||||
|
||||
svgStroke: {
|
||||
'current': 'currentColor',
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Modules https://tailwindcss.com/docs/configuration#modules
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you control which modules are generated and what variants are
|
||||
| generated for each of those modules.
|
||||
|
|
||||
| Currently supported variants:
|
||||
| - responsive
|
||||
| - hover
|
||||
| - focus
|
||||
| - active
|
||||
| - group-hover
|
||||
|
|
||||
| To disable a module completely, use `false` instead of an array.
|
||||
|
|
||||
*/
|
||||
|
||||
modules: {
|
||||
appearance: ['responsive'],
|
||||
backgroundAttachment: ['responsive'],
|
||||
backgroundColors: ['responsive', 'hover', 'focus'],
|
||||
backgroundPosition: ['responsive'],
|
||||
backgroundRepeat: ['responsive'],
|
||||
backgroundSize: ['responsive'],
|
||||
borderCollapse: [],
|
||||
borderColors: ['responsive', 'hover', 'focus'],
|
||||
borderRadius: ['responsive', 'focus'],
|
||||
borderStyle: ['responsive'],
|
||||
borderWidths: ['responsive', 'active', 'focus'],
|
||||
cursor: ['responsive'],
|
||||
display: ['responsive'],
|
||||
flexbox: ['responsive'],
|
||||
float: ['responsive'],
|
||||
fonts: ['responsive'],
|
||||
fontWeights: ['responsive', 'hover', 'focus'],
|
||||
height: ['responsive'],
|
||||
leading: ['responsive'],
|
||||
lists: ['responsive'],
|
||||
margin: ['responsive'],
|
||||
maxHeight: ['responsive'],
|
||||
maxWidth: ['responsive'],
|
||||
minHeight: ['responsive'],
|
||||
minWidth: ['responsive'],
|
||||
negativeMargin: ['responsive'],
|
||||
objectFit: false,
|
||||
objectPosition: false,
|
||||
opacity: ['responsive'],
|
||||
outline: ['focus'],
|
||||
overflow: ['responsive'],
|
||||
padding: ['responsive'],
|
||||
pointerEvents: ['responsive'],
|
||||
position: ['responsive'],
|
||||
resize: ['responsive'],
|
||||
shadows: ['responsive', 'hover', 'focus'],
|
||||
svgFill: [],
|
||||
svgStroke: [],
|
||||
tableLayout: ['responsive'],
|
||||
textAlign: ['responsive'],
|
||||
textColors: ['responsive', 'hover', 'focus'],
|
||||
textSizes: ['responsive'],
|
||||
textStyle: ['responsive', 'hover', 'focus'],
|
||||
tracking: ['responsive'],
|
||||
userSelect: ['responsive'],
|
||||
verticalAlign: ['responsive'],
|
||||
visibility: ['responsive'],
|
||||
whitespace: ['responsive'],
|
||||
width: ['responsive', 'focus'],
|
||||
zIndex: ['responsive'],
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Plugins https://tailwindcss.com/docs/plugins
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register any plugins you'd like to use in your
|
||||
| project. Tailwind's built-in `container` plugin is enabled by default to
|
||||
| give you a Bootstrap-style responsive container component out of the box.
|
||||
|
|
||||
| Be sure to view the complete plugin documentation to learn more about how
|
||||
| the plugin system works.
|
||||
|
|
||||
*/
|
||||
|
||||
plugins: [
|
||||
require('tailwindcss/plugins/container')({
|
||||
// center: true,
|
||||
// padding: '1rem',
|
||||
}),
|
||||
function({ addUtilities }) {
|
||||
const newUtilities = {
|
||||
'.transition-fast': {
|
||||
transition: 'all .2s ease-out',
|
||||
},
|
||||
'.transition': {
|
||||
transition: 'all .5s ease-out',
|
||||
},
|
||||
}
|
||||
|
||||
addUtilities(newUtilities)
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
/*
|
||||
|-----------------------------------------------------------------------------
|
||||
| Advanced Options https://tailwindcss.com/docs/configuration#options
|
||||
|-----------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can tweak advanced configuration options. We recommend
|
||||
| leaving these options alone unless you absolutely need to change them.
|
||||
|
|
||||
*/
|
||||
|
||||
options: {
|
||||
prefix: '',
|
||||
important: false,
|
||||
separator: ':',
|
||||
},
|
||||
|
||||
}
|
||||
19
docs/tasks/bin.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
var hasbin = require('hasbin');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
path: function() {
|
||||
if (fs.existsSync('./vendor/bin/jigsaw')) {
|
||||
return path.normalize('./vendor/bin/jigsaw')
|
||||
}
|
||||
|
||||
if (hasbin.sync('jigsaw')) {
|
||||
return 'jigsaw';
|
||||
}
|
||||
|
||||
console.log('Could not find Jigsaw; please install it via Composer, either locally or globally.');
|
||||
|
||||
process.exit();
|
||||
}
|
||||
};
|
||||