Service Overrides

Service overrides all about making services tenant-aware, whether they're part of Laravel's core, or a third-party package.

The documentation is still in progress, and this page is not yet complete. Please check back again in the future.

Introduction

Your application is made up of many "services", whether they're things like file storage, sessions, authentication, or one of the many others provided by Laravel. Sometimes these services aren't part of the core Laravel installation, things like Fortify, Livewire, Inertia or Filament. Whatever they are, they aren't going to understand your application out of the box, and they're definitely not going to support your multitenancy functionality.

This is where the service override comes in. Service overrides are similar to service providers, except they're specific to the lifecycle of Sprout, as well as that of a tenant. They are called when the current tenant changes, allowing them to set up for the new tenant, as well as clean up after the previous. They can also optionally be bootable, which means they have actions to perform once Laravel has booted.

Configuring Overrides

Service overrides are configured in the sprout.overrides config again the "service" they override, with their class as the driver, and any extra configuration options required or allowed by the driver itself. The order that the overrides appear in the sprout.overrides config is the order that they'll be registered in, and the order they'll be called in.

There are no specific rules surrounding the name that a service override is registered under, but it is recommended that you avoid changing them.

Stacking Overrides

There are times when you'll need more than one service override for a service, and rather than having to register each under a different name, Sprout supports stacking. If you need to stack, you can set the driver to \Sprout\Overrides\StackedOverride, then provide the drivers you want to stack under the overrides config key. If either driver requires config options, you can provide them normally.

1'filesystem' => [
2 'driver' => \Sprout\Overrides\StackedOverride::class,
3 'overrides' => [
4 \Sprout\Overrides\FilesystemManagerOverride::class,
5 \Sprout\Overrides\FilesystemOverride::class,
6 ],
7],

There are two menu uses for stacking.

  • The override for a particular service has been broken into smaller separate parts, and can be used independently of the others.
  • You have multiple overrides from different sources, and want to use all of them.

Per Tenancy

Once a service override is registered with Sprout, it is available for use. By default, no service override is used unless the tenancy is configured to use it. You can do this using tenancy options, by either enabling all overrides for the tenancy, which is what the default config does.

1'tenants' => [
2 'provider' => 'tenants',
3 'options' => [
4 TenancyOptions::allOverrides(),
5 ],
6],

Or by listing only the ones you want.

1'tenants' => [
2 'provider' => 'tenants',
3 'options' => [
4 TenancyOptions::overrides([
5 'job', 'filesystem', 'cache'
6 ]),
7 ],
8],

While you can enable or disable service overrides on a per tenancy basic, any that are bootable will be booted regardless, as that happens before the tenancy is identified.

Setting and Cleaning Up

Service overrides are built around the concept of setting the override up for the tenant when it becomes the current one, and cleaning up once it no longer is. While this is a core part of this feature, both the setting and cleaning up are controlled by tenancy bootstrappers.

The \Sprout\Listeners\CleanupServiceOverrides bootstrapper is responsible for making the service overrides clean-up after themselves, and the \Sprout\Listeners\SetupServiceOverrides bootstrapper is responsible for the setup.

Both the order that these bootstrappers appear in the config, and their presence in it is important for the service override functionality. Removing either, or changing their order relative to each other, will have unknown side effects and may cause your application to not function properly.

Available Service Overrides

Sprout ships with several overrides for core parts of Laravel, all of which are enabled by default.

Filesystem

The filesystem override comes in two parts. The first replace Laravel's filesystem manager with Sprouts, which only exists to simplify the second, which adds a sprout driver that allows you to create tenant scoped filesystem disks.

1'filesystem' => [
2 'driver' => \Sprout\Overrides\StackedOverride::class,
3 'overrides' => [
4 \Sprout\Overrides\FilesystemManagerOverride::class,
5 \Sprout\Overrides\FilesystemOverride::class,
6 ],
7],

This service override requires that the tenant is configured for resources, and will throw an exception if it isn't.

Filesystem Manager

The filesystem manager service override does only a single thing, and that is adding the name of the disk to the disk config as name, when calling a custom driver. This is primarily of use to the other override, as it keeps track of the tenant disks that were accessed, and uses that to perform the clean-up.

Filesystem

This service override is the one responsible for adding the sprout driver to the filesystem service. The new driver allows you to create a filesystem disk scoped to the current tenant, either based on an existing disk, or using new settings.

To use this override, create a filesystem disk in config/filesystems.php under disks, whose driver is sprout. Then under the disk option, add either the name of an existing disk.

1'tenant-disk' => [
2 'driver' => 'sprout',
3 'disk' => 'local',
4],

This override uses the config from the other disk, and then scopes it, so you can use both the sprout one, and the base one simultaneously. Just be aware that the base one will have access to all data for all tenants.

Or the config for a new disk.

1'tenant-disk' => [
2 'driver' => 'sprout',
3 'disk' => [
4 'driver' => 'local',
5 'root' => storage_path('tenants'),
6 'throw' => false,
7 ],
8],

When the disk is created, its root will be set to {tenancy}/{tenant}, where tenancy is the name of the tenancy from the multitenancy.tenancies config, and tenant is replaced with its resource key. If you want to change this, you can provide the path config option for the disk, which uses the same placeholders as the identity resolver parameter names, except that tenant uses the resource key, and not identifier.

1'tenant-disk' => [
2 'driver' => 'sprout',
3 'disk' => 'local',
4 'path' => '/{tenancy}/files/{tenant}'
5],

Sprout will not take steps to ensure that the disk path exists. This is something that you will need to do as part of your applications lifecycle whenever a new tenant is created.

Job

The job service override is arguably the simplest of the defaults. All it does is register an event listener for a job event that makes sure that whenever a job is run, it has the same tenancies and tenants available as it did when it was first queued.

1'job' => [
2 'driver' => \Sprout\Overrides\JobOverride::class,
3],

This is registered as job because it technically does do anything with the queue, and naming it something like that may get in the way or cause confusion.

Cache

Auth

Session