Skip to main content

Back to README · Getting Started →

Laravel Model Settings

Laravel Model Settings stores shared defaults and per-model overrides in a separate database table. Use it when every model should start with the same value but individual records may override it.

The package does not add setting columns to parent tables. Settings remain independent from the model schema and are grouped by the model's Eloquent morph class.

When it fits​

RequirementPackage behavior
Give every saved model the same initial valueStore one class-level default
Change the value for one modelStore one per-model override
Remove an overrideExpose the class default again
Read many modelsEager load one relation for defaults and overrides

Resolution order​

When you read a setting, the package returns the first available value:

  1. The override for the saved model.
  2. The default for that model class.
  3. null.
Sourcetimezone
User defaultUTC
User 123 overrideEurope/Paris
Effective value for User 123Europe/Paris
Effective value for another saved userUTC

Removing an override exposes the default again. It does not delete the default.

Core operations​

(new User)->defaultSettings()->set('timezone', 'UTC');

$user->settings()->set('timezone', 'Europe/Paris');

$timezone = $user->settings()->get('timezone');
$settings = $user->settings()->all();
$hasTimezone = $user->settings()->has('timezone');

$user->settings()->setMany([
'locale' => 'fr',
'notifications.email' => true,
]);
$user->settings()->forgetMany(['timezone', 'locale']);

get() returns one effective value. all() returns an Illuminate\Support\Collection containing defaults merged with overrides. Use has() when effective-key existence matters, including when the stored value is JSON null. get() intentionally has no caller-supplied fallback argument: the persistent class default is its only fallback, followed by null when neither scope contains the key.

Defaults and overrides use the same operations: all(), get(), has(), set(), setMany(), forget(), forgetMany(), and purge().

Focused package boundaries​

Laravel Model Settings is a focused Eloquent package, not a general application-settings framework.

BoundaryIntentional behavior
StorageOne database table; no Redis backend or parent-model field storage
DefaultsReserved rows in the same table; no second defaults table
RegistrationNo repository registry, typed global settings classes, or class discovery
MigrationsNo per-key settings migration runner
CachingNo mandatory cross-request cache; eager loading only reuses a loaded relation

Applications that need those features should compose them outside this package rather than treating modelSettings or the internal repository as an extension API.

Storage boundaries​

Each row is identified by four values:

ValueMeaning
item_typeParent model morph class or morph-map alias
item_idParent primary key; class defaults keep the physical value 0
is_defaulttrue for a class default, false for a model override
keySetting name

This makes defaults independent for each model class. A User default never becomes a Post default, even when both classes use the same setting key.

Supported models​

The package supports Eloquent models with integer, string, UUID, or ULID primary keys. Persisted models with integer 0 or string '0' identifiers can store overrides without colliding with class defaults. Models may also use a Laravel morph map.

Per-model settings belong to persisted models. An unsaved model does not inherit defaults: get() returns null, has() returns false, and all() returns an empty collection. Calling set(), setMany(), forget(), forgetMany(), or purge() for an unsaved owner throws InvalidSettingsOwnerException before a storage query runs.

Payloads are stored as JSON, including null, empty strings, whitespace strings, empty arrays, zero, and false. Without a configured cast, reads return the exact decoded value. Payload casts can return application-specific objects instead.

See Also​