tarfin-labs/laravel-config
Manage Laravel configuration dynamically. This package lets you store and retrieve config values from the database, override defaults at runtime, and keep application settings organized through a simple, cache-friendly API. Useful for admin-editable settings and environment-specific overrides.
Install and publish
composer require tarfin-labs/laravel-config
php artisan vendor:publish --tag="laravel-config-config" # config file
php artisan vendor:publish --tag="laravel-config-migrations" # table for stored values
php artisan migrate
Where to look first
README.md – the public API surface (helper functions / facade) is small; read it end-to-end before writing wrappers.config/laravel-config.php – table name, model override, cache settings, and any connection option. This is the file you will touch most.First use case
Migrate a small set of runtime-tunable settings out of config/*.php and .env and into the database so non-developers can change them without a deploy: site contact email, support phone, maintenance-mode banner text, feature toggles, per-environment third-party keys. Keep genuinely structural config (queue driver, DB credentials, mail transport) in files — the package is for values that change on a business cadence, not application wiring.
Laravel 13 note (6.1.0): this release only adds Laravel 13 support. If you are starting on Laravel 13, require ^6.1; if you are on Laravel 12, ^6.0 is fine and there is nothing to migrate.
Hybrid resolution with a fallback chain. The strongest pattern is DB value → config() file value → hard default, exposed through one accessor in your app (a thin service class or a settings() helper) rather than calling the package directly from fifty places. That gives you a single seam to swap the backend later and one place to add casting and logging.
Typed reads. Store the type alongside the value (or infer it) and always cast on read — booleans in particular arrive as "1"/"0" strings from the DB. Centralise casts for bool, int, json/array, and null. Validate on write, not on read; a bad value written once should not poison every request.
Cache warm-up and invalidation. Reads happen on nearly every request, so depend on the package's cache layer rather than the DB. Add a deploy step that warms the cache, and make sure writes invalidate it (if the package does not do this for direct model writes, wrap writes in a repository that flushes explicitly). For multi-server setups, prefer a shared cache store (redis/memcached) over file so invalidation propagates.
Seeding defaults as part of the schema. Put default values into a seeder or into the migration that creates the table so a fresh environment boots with known-good values and no null-handling branches. Make seeds idempotent (updateOrCreate on key) so they are safe to re-run on deploy.
Multi-tenant / scoped settings. If settings differ per tenant, add a tenant_id (or group/scope column) via your own migration on top of the package table and always resolve through a scoped repository. Do not fork the package for this; scope in your wrapper.
Admin surface. The stored values are just an Eloquent model, so a simple Filament/Nova/Blade CRUD over it is usually enough. Group keys by prefix (mail.*, features.*) and render an appropriate input type per key — this is the part that delivers the actual business value, so budget for it.
Testing. Use RefreshDatabase plus a base TestCase that seeds the default settings, so feature tests see a realistic config. For pure unit tests, bind a fake implementation of your wrapper rather than hitting the table. Never let a test depend on ambient DB state for config.
CI matrix. With 6.1.0 the only supported-version change is Laravel 13; run the suite against both the current and next major if you publish this package internally, and pin laravel/framework constraints in composer.json to match what you actually test.
config:cache. Anything read during application boot (service providers, config/*.php files, package discovery) may run before the database is available. Never call the package from inside a config file or from a provider's register(). Reading in a middleware, controller, or lazily-resolved service is safe.Schema::hasTable().php artisan serve daemons keep resolved values in memory. Restart workers after changing settings, or read through the cache with a TTL.config:cache does not know about the DB. php artisan config:cache snapshots file config only; DB-backed values stay dynamic. Do not assume clearing the config cache refreshes them — clear the package cache too, and document that in your deploy script.encrypted cast and remember the DB dump is now a secret-bearing artefact — broader blast radius than a .env file. Consider keeping credentials in .env and only non-secret tunables in the DB."false" is truthy in PHP; always cast explicitly and add a test for the boolean false case.How can I help you explore Laravel packages today?