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

phakpoom/settings

A lightweight Laravel package for managing persistent application settings. Store and retrieve key-value configuration from the database, update values at runtime, and cache settings for fast, consistent access across your app. Useful for admin-editable options and site preferences.

View on GitHub
Deep Wiki
Context7

Getting Started

The package has no dependents and essentially no external documentation, so the first job is reconnaissance. Before writing any feature code, read these four things in the repo (in this order):

  1. composer.json — confirms the real namespace, the PHP/Laravel constraints, and whether a facade alias is registered.
  2. src/*ServiceProvider.php — tells you the config file name, whether migrations are loaded via loadMigrationsFrom, and whether a facade/helper is bound in the container.
  3. database/migrations/* — the actual columns on the settings table (key, value, group?), the unique index, and whether value is text or json.
  4. config/settings.php (if published) — table name, cache store, cache TTL, and any default map.

Then wire it up:

composer require phakpoom/settings

# Publish the config and migration (verify the provider class name from composer.json)
php artisan vendor:publish --provider="Phakpoom\Settings\SettingsServiceProvider"
php artisan migrate

Laravel's package auto-discovery should register the provider; if the package predates it, add the provider (and any facade alias) to config/app.php manually.

First use case — a runtime-tunable knob that you'd otherwise put in a .env:

use Phakpoom\Settings\Facades\Settings; // verify the facade FQCN

// Read, with a sensible fallback when the key has never been written
$perPage = Settings::get('ui.per_page', 25);

// Write (typically from an admin controller or an artisan command)
Settings::set('ui.per_page', 50);

Confirm two behaviours on day one with php artisan tinker: what get() returns for a missing key (null vs. the default vs. an empty string), and what set() does to a value that's already cached. Everything else in this assessment depends on those two answers.

Implementation Patterns

Treat the config file as defaults and the DB as an override. The package has no schema of its own — any key is legal — so an unseeded production database silently returns defaults for everything. Seed the full key set explicitly and keep a single source of truth for defaults:

// config/settings-defaults.php — committed, plain PHP, no DB required
return [
    'ui.per_page'       => 25,
    'mail.from_name'    => config('app.name'),
    'features.beta_ui'  => false,
];

Wrap it in a typed accessor. Values come back as whatever the DB driver hands over — usually strings. A thin service layer keeps casts and defaults out of your controllers:

class AppSettings
{
    public function bool(string $key): bool
    {
        return filter_var(
            Settings::get($key, config("settings-defaults.$key")),
            FILTER_VALIDATE_BOOLEAN
        );
    }

    public function int(string $key): int
    {
        return (int) Settings::get($key, config("settings-defaults.$key"));
    }

    public function array(string $key): array
    {
        $raw = Settings::get($key, config("settings-defaults.$key"));
        return is_string($raw) ? (json_decode($raw, true) ?? []) : (array) $raw;
    }

    public function set(string $key, mixed $value): void
    {
        Settings::set($key, is_array($value) ? json_encode($value) : $value);
    }
}

Build one admin screen, not many. A single controller that renders the keys grouped by dot-notation prefix, validates against a rule map, and writes back is the whole UX this package needs. Pair it with an artisan command for ops changes so you never edit rows by hand:

Artisan::command('settings:set {key} {value}', function () {
    Settings::set($this->argument('key'), $this->argument('value'));
    $this->info('Saved.');
});

Read once per request. Settings rarely change mid-request. Resolve the handful you need in a base controller, a view composer, or a Settings value object bound as a singleton — don't call into the package from Blade partials in a loop. If you find yourself reading more than ~20 keys per request, check whether the package exposes a bulk all() and hydrate a cached array yourself.

Invalidate cache deliberately. Any write path must go through set() so the package can bust its cache entry. If you seed rows with DB::table('settings')->insert(...), or touch them in a migration, follow with Cache::flush() (or the specific tag/store the package uses) or you'll debug a phantom for an hour.

Test with the array cache store. Set CACHE_DRIVER=array in phpunit.xml and use RefreshDatabase so the settings table resets between tests. Add a helper to seed the default map in setUp() — otherwise tests that pass locally fail in CI where the table is empty.

Gotchas and Tips

String typing will bite you. Settings::get('features.beta_ui', false) returns the string "0" or "false" if the value went in as text — both of which are truthy in PHP. Never use a settings value in a raw if (); always cast through your wrapper.

Caching across processes. The cache layer is the main source of "I changed it but nothing happened." Queue workers and Octane workers hold their own state; after a set() in a web request, long-running workers may serve the old value until their own cache is invalidated. If the package supports cache tags, tag it; if not, use a short TTL for anything a worker reads.

Transactions. If you set() inside a DB transaction and the transaction rolls back, the cache has already been busted (or worse, repopulated with the uncommitted value). Write settings outside transactions, or dispatch the write with DB::afterCommit().

No locking, last-write-wins. Two admins saving the same settings form will clobber each other. That's usually fine for this class of data — just don't use it for counters or anything that needs atomicity.

Dot notation collides with literal dots. A key mail.from and a key mail.from.name may map to the same nested array internally. Keep your key naming flat and consistent (section.key) and validate against a regex on write.

Don't store secrets. There's no encryption here — API keys, SMTP passwords, and tokens land in plaintext in a table that gets dumped to every dev's laptop. Keep those in .env / a vault; use settings only for non-sensitive, operator-tunable values.

Config caching is irrelevant, package config isn't. php artisan config:cache won't freeze your DB values, but it will freeze the package's config file. If you change the table name or cache store, you must re-run config:cache.

Deployment ordering. Run php artisan migrate before any code that reads settings, and ship the defaults map in the same release. A deploy where the code expects billing.currency but the row doesn't exist yet should degrade to the config default, not 500 — that's what the fallback argument is for.

Pin the version and vendor it mentally. Zero stars, zero dependents, and a single maintainer means you should composer require phakpoom/settings:^x.y with an exact constraint, read the source before upgrading, and be prepared to fork. The API surface is small enough that replacing the package with your own 100-line repository class is a realistic escape hatch — which is also why you should keep every call behind AppSettings rather than sprinkling Settings::get() through the codebase.

Leaving the package behind. Dropping it later leaves an orphaned table and a stale cache entry. Keep a note in the README for the next developer: which keys exist, what they mean, and which config defaults they shadow.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
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