This guide shows how to read and write setting values using either the global helpers or the SettingsManager service.
Helpers
The helpers are the simplest way to work with settings anywhere in your codebase.
use function apGetSetting; // Retrieve
use function apUpdateSetting; // Update (sanitizes before persisting)
// Retrieve with optional explicit fallback default
$siteTitle = apGetSetting('site.title', 'My Site');
// Update — the value is sanitized by the callback you registered
apUpdateSetting('site.title', ' New Title ');
Manager Service
use ArtisanPackUI\CMSFramework\Modules\Settings\Managers\SettingsManager;
public function show(SettingsManager $settings)
{
$itemsPerPage = $settings->getSetting('site.items_per_page', 10);
// ...
}
public function update(SettingsManager $settings)
{
$settings->updateSetting('site.items_per_page', request('per_page'));
}
REST API
Saving registered settings (recommended)
To update registered settings over HTTP, use the by-key bulk endpoint. Every value is routed through SettingsManager::updateSetting(), so the registered sanitizer callback and SettingType are applied — exactly as if you had called apUpdateSetting() in PHP.
PUT /api/v1/settings
Content-Type: application/json
{
"settings": {
"site.title": " My Site ",
"site.logo_id": "42"
}
}
The response echoes the freshly-stored, sanitized and cast values:
{
"settings": {
"site.title": "My Site",
"site.logo_id": 42
}
}
Notes:
- Authorization goes through
SettingPolicy(settings.manageby default, via thesettings.updatecapability filter). - Every key in the payload must be registered (
apRegisterSetting/SettingsManager::registerSetting). Unregistered keys are rejected with a422validation error, since there would be no sanitizer or type to apply. - This is the natural fit for an admin settings page that saves a group of keys at once.
Generic CRUD caveat
The resourceful endpoints — POST /api/v1/settings, PUT /api/v1/settings/{key}, etc. — write directly to the Setting model and therefore bypass the registered sanitizer callback and SettingType. They are intended for ad-hoc, unregistered key/value storage.
Do not use the generic CRUD to save registered settings. Use
PUT /api/v1/settings(above) orapUpdateSetting()so sanitizers and types are always applied.
Default Resolution Order
When you request a setting value using getSetting($key, $default) or apGetSetting($key, $default), the value is resolved in this order:
- Stored database value (if the
settingstable exists and a record is present) - The explicit
$defaultargument you provided togetSetting/apGetSetting(if not null) - The registered default from
apRegisterSetting/SettingsManager::registerSetting
If none are available, null (or an empty string for string type) may be returned depending on the model cast behavior.
Caching
Since 2.15.4, reads don't query the database on every call. The first getSetting()/apGetSetting() in a request loads every stored setting with one query and keeps them in memory for the rest of the request. The rows are also stored in the cms.settings.all cache entry (SettingsManager::CACHE_KEY) for up to an hour (SettingsManager::CACHE_TTL), so a warm request runs no settings queries at all.
The cache is cleared automatically:
- Any Eloquent save or delete on a
Setting(includingapUpdateSetting()and the REST endpoints) flushes it immediately, and again after the surrounding transaction commits. - Reads inside a database transaction skip the cache and go to the database, so an uncommitted value is never cached or kept after a rollback.
- Queue jobs and Octane requests reset the in-memory copy at each job/request boundary, so they see writes made by other processes.
Writes that skip Eloquent events, such as a raw DB::table('settings')->update(...), must clear the cache themselves:
use ArtisanPackUI\CMSFramework\Modules\Settings\Managers\SettingsManager;
DB::table('settings')->where('key', 'site.title')->update(['value' => 'New Title']);
app(SettingsManager::class)->flushCache();
forgetLoadedSettings() clears only the in-memory copy and leaves the shared cache entry in place.
Since 2.15.5, an unavailable cache store doesn't break settings. If the store throws a database error (for example, the database cache store before its cache table is migrated), a read is treated as a cache miss and goes to the database, and the cache write and flush are skipped. The in-memory copy still works, so a request still runs at most one settings query.
Sanitization on Update
All updates go through the registered sanitization callback for the setting key. The callback should:
- Accept any raw input
- Return a cleaned value of the expected type
Example boolean sanitizer:
apRegisterSetting(
'site.is_private',
defaultValue: false,
callback: fn ($value) => filter_var($value, FILTER_VALIDATE_BOOLEAN),
type: 'boolean'
);
// Later...
apUpdateSetting('site.is_private', '1'); // stores true
Type Casting on Read/Write
The underlying Setting model casts values based on a stored type:
- On write, the model determines a type from the PHP value (boolean, integer, float, json, string) and stores both the
typeand a normalized representation ofvalue. - On read, it converts the stored value back to the proper PHP type using
type.
Tip: If you always return the correct type from your sanitizer, your stored type will remain consistent.

