laravel-admin-ext/config
A laravel-admin extension that adds a web-based configuration manager, letting you edit and organize application settings from the admin panel without touching config files. Ideal for managing dynamic options in Laravel projects.
This is a settings editor for laravel-admin: it gives you an admin screen where non-developers can create/edit named config values, and it lets your application read those values back at runtime. Small package (97 stars, one maintainer, last release Dec 2019), so treat it as "a UI over a key/value table," not as infrastructure you build a platform on.
Minimal steps:
composer require laravel-admin-ext/config
php artisan admin:import config
The import registers the menu entry, routes, and permission rows.config/admin.php (extensions array) and the admin_extensions table to confirm the extension is actually enabled — a missing menu item is almost always one of these two, not a bug in the package.src/ (controller, Config model, routes) and the migration. The migration tells you the exact table/columns; the controller tells you how values are persisted and whether anything is cached.site_name, contact_email, footer_text, support_phone. These are the values marketing/support ask you to change weekly — perfect fit. Secrets (API keys, SMTP passwords) are a poor fit; keep those in .env.The one thing to nail down immediately is the read path: how does the package expose a stored value to your app (a model call, a helper, or values merged into Laravel's config repository so config('site_name') resolves)? Check the README for your pinned version, then wrap it (see below) so nothing else in your codebase depends on the answer.
Wrap the package behind one accessor. Your controllers, Blade views, and jobs should never call the extension directly. A thin Settings class gives you caching, defaults, and a migration path off the package later.
// app/Support/Settings.php
class Settings
{
public static function get(string $key, $default = null)
{
return Cache::rememberForever("settings.{$key}", function () use ($key, $default) {
return Config::where('slug', $key)->value('value') ?? $default;
});
}
public static function flush(): void
{
Cache::flush(); // or a dedicated tag/store if your cache driver supports it
}
}
Hook Settings::flush() into the model's saved/deleted events (or an observer) so edited values take effect immediately. Without this you get the classic "I changed it in admin and nothing happened" ticket.
Read once per request, share to views. Don't scatter Settings::get() calls through Blade — resolve the handful of values you need in a view composer or AppServiceProvider::boot() and View::share() them. That also makes the cache hit pattern obvious in profiling.
Use it for flags, not for secrets or structure. Good: display names, toggles (maintenance_banner_enabled), contact routing, feature flags read inside a middleware or policy. Bad: anything that must differ per environment and must be reproducible from git (queue names, storage disks, third-party credentials). If losing the row would break the app, it belongs in code or .env.
Type your values deliberately. A key/value table stores strings. Decide the contract per key: booleans as '0'/'1', arrays as JSON encoded on save and json_decoded on read, money as integer minor units. Centralize the casts in your Settings wrapper so Settings::bool('x') and Settings::array('x') exist and callers stop guessing.
Group by domain in the admin UI. One page/tab per concern — Site, Mail, Payments, Feature flags — rather than a 90-row wall of fields. The person editing should be able to find "footer text" without reading a list of slugs.
Seed defaults. Write a seeder or migration that inserts every key with a sane default. New environments then work without a manual admin session, and your code can assume keys exist. Pair it with Settings::get($key, $default) as a second line of defense.
Guard the screen. The import adds permission entries, but explicitly assign the config page to a super-admin or a dedicated "settings manager" role. Treat writing settings as a privileged action — it changes behavior for every user.
Deployment note. DB-backed settings are data, not code. Staging edits don't reach production, and production edits aren't in git. Decide this explicitly per key, and if a value is environment-specific, resolve it as Settings::get('key') ?: config('services.key') so .env stays the fallback.
str_*/array_* helper migrations. If it breaks, you're forking it — budget for that.settings row, or spatie/laravel-settings. For a handful of keys, the custom page is often less total work than debugging a 2019 extension.APP_KEY means lost settings.null as distinct (decide which one means "unset" and be consistent).php artisan config:cache does not cache these values; your own Cache::rememberForever does. Include a cache flush in your deploy script, and remember queue workers and multiple app servers need a shared cache store to see edits.admin_extensions; extension listed in config/admin.php; php artisan admin:import config re-run; php artisan admin:import permission rows; menu cache cleared. That sequence resolves almost every install complaint.Settings wrapper, swapping the whole package out later is a one-class change rather than a grep across the app.How can I help you explore Laravel packages today?