trinity/settings
Trinity Settings is a Laravel package for managing application settings at runtime. Store, retrieve, and update configuration values in the database with a simple API, avoiding direct edits to config files. Ideal for admin-editable options and per-app customization.
Before anything else, set expectations: this is a Symfony bundle (Trinity Settings bundle), not a Laravel package. It has one star, zero dependents, and its last release was 2017-08-02, so treat it as abandonware you are evaluating, not adopting blindly.
Minimal steps:
composer.json for symfony/* and php constraints (a 2017 bundle will likely pin Symfony 2.8/3.x and PHP 5.6/7.0 — incompatible with modern Laravel's PHP 8.x baseline).src/ for the manager/service class — this is the API you'll actually call.Resources/config/services.* for how the bundle wires its services (constructor arguments reveal dependencies like Doctrine, cache, or a config array).Entity/ or Migrations/ folder — settings bundles almost always persist to a settings table.Settings contract.site.name — through the manager, confirm it persists, and confirm a second read comes from cache, not the DB.If step 1 shows hard Symfony framework coupling (container, Kernel, event dispatcher), the "install and use" path is effectively dead and you should port the pattern rather than the package.
Facade/service wrapper. Never call the bundle's manager directly from controllers. Bind an interface in a Laravel service provider and resolve the underlying manager once:
$this->app->singleton(Settings::class, function ($app) {
return new CachedSettings(
new TrinitySettingsManager(/* DB connection, config */),
$app['cache.store'],
);
});
This isolates the abandoned code to one file, so replacing it later is a one-line change.
Read-through cache. Settings are read on nearly every request and written rarely — the correct pattern is cache-first with write-through invalidation:
get($key) → check cache → on miss, query the settings table → store → return.set($key, $value) → write to DB → forget/refresh the cache key.settings_version counter bumped on write.Scoped keys. Use dot notation (mail.host, mail.port) and store group + key as separate columns if the bundle supports it, or split on . yourself. This keeps admin UIs groupable and avoids a flat, unnamed key soup.
Typed values. The table will almost certainly store strings (or JSON). Wrap casting in your own layer:
public function bool(string $key): bool { return filter_var($this->get($key), FILTER_VALIDATE_BOOL); }
public function json(string $key): array { return json_decode($this->get($key), true) ?? []; }
Do not rely on the bundle to preserve types across a DB round-trip — it almost certainly doesn't.
Defaults with DB override. Merge a config/settings.php defaults array underneath the DB values so a missing row is a valid state, not an error. Seed the table from the same file in a seeder/migration.
Template integration. Expose a Blade directive or helper (@setting('site.name') → setting('site.name')) that reads through the cached manager. Don't inject the manager into views directly.
Workflow for a new setting: add default to config → add admin form field → set() on save → read via setting() helper → clear cache in the same transaction. Codify this as a checklist; it prevents half-wired settings.
php artisan vendor:publish, no package discovery, no Laravel service provider. You must register it yourself and satisfy whatever Symfony contracts its constructor demands. Budget for this before promising a feature.composer.json first; if it requires doctrine/orm ^2.5 and you're on Laravel 11 + PHP 8.3, expect conflicts. Test in a throwaway branch before touching composer.json in the main app.encrypt()/decrypt() in your wrapper) — don't trust the package to do it.Cache repository.migrate on a fresh DB throws. Guard with Schema::hasTable() in your wrapper.'1'/'', nulls stored as 'null'. Always decode on read in your wrapper, never at the call site.settings table directly first; if the row is right, the problem is caching or casting, not the bundle. Check storage/logs/laravel.log for the Symfony service/container exceptions the bundle will emit during bootstrap — those fail before your code runs.settings table + cached repository is a half-day of work and removes the whole dependency risk. If the bundle's only real value is the table schema, copy it.Bottom line: the package is a thin, unmaintained Symfony settings store. Its only day-to-day value in a Laravel app is as a reference implementation. Wrap it tightly if you must use it, and treat replacement-by-porting as the likely end state.
How can I help you explore Laravel packages today?