Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Settings Laravel Package

nhwin/settings

A Laravel package for database-backed application settings. It offers a simple API to get, set, and manage grouped configuration values at runtime, with caching support for fast access and a clean way to avoid hardcoding editable options.

View on GitHub
Deep Wiki
Context7

Getting Started

v2.0.0 is a major release, but the notes you have are a link-only changelog — no migration guide, no rename list. Treat the upgrade as a discovery task before you treat it as an install task.

Install / upgrade

composer require nhwin304/settings:^2.0
php artisan vendor:publish --provider="Nhwin304\Settings\SettingsServiceProvider" --tag=config
php artisan vendor:publish --provider="Nhwin304\Settings\SettingsServiceProvider" --tag=migrations
php artisan migrate

If you're coming from 1.1.2, pin the old version in a branch first and diff the published artefacts:

git diff v1.1.2..v2.0.0 -- config/ database/migrations/ src/

That diff is the real upgrade documentation. Look for: renamed config keys, changed default table name, changed cache key prefix, changed method signatures on the repository/facade, and any new migration that alters an existing column.

Look here first, in this order

  1. config/settings.php — table name, cache store, cache TTL, encryption flag, casts map, any multi-tenant/scope key.
  2. The published migrations — column types and indexes tell you more about intended usage than the README does (is the value a text column? is key unique? is there a group column?).
  3. The main service class / facade — the public surface is usually 6–8 methods (get, set, has, forget, all, maybe group/scope).
  4. The model trait, if one ships — that decides whether you get per-model settings or only global ones.

First use case — app-wide feature flags and editable site config, read on every request, written rarely:

use Nhwin304\Settings\Facades\Settings;

// somewhere in a service provider or seeder
Settings::set('billing.trial_days', 14);
Settings::set('features.new_checkout', false);

// in a controller / middleware
if (Settings::get('features.new_checkout')) {
    // ...
}

Do this in a throwaway branch against a copy of your real database before migrating production. v1.x data may need a backfill if the column shape changed.

Implementation Patterns

Cache-first reads behind a helper. Wrap the package behind your own thin class so the rest of the app never touches the facade directly. This is the single highest-value move when you're on a v2 with an undocumented changelog — it localises the blast radius of any signature change to one file.

final class AppSettings
{
    public function get(string $key, mixed $default = null): mixed
    {
        return Settings::get($key, $default);
    }

    public function flag(string $key): bool
    {
        return (bool) $this->get("features.{$key}", false);
    }
}

Namespace your keys from day one. A flat key column without grouping gets unusable fast. Adopt a dotted convention (billing.trial_days, mail.from_name, features.x) and, if the package supports groups/scopes, map the first segment to it so you can flush a namespace independently.

Type coercion belongs in config, not in your controllers. If the package exposes a casts map, register booleans, integers, JSON arrays, and dates there:

'casts' => [
    'features.*'   => 'bool',
    'billing.*'    => 'int',
    'integrations.*' => 'array',
],

If it doesn't, cast at your AppSettings boundary — never in Blade.

Form-driven admin screen. The pattern that pays off is: validate with a FormRequest, then write all fields in one pass and invalidate cache once.

public function update(SettingsRequest $request)
{
    DB::transaction(fn () => collect($request->validated())
        ->each(fn ($value, $key) => Settings::set($key, $value)));

    return back();
}

Guard the write path with Gate/policy — settings are usually global-write, per-role-read, and a missing authorization check is the most common way this package gets abused.

Defaults as seed data, not code. Create a SettingsSeeder (or a settings entry in your deploy script) that uses firstOrCreate-style semantics so new installs and existing installs converge. This is also where you record what changed in v2 — if a key was renamed, the seeder is the migration.

Multi-tenant scoping. If v2 adds or changes scoping, resolve the tenant from the request/queue context and never from a global singleton — queued jobs and Octane workers will leak tenant state across requests.

Testing. RefreshDatabase for the table, plus an explicit cache flush in setUp/tearDown; a stale cache store between tests is the classic source of "works alone, fails in the suite".

Gotchas and Tips

  • The changelog is a URL, not a changelog. For a major version bump that is a real risk. Read the commit list, or at minimum the UPGRADING.md/README diff and the src/ diff, before touching production. Assume at least one silent behaviour change (default value handling, cast behaviour, cache TTL default).
  • php artisan config:cache is the #1 breakage. Any env() call inside the package's config file returns null once cached. Verify your published config has hardcoded defaults and move environment-driven values into AppServiceProvider.
  • Cache store choice matters in production. The array or file store works in development and silently fails to invalidate across multiple app servers. Use Redis/memcached/database, and confirm the cache key prefix changed (or didn't) in v2 — a stale prefix from v1 will serve old values after deploy.
  • Clear caches on deploy. Add php artisan cache:clear (or the package's own flush command, if one exists) to your release script, immediately after migrate.
  • null vs. missing. Decide and document: does get('missing') return your default, or null? Wrapping in AppSettings makes this consistent regardless of what v2 changed.
  • Booleans stored as '0'/'1'/'false' strings. Without casts, (bool) 'false' is true. Always cast explicitly.
  • Bulk writes = N cache invalidations. Batch inside a transaction and flush once; on large settings pages this is the difference between 8 queries and 80.
  • Column limits. Long keys hit index length limits on MySQL with utf8mb4; encrypted values need enough column width (and if v2 changed the cipher or encoding, existing rows may not decrypt — test on a database copy).
  • Queues and Octane. Settings cached in a singleton survive between requests. Resolve per-request or flush after writes in long-running workers.
  • Debugging checklist when a value "doesn't update": (1) Settings::get() directly in tinker, (2) query the table, (3) check the cache store and key prefix, (4) confirm config:cache isn't pinning an old config. That order isolates it in under a minute.
  • Extension points worth knowing: the model/repository binding (swap in your own implementation for auditing or per-tenant resolution), the cast resolver, and the cache store config. If v2 removed or renamed any of these, that's your upgrade blocker — find it before you find it in production.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums