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

App Settings Laravel Package

cwsps154/app-settings

Filament App Settings lets you build a customizable settings panel with all Filament form components, add custom tabs via an artisan command, and control page and tab access with simple plugin methods. Includes install command and publishable config.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a Filament panel plugin that gives you a ready-made "App Settings" page backed by custom Filament forms, so you don't hand-roll a settings resource. It is small (single maintainer, low star count), so treat the source as the documentation after the first install.

Minimal path:

  1. Install and run the installer, which is what wires up config, migrations, and any default settings records:
    composer require cwsps154/app-settings
    php artisan app-settings:install
    
  2. Run the migration and inspect what it created before going further:
    php artisan migrate
    
    Look for the settings table (typically app_settings) and any seeded default rows.
  3. Register the plugin in the panel() method of your Filament PanelProvider:
    use CWSPS154\AppSettings\AppSettingsPlugin; // verify actual namespace in vendor/
    
    $panel->plugins([
        AppSettingsPlugin::make(),
    ]);
    
  4. Log into the panel and open the App Settings page. The default App tab is your first use case — add one field you actually need (e.g. site name, support email) via the plugin config and confirm it round-trips.

Where to look first, in order:

  • vendor/cwsps154/app-settings/src/AppSettingsPlugin.php — the plugin entry point and the canAccess, canAccessAppSectionTab, and appAdditionalField methods.
  • The published config file (config/app-settings.php or similar) — the real extension surface.
  • The AppSettings model and its migration — how values are stored (key/value vs. one column per field matters a lot).
  • The install command class — it tells you exactly which files are published and whether anything is seeded.

If you only need two or three global flags, this package is still worth it over a custom page, because you inherit Filament's form components (toggles, file uploads, rich editors) and the permission hooks for free.

Implementation Patterns

Gate the page per panel or role. The access callbacks are the main integration point. Wrap your own policy/role checks:

AppSettingsPlugin::make()
    ->canAccess(fn () => auth()->user()?->can('manage app settings') ?? false)
    ->canAccessAppSectionTab(fn () => auth()->user()?->hasRole('admin') ?? false);

If you use cwsps154/users-roles-permissions, plug its helpers straight into these callbacks rather than duplicating logic.

Add your own settings sections. Use appAdditionalField() to inject extra sections into the App tab. Keep each section as a Filament Section with a stable, prefixed field name so you can retrieve values predictably:

use Filament\Forms\Components\Section;
use Filament\Forms\Components\TextInput;

AppSettingsPlugin::make()
    ->appAdditionalField([
        Section::make('Branding')
            ->schema([
                TextInput::make('branding.site_name')->required(),
                TextInput::make('branding.support_email')->email(),
            ]),
    ]);

Read settings through the model, not the form. The page is an editor; your app code should consume settings via the AppSettings model (or a small Settings facade/wrapper you write). That wrapper is worth the ten minutes:

class Settings
{
    public static function get(string $key, mixed $default = null): mixed
    {
        return cache()->rememberForever("setting.{$key}", fn () =>
            AppSettings::query()->where('key', $key)->value('value') ?? $default
        );
    }
}

Treat the settings page as config, not user data. It's a write-rarely, read-often surface. Cache reads aggressively and bust the cache in an observer or in whatever callback runs after the settings form saves.

Layer environment-specific defaults. Store the database value as an override and keep a config/app-settings.php (or your own config) as the fallback, so a fresh environment works before anyone opens the panel:

public static function get(string $key): mixed
{
    return AppSettings::query()->where('key', $key)->value('value')
        ?? config("app-settings.defaults.{$key}");
}

Multi-panel / multi-tenant. If several panels need different settings, register the plugin per panel and scope your reads by tenant (or store the panel/tenant id as part of the key). Don't assume one global row set.

Testing. Because the page is pure Filament, livewire() tests against the settings page cover save + validation. Add a unit test around your Settings::get wrapper for cache-hit and default-fallback behavior — that's where real bugs live.

Gotchas and Tips

The README's namespaces are inconsistent. It shows CWSPS154\UsersRolesPermissions\AppSettingsPlugin in the usage example and then references CWSPS154\AppSettings\Models\AppSettings. The plugin likely lives under CWSPS154\AppSettings. Verify with:

grep -R "class AppSettingsPlugin" vendor/cwsps154/app-settings/src

Copy the real namespace into your provider; don't paste the README snippet blindly.

Pin the version. Nine stars, one maintainer, no dependents. Use an explicit constraint in composer.json (e.g. "cwsps154/app-settings": "^1.0") and commit composer.lock. Review the published migration before running it in production — small packages sometimes ship opinionated column choices you'll want to adjust (or a destructive rollback).

Cache invalidation is on you. If you add caching in a wrapper, remember to forget the key on save. A stale settings cache is the most common "the toggle didn't work" bug report.

File uploads need a disk. Any image/file field you add via appAdditionalField() writes through Filament's storage. Set an explicit disk in the field and confirm php artisan storage:link has run, or images will 404 in the panel.

canAccessAppSectionTab gates the tab, not the field values. It hides the UI; it does not protect stored settings from being read elsewhere. If a setting is sensitive (API keys, SMTP passwords), consider encrypting at rest with a model cast rather than relying on the panel gate.

The installer is not idempotent-safe in all cases. If you re-run php artisan app-settings:install after editing published files, check whether it overwrites config or re-seeds settings. Back up the published config before re-running, and prefer copying changes manually.

appAdditionalField() expects Filament schema components. Passing a raw array of fields without a wrapping Section may render but will look ungrouped, and layout components (Grid, Tabs) can behave unexpectedly if they aren't the expected shape. When in doubt, inspect how the default App tab is built in the package source and mirror that.

Debugging saves: if a field silently doesn't persist, confirm the field's name matches the key your read path queries, and that the field isn't missing a dehydrated() / is inside a disabled section. Filament's ->debugMode() on the form and Log::info($state) in a save hook are the fastest way to see what's actually being written.

Extension points summary: plugin fluent methods (canAccess, canAccessAppSectionTab, appAdditionalField), the published config, the AppSettings model (add accessors/encryption casts there), and Filament's own form component API. If you need a whole new tab or a second page, you're outside the package's scope — build a normal Filament page and use this package only for shared global config.

Exit strategy: because the storage is a plain table, migrating off this package is easy — point your Settings wrapper at the table directly and drop the plugin registration. Keep that wrapper in your own code from day one so the package stays a UI convenience rather than an architectural dependency.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package