Appearance
Tenancy
The library can keep every tenant's files apart. Folders, files and tags carry a tenant, uploads are stamped with it, and every query is filtered by it. Tenancy is off until a panel with tenancy, or your config, turns it on.
Filament panel tenancy
Nothing to configure. When the panel uses ->tenant(Team::class), the plugin turns tenancy on and resolves the tenant with Filament::getTenant().
php
use Hoceineel\FilamentMediaLibrary\FilamentMediaLibraryPlugin;
public function panel(Panel $panel): Panel
{
return $panel
->tenant(Team::class)
->plugin(FilamentMediaLibraryPlugin::make());
}Everything is scoped to the current tenant:
| What | How it is scoped |
|---|---|
| Files, folders and tags | A tenant_type and tenant_id column on each table, filtered by a global scope. |
| New records | Stamped with the current tenant when they are created. |
| Upload and download routes | Registered as tenant routes, so the tenant is in the URL, for example /studio/media-library/upload. |
| Picker validation | The picker field rejects ids that belong to another tenant. |
| Attachments | syncMediaLibraryItems() drops ids from another tenant. |
| Rich editor | The rich editor plugin never inserts another tenant's files. |
| Quota | Counted per tenant. See below. |
In strict mode, which is on for Filament tenancy, a request with no resolved tenant sees nothing at all.
stancl/tenancy, single database
Store every tenant in one database with a tenant_id column, and resolve the tenant from stancl:
php
use Hoceineel\FilamentMediaLibrary\Tenancy\StanclTenantResolver;
FilamentMediaLibraryPlugin::make()
->tenancy()
->resolveTenantUsing(StanclTenantResolver::class);StanclTenantResolver calls stancl's tenant() helper. It returns the tenant model, or null when stancl has not been initialised for the request.
WARNING
On a panel without Filament tenancy, the plugin's strict argument has no effect: the library does not hide files when no tenant resolves. Make sure stancl's tenancy middleware runs on the panel's routes. For pages outside panels, use the tenancy.strict config key instead.
stancl/tenancy, database per tenant
The library tables live in each tenant's database, so the database already isolates tenants.
- Add the package migration to your tenant migrations.
- Keep
tenancy.enabledset tofalse. - Enable stancl's
FilesystemTenancyBootstrapper, so each tenant writes to its own disk path.
Custom resolver
Pass a closure, a class name or a TenantResolver instance to resolveTenantUsing():
php
FilamentMediaLibraryPlugin::make()
->tenancy()
->resolveTenantUsing(fn () => auth()->user()?->organization);A resolver class implements one method and returns an Eloquent model, or null:
php
use Hoceineel\FilamentMediaLibrary\Tenancy\TenantResolver;
use Illuminate\Database\Eloquent\Model;
class OrganizationResolver implements TenantResolver
{
public function resolve(): ?Model
{
return auth()->user()?->organization;
}
}The tenant is stored as a morph (tenant_type and tenant_id), so it can be any model. The tenant_id column type follows key_types.tenant in the config. Set it to uuid, ulid or string before you run the migration if your tenants do not use integer keys.
Strict mode
| Strict | When no tenant resolves |
|---|---|
| on | Every query returns nothing. Safe by default. |
| off | The tenant filter is skipped, so the library shows every record. |
Leave strict mode on unless you know why you need otherwise. A missing tenant then shows an empty library, not another tenant's files.
Standalone pages
Pages outside panels have no panel tenant. Turn tenancy on in config/filament-media-library.php and give it a resolver:
php
'tenancy' => [
'enabled' => true,
'resolver' => App\Support\CurrentTeamResolver::class,
'strict' => true,
],| Key | Default | Meaning |
|---|---|---|
enabled | false | Scope the library to a tenant outside panels. |
resolver | FilamentTenantResolver | A class implementing TenantResolver. |
strict | true | Hide everything when no tenant resolves. |
The default resolver reads the Filament panel tenant, which does not exist outside a panel, so always set your own. Panels with tenancy keep using their own tenant, whatever this config says. See Outside Panels.
Quotas
Limit storage per tenant, or for the whole app when tenancy is off:
php
'quota' => 5 * 1024 ** 3,
'quota' => PlanQuota::class,The value is a number of bytes, null for unlimited, or an invokable class that receives the current tenant and returns a byte limit, or null.
php
use Illuminate\Database\Eloquent\Model;
class PlanQuota
{
public function __invoke(?Model $tenant): ?int
{
return $tenant?->plan === 'pro' ? 50 * 1024 ** 3 : 5 * 1024 ** 3;
}
}Used space counts every file the tenant has, including files in the trash and previous versions. An upload that does not fit is refused. For chunked uploads the check runs when the first chunk arrives, before the file is stored. The library sidebar shows the storage used.
Private files
Tenancy separates tenants. Private files separate people inside a tenant.
A user can mark a file or folder as Only me. Private records are visible only to their uploader, and to users who pass the viewPrivate policy ability, for example admins. Turn the feature off with private_media => false. See Private Files and Authorization.