dragon-code/laravel-model-settings
Store per-model key-value settings in Laravel with dragon-code/laravel-model-settings. Attach typed settings to any Eloquent model, read and write them through a simple API, and keep configuration data with your records without extra tables.
This release (1.3.0) is a good entry point: the package now ships a full Docusaurus documentation site and generator commands for localization files, so the first stop should be the docs rather than the source.
Minimal setup:
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config
php artisan migrate
User, Team, or a dedicated Setting model).$user->settings->theme = 'dark';
$user->settings->save();
First use case: per-owner application preferences (theme, locale, notification toggles). Define defaults on the settings model rather than manufacturing a "default owner" row — see the ID 0 note below.
Use the new localization generator commands (#73) early if your settings have human-readable labels or validation messages you intend to translate.
Owner-scoped settings. Treat settings as always belonging to an owner (morph relation or model key). Owner validation is hardened in 1.3.0 (#95), so passing an arbitrary/stale owner ID will fail loudly rather than silently attach settings to the wrong record. Resolve owners through Eloquent relationships where possible.
Defaults are not rows. 1.3.0 distinguishes persistent defaults from a model with ID 0 (#100). Model defaults on the settings class and read through the accessor; do not create or query a sentinel id = 0 record to represent "unset". This makes fresh owners and migrated data behave consistently.
Payload casts per model and per key. #97 allows cast definitions scoped to a specific settings model and a specific setting key. Use this for structured values (arrays, DTOs, enums) instead of manually json_encode/json_decode-ing in accessors. Keep casts next to the settings definition so bulk reads and writes deserialize the same way as single reads.
Blank JSON is stored explicitly. #101 means an intentionally blank JSON payload persists as blank rather than collapsing to null/absent. If your UI distinguishes "never configured" from "configured as empty", this is now reliable — check for key existence/null, not truthiness.
Bulk mutations with bounds. #96 adds bounded-query bulk mutations and an overridable purge. For large owner sets, use the bulk API with an explicit bound/chunk size rather than iterating and saving one model at a time:
Settings::query()
->forOwners($ownerIds)
->updatePayload(['notifications' => true]);
Override the purge behavior in your settings model when deletion semantics differ from the package default (e.g. soft-deleting settings or cascading cleanup).
Typed exceptions instead of logs. #102 replaces package logging with typed bulk mutation exceptions. Wrap bulk operations in try/catch against the specific exception types and surface partial-failure information to the caller/job. This fits naturally with queued jobs: let the exception fail the job rather than discovering problems in logs later.
Localization workflow. Use the generator commands (#73) to scaffold translation files, then translate them (the docs site itself is localized, #80/#75). Keep generated files in version control and regenerate on upgrade.
0-ID default record, or treats blank JSON as null.catch (\Throwable) will swallow the useful detail. Catch the specific types and log/report them yourself.How can I help you explore Laravel packages today?