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

Laravel Settings Laravel Package

owowagency/laravel-settings

A Laravel package for managing application settings with a clean, typed API. Store settings in the database or config, cache values for speed, and organize environment-specific options without scattering configuration across your codebase.

View on GitHub
Deep Wiki
Context7

Getting Started

Verify before you build. This is a 0.1.0 release with empty release notes and no README shipped in the artifact. Spend the first 20 minutes reading composer.json, the service provider, config/settings.php, and any database/migrations/ in vendor/owowagency/laravel-settings before you write a line of application code. Everything below assumes the described API (get/set, defaults, typed values, cache-backed); confirm the actual signatures against the source.

Install pinned, not floated.

composer require owowagency/laravel-settings:0.1.0   # exact pin, not ^0.1

Commit the lockfile. A 0.x minor bump may break the API with no changelog to warn you.

Publish and migrate.

php artisan vendor:publish --provider="Owowagency\Settings\SettingsServiceProvider"

Publish tags to check for, in order of likelihood: config, migrations. Then inspect the migration file before running it — check it is reversible and that the table name does not collide with anything you already own.

php artisan migrate

First use case. Pick one low-risk, admin-editable value that today lives in a config file and gets changed by a deploy — a support email, a banner message, a feature toggle. Not a secret, not a revenue-path flag. Read it with an explicit default so the app still boots on a fresh database:

$supportEmail = settings('support_email', config('mail.support.address'));

Where to look first, in this order:

  1. The service provider — is it auto-discovered, what does it bind into the container, does it register a facade or global helper?
  2. The published config — cache store, cache TTL, table name, type mapping.
  3. The migration — schema shape and reversibility.
  4. The read/write class — whether values are cast on read or stored as strings.

If the provider registers global helpers or a facade, decide immediately whether you will allow them in application code (recommended: no — see below).

Implementation Patterns

Wrap it behind an application-owned adapter. This is the single most important pattern for a pre-1.0 dependency. One class, one contract, and nothing else in the codebase imports the package namespace.

// app/Support/Settings/AppSettings.php
interface AppSettings
{
    public function get(string $key, mixed $default = null): mixed;
    public function set(string $key, mixed $value): void;
}

// app/Support/Settings/PackageSettings.php
final class PackageSettings implements AppSettings
{
    public function get(string $key, mixed $default = null): mixed
    {
        return settings($key, $default); // ← the only call site in the app
    }

    public function set(string $key, mixed $value): void
    {
        settings()->set($key, $value);
    }
}

Bind it in a provider and inject AppSettings everywhere. When the package is abandoned or breaks, you rewrite one file.

Read with defaults, always. Never assume a key exists — a fresh environment, a rolled-back migration, or a cache flush all produce empty reads. Defaults at the call site are cheap; a null propagating into a mailer is not.

Write through validation, not through controllers. Settings writes come from admin forms; treat them like any other user input.

final class UpdateSettingsRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'support_email' => ['required', 'email'],
            'banner_enabled' => ['required', 'boolean'],
        ];
    }
}

public function update(UpdateSettingsRequest $request, AppSettings $settings)
{
    $settings->set('support_email', $request->validated('support_email'));
    $settings->set('banner_enabled', $request->boolean('banner_enabled'));
}

Note $request->boolean() — the HTTP layer hands you "1"/"0"/"on", and you want a real bool persisted so the type cast round-trips.

Seed defaults idempotently. Do not put defaults in a migration (you cannot change them later without a new migration). Put them in a seeder or an artisan command that uses firstOrCreate-style semantics, and run it from your deploy pipeline:

foreach (config('settings.defaults') as $key => $value) {
    if ($settings->get($key) === null) {
        $settings->set($key, $value);
    }
}

Keep reads out of loops. The package is cache-backed, but the described design is per-key. If you render a page that needs twelve settings, read them once at the top of the request (or via a view composer) and pass an array down — do not call get() inside a Blade @foreach over rows.

View::composer('layouts.admin', function ($view) use ($settings) {
    $view->with('site', [
        'banner' => $settings->get('banner_enabled', false),
        'email'  => $settings->get('support_email', config('mail.support.address')),
    ]);
});

Group related settings into a value object rather than scattering string keys. A small SiteSettings DTO hydrated from three get() calls gives you autocompletion, a single place to change a key name, and a boundary you control if the package is swapped.

Testing pattern. Use the array cache driver in phpunit.xml so tests do not depend on a shared Redis, and use RefreshDatabase so the settings table exists:

protected function setUp(): void
{
    parent::setUp();
    config(['cache.default' => 'array']);
}

Then fake your own contract, not the package:

$this->instance(AppSettings::class, new InMemorySettings(['banner_enabled' => true]));

This keeps the entire test suite independent of the package's internals — the same property that makes the adapter worth writing.

Gotchas and Tips

Exact-pin the version. ^0.1 on a 0.x line permits 0.1.x and nothing else under strict SemVer, but any tooling or a maintainer's loose tagging can surprise you. Use 0.1.0, commit the lockfile, and treat every upstream bump as a code review with a manual test.

Cache is the source of truth at runtime — the database is not. Two failure modes follow:

  • Writing to the settings table directly (SQL, a one-off script, a data import) will not invalidate the package's cache. Readers keep seeing the old value until TTL expiry or an explicit flush. Route every write through the package API, or flush the cache as part of the script.
  • Multi-server deploys share a cache. Configure the package to use a shared store (Redis/database), not file or array, or server A and server B will disagree about the same setting.

Flush settings cache as a deploy step. If the cache key namespace changes between releases, or you seed defaults during deploy, add a cache:clear-adjacent step (or whatever flush the package exposes) to your release script. Otherwise the first request after deploy serves stale values.

Type coercion is where these packages bite. Database columns and cache serializers both have opinions about booleans and arrays. Test the round-trip explicitly for each type you use:

$settings->set('flags', ['beta' => true]);
$this->assertSame(['beta' => true], $settings->get('flags'));

Watch for the classic "false" string being truthy, and for arrays coming back as JSON strings if the cast is missing or misconfigured. Prefer scalar-typed settings and derive arrays in code if the casting is unreliable.

Do not store secrets. Database-stored settings are visible to anyone with DB read access, may be dumped in backups, and are frequently exposed by admin UIs or debug endpoints. API keys, SMTP passwords, and signing keys belong in .env or a secret manager.

Check migration reversibility before running. If the published migration is not reversible, or if it alters an existing table, you have created a dependency you cannot cleanly remove. A one-way migration into a shared database is a commitment — inspect it first, and consider copying the schema into your own migration so you own it.

Octane and queue workers. These are long-lived processes. If the package memoizes settings in a singleton or static property, a write in one request will not be visible to other workers until restart, and a stale value can persist indefinitely. Verify behavior under octane:start before putting this on a request path, and re-resolve the settings service (or explicitly refresh) inside long-running jobs.

Multi-tenancy is not addressed by the package. If two tenants need different values for the same key, the described API has no scope parameter. The workarounds are all ugly: key prefixes (tenant_42.support_email), a tenant-aware adapter that composes the key, or a different package. Decide which before you ship the first tenant.

Treat the absent changelog as your problem to solve. Keep a docs/settings.md in your repo recording: the pinned version, the keys you use, the adapter contract, and the date you last reviewed upstream. That note is the only durable record of why the integration looks the way it does, and it is what the next developer will need.

Extension points worth checking in the source. The config file (cache store, table name, TTL), the container binding (so you can swap the implementation in tests), and whether there is a model or cast layer you can subclass. Any of these gives you a supported seam; if none exist, the adapter is your only seam.

Re-evaluation trigger. Dependents: 0. Last release: November 2020. Set an explicit review date — 90 days of upstream inactivity, or any 0.x minor release — and decide then whether to keep it, fork it, or replace it with ~50 lines of your own cached key/value model. For a package of this scope, writing it yourself is a genuinely viable outcome, and the adapter means you can make that call without touching application code.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource