PHPackages                             sitesource/laravel-polymorphic-settings - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. sitesource/laravel-polymorphic-settings

ActiveLibrary[Database &amp; ORM](/categories/database)

sitesource/laravel-polymorphic-settings
=======================================

Polymorphic, per-model key-value settings for Laravel with opt-in encryption and transparent caching.

v0.1.3(1mo ago)0138[1 issues](https://github.com/sitesource/laravel-polymorphic-settings/issues)1MITPHPPHP ^8.3||^8.4||^8.5CI passing

Since Apr 23Pushed 1mo agoCompare

[ Source](https://github.com/sitesource/laravel-polymorphic-settings)[ Packagist](https://packagist.org/packages/sitesource/laravel-polymorphic-settings)[ Docs](https://github.com/sitesource/laravel-polymorphic-settings)[ GitHub Sponsors](https://github.com/SiteSource)[ RSS](/packages/sitesource-laravel-polymorphic-settings/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (12)Versions (5)Used By (1)

Polymorphic, per-model key-value settings for Laravel
=====================================================

[](#polymorphic-per-model-key-value-settings-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b43a887b209c063cb08005224c11e60c72c63e4fcdd648afaea159b9afb7c112/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73697465736f757263652f6c61726176656c2d706f6c796d6f72706869632d73657474696e67732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sitesource/laravel-polymorphic-settings)[![GitHub Tests Action Status](https://camo.githubusercontent.com/563d044f5171950ee508ae0063019cd170d292df9b6016631a62af08354aa10a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f73697465736f757263652f6c61726176656c2d706f6c796d6f72706869632d73657474696e67732f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/sitesource/laravel-polymorphic-settings/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/654e085b81aa7f6a2da185f38af82d5da11ea155f2c37b8140e662e9768fdc31/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f73697465736f757263652f6c61726176656c2d706f6c796d6f72706869632d73657474696e67732f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/sitesource/laravel-polymorphic-settings/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/6dd5de5429b7970baa9f7e6f08d3a0a344c0cb6298b8fdc038eee43d68df3125/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f73697465736f757263652f6c61726176656c2d706f6c796d6f72706869632d73657474696e67732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/sitesource/laravel-polymorphic-settings)

Store settings that belong to **any model** — a team, a user, a tenant, or nothing at all — with opt-in encryption per key and transparent caching.

```
use SiteSource\PolymorphicSettings\Facades\PolymorphicSettings;

PolymorphicSettings::global()->put('site_title', 'Acme');
PolymorphicSettings::for($team)->put('theme', ['mode' => 'dark']);
PolymorphicSettings::for($user)->put('api_key', $secret, encrypted: true);

PolymorphicSettings::for($team)->get('theme.mode');   // 'dark'
$team->settings()->int('max_members', 25);            // typed getters
```

Why another settings package?
-----------------------------

[](#why-another-settings-package)

Most Laravel settings packages are **class-based and global**: you define a `GeneralSettings` class with typed properties, access `app(GeneralSettings::class)->site_name`. Great when you have a fixed schema and one set of values for your whole app.

This package is **key-based and polymorphic**: you attach settings to any Eloquent model at runtime. Per-team themes, per-user preferences, per-tenant feature flags, and plain old global settings all live in one table, all accessed through one fluent API.

Want…Reach forStrongly-typed, class-based global settings[`spatie/laravel-settings`](https://github.com/spatie/laravel-settings)Runtime-scoped settings per team/user/tenant, plus encryption**this package**Installation
------------

[](#installation)

```
composer require sitesource/laravel-polymorphic-settings
```

Run the installer — it detects your User model's primary key type, publishes the config, publishes the migration, and migrates. Non-interactive runs (CI, `--no-interaction`) use detected defaults:

```
php artisan polymorphic-settings:install
```

Or do each step manually:

```
php artisan vendor:publish --tag="polymorphic-settings-config"
php artisan vendor:publish --tag="polymorphic-settings-migrations"
php artisan migrate
```

Usage
-----

[](#usage)

### Global settings

[](#global-settings)

```
use SiteSource\PolymorphicSettings\Facades\PolymorphicSettings;

PolymorphicSettings::global()->put('site_title', 'Acme');
PolymorphicSettings::global()->get('site_title');   // 'Acme'
```

### Scoped to any Eloquent model

[](#scoped-to-any-eloquent-model)

```
PolymorphicSettings::for($team)->put('theme', 'dark');
PolymorphicSettings::for($team)->get('theme');      // 'dark'

// Same key, different scopes — fully isolated
PolymorphicSettings::for($userA)->put('locale', 'en-US');
PolymorphicSettings::for($userB)->put('locale', 'fr-FR');
```

### Via the `HasSettings` trait

[](#via-the-hassettings-trait)

Clean up calling sites by adding the trait to your models:

```
use SiteSource\PolymorphicSettings\Concerns\HasSettings;

class Team extends Model
{
    use HasSettings;
}

$team->settings()->put('theme', 'dark');
$team->settings()->get('theme');          // 'dark'

// Eager load to avoid N+1
Team::with('scopedSettings')->get();
```

### Opt-in cascade delete

[](#opt-in-cascade-delete)

Settings survive when their parent model is deleted by default. If you'd rather have them cleaned up:

```
class Team extends Model
{
    use HasSettings;

    public bool $cascadeDeleteSettings = true;
}

$team->delete();    // settings for this team are purged
```

`SoftDeletes` is respected — soft deletes **don't** cascade (so `restore()` works), only `forceDelete()` triggers the cleanup.

### Encryption

[](#encryption)

Per-key, opt-in via a named argument:

```
$user->settings()->put('api_key', 'sk-live-abc', encrypted: true);
$user->settings()->get('api_key');        // 'sk-live-abc' (transparently decrypted)
```

Encryption is transparent — `all()`, `getMany()`, typed getters and dot-notation reads all see the plaintext. The raw DB value never contains the plaintext.

You can promote a plain setting to encrypted (and vice-versa) just by re-putting with a different `encrypted:` argument.

### Bulk operations

[](#bulk-operations)

```
$team->settings()->putMany([
    'theme' => 'dark',
    'locale' => 'en-US',
    'timezone' => 'UTC',
]);

$team->settings()->getMany(['theme', 'locale', 'missing']);
// ['theme' => 'dark', 'locale' => 'en-US', 'missing' => null]

$team->settings()->all();               // every setting in this scope
$team->settings()->forgetAll();         // nuke every setting in this scope
```

### Dot-notation reads

[](#dot-notation-reads)

```
$team->settings()->put('theme', [
    'palette' => ['primary' => ['hex' => '#abc123']],
]);

$team->settings()->get('theme.palette.primary.hex');   // '#abc123'
```

Dot-notation is a **read-time convenience** for walking into an array value. Keys are always stored literally — `put('commerce.foo', true)` and `get('commerce.foo')` round-trip against a row whose stored key is the string `commerce.foo`. `get()` only falls back to nested-path traversal when no literal match exists, so you can safely use dotted keys as namespaced flat identifiers.

`has()`, `forget()`, and `pull()` are always literal. `has('theme.mode')` asks "is there a row with that exact key", not "can I reach that nested path through the `theme` row".

### Typed getters

[](#typed-getters)

No implicit coercion — if the stored value is the wrong type, the default is returned:

```
$team->settings()->string('theme', 'default');
$team->settings()->int('max_members', 25);
$team->settings()->float('rate', 1.0);
$team->settings()->bool('feature_x', false);
$team->settings()->array('tags', []);
```

### Miscellaneous

[](#miscellaneous)

```
$team->settings()->has('theme');           // true
$team->settings()->forget('theme');         // bool — whether it existed
$team->settings()->pull('theme');           // get + forget atomically
$team->settings()->updateValues('theme', [  // merge into an existing array
    'accent' => '#f00',
]);
```

Configuration
-------------

[](#configuration)

`config/polymorphic-settings.php` (all values can also be set via env vars):

```
return [
    'table'    => env('POLYMORPHIC_SETTINGS_TABLE', 'polymorphic_settings'),
    'key_type' => env('POLYMORPHIC_SETTINGS_KEY_TYPE', 'int'),   // 'int' | 'uuid'

    'cache' => [
        'enabled' => env('POLYMORPHIC_SETTINGS_CACHE_ENABLED', true),
        'store'   => env('POLYMORPHIC_SETTINGS_CACHE_STORE'),    // null = default
        'ttl'     => env('POLYMORPHIC_SETTINGS_CACHE_TTL'),      // null = forever
        'prefix'  => env('POLYMORPHIC_SETTINGS_CACHE_PREFIX', 'polymorphic-settings'),
    ],
];
```

### Caching

[](#caching)

Reads are cached through Laravel's cache. `put` and `forget` invalidate automatically. Cache keys are scoped (`{prefix}:{scope_key}:{setting_key}`) so two different models can never collide.

Missing keys are intentionally not cached to sidestep cross-driver null-caching quirks. If you hammer `get('nonexistent')` in a hot path, either supply a default or populate the key.

### UUID primary keys

[](#uuid-primary-keys)

The installer asks which PK type to use and auto-detects from your User model. If your app uses UUIDs:

```
POLYMORPHIC_SETTINGS_KEY_TYPE=uuid
```

The `configurable_id` column is always stored as a string, so int-keyed and UUID-keyed parent models can coexist in the same settings table regardless of this setting.

### Using `Settings` as the facade alias

[](#using-settings-as-the-facade-alias)

`PolymorphicSettings` is a mouthful. If you'd prefer `Settings::for($team)->get(...)`:

```
// config/app.php
'aliases' => [
    'Settings' => SiteSource\PolymorphicSettings\Facades\PolymorphicSettings::class,
],
```

The default facade alias stays `PolymorphicSettings` to avoid collisions with any existing `Settings` class in your app.

Testing
-------

[](#testing)

```
composer test
```

The suite runs against SQLite, MySQL, and Postgres in CI (120 tests, ~194 assertions as of v0.1.0).

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md).

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Nathan Call](https://github.com/nathancall)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance90

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

4

Last Release

46d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/19826928?v=4)[Nathan Call](/maintainers/nathancall)[@nathancall](https://github.com/nathancall)

---

Top Contributors

[![nathancall](https://avatars.githubusercontent.com/u/19826928?v=4)](https://github.com/nathancall "nathancall (1 commits)")

---

Tags

laravelconfigurationSettingsencryptioneloquentpreferencesmulti-tenantlaravel-settingspolymorphicmodel settings

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/sitesource-laravel-polymorphic-settings/health.svg)

```
[![Health](https://phpackages.com/badges/sitesource-laravel-polymorphic-settings/health.svg)](https://phpackages.com/packages/sitesource-laravel-polymorphic-settings)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M41](/packages/spatie-laravel-pdf)[wnx/laravel-backup-restore

A package to restore database backups made with spatie/laravel-backup.

210389.8k2](/packages/wnx-laravel-backup-restore)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

436834.4k1](/packages/clickbar-laravel-magellan)[lacodix/laravel-model-filter

A Laravel package to filter, search and sort models with ease while fetching from database.

17555.1k](/packages/lacodix-laravel-model-filter)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)[indexzer0/eloquent-filtering

Powerful eloquent filtering

22529.9k4](/packages/indexzer0-eloquent-filtering)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
