Skip to content

Settings and configuration

CPalius has two separate "configuration" concepts and they must not be confused:

Settings Configuration sync (config sync)
Where In the database (cp_settings) In cp-content/config/sync/*.yaml files
Who changes it The administrator, from the panel The developer, with Git
What for Site name, SMTP, security modes… Roles, workflows, path patterns, field definitions
Moving Backup/restore or with cp:config Git

The two are bridged with cp:config: settings and fields are exported to YAML and imported from YAML.

Defining a setting: #[CpSetting]

Each setting is an attribute collected at build time. By convention they are piled onto an empty carrier class:

#[CpSetting(key: 'shop.currency', label: 'shop.setting.currency', type: 'select',
            default: 'TRY', variants: ['TRY' => 'Turkish Lira', 'EUR' => 'Euro'],
            module: 'shop', group: 'shop_general')]
#[CpSetting(key: 'shop.notice', label: 'shop.setting.notice', type: 'textarea',
            translatable: true, module: 'shop', group: 'shop_general')]
#[CpSetting(key: 'shop.api_secret', label: 'shop.setting.api_secret', type: 'password', module: 'shop')]
final class ShopSettings
{
}
Parameter Meaning
key Unique id: module.name
label Label shown on screen (can be a translation key)
type text, checkbox, select, textarea, integer, password
default Value returned if there is no row in the database
variants select options (value => label)
module Owning module/plugin id; core for the core
group Which section it is gathered under on screen
translatable The value is stored as a {"tr": "...", "en": "..."} map (only text and textarea)
scope Settings tab: core, module or plugin. If empty, it is found automatically from the module field

Settings appear as tabs under AACP → System → System Settings; adding the definition also creates the screen by itself.

Reading

$currency = $settings->get('shop.currency');                 // default 'TRY'
$notice   = $settings->getForLocale('shop.notice', 'en');   // by language
$all      = $settings->getTranslations('shop.notice');      // ['tr' => '…', 'en' => '…']

For a translatable setting the reading order is: request language → default language → first filled value → $default.

Values are kept in memory per request and cached in cache.app (loaded with a single query). If you write a setting in code, call clearCache('shop.currency') afterwards; the panel does this itself.

Encrypted settings

type: 'password' settings (SMTP password, CAPTCHA secret key, API credentials) are stored encrypted in the database (SettingSecretCodec). A read-only SQL injection or a shared database backup does not hand these values out as plain text. An encoded value carries a distinct marker, so plain-text values written before encryption was added are not mistaken for a "corrupt cipher" but are recognized as "old plain text".

Core setting keys

The main settings with their defaults. (All security settings are on the Security page.)

Site

Key Default
core.site_name CPalius CMF Site name
core.site_description empty Site description
core.maintenance_mode off Maintenance mode
core.maintenance_message empty Maintenance page message
core.default_locale tr Default language
core.timezone Europe/Istanbul Time zone
core.permalink_structure postname Permalink structure
core.default_meta_title_format %%title%% – %%site_name%% Default title format
homepage.mode portal Home page mode (portal or simple home page)

Email

Key Default
mail.enabled off
mail.smtp_host, mail.smtp_port empty, 587
mail.smtp_encryption tls
mail.smtp_user, mail.smtp_password empty (the password is stored encrypted)
mail.from_email, mail.from_name empty
mail.log_retention_days 90

Account registration

Key Default
account.registration_enabled on
account.require_email_verification off
account.require_admin_approval off
account.username_required on
account.require_terms on

Content

Key Default
content.revisions.enabled on
content.revisions.max_per_node 25
content.moderation.enabled_types empty (no type enters the workflow)
content.moderation.workflow editorial

Notification, logging, audit

Key Default
notification.enabled, notification.digest.enabled on, on
logging.min_level warning
logging.retention_days 30
audit.enabled, audit.retention_days on, 365

consent.enabled is off by default. When turned on, separate consent categories for analytics and marketing cookies (consent.analytics_enabled, consent.marketing_enabled) and all button/message texts are customized with settings.

Performance and storage settings are on the Cache and performance and Media and storage pages.

Configuration sync (config sync)

php cp-core/bin/console cp:config export          # write the live configuration to YAML
php cp-core/bin/console cp:config status          # is there drift?
php cp-core/bin/console cp:config import          # apply the YAML (default: dry run)
php cp-core/bin/console cp:config import -f       # actually apply
php cp-core/bin/console cp:config import --only=settings

Design properties:

  • Deterministic: keys are sorted recursively; if two environments have the same configuration, the YAML files come out byte for byte identical. A Git diff shows only the real change.
  • Portable: the export contains no ids or timestamps.
  • Transactional: the import runs in a single database transaction; providers persist but do not flush.
  • Provider-based: each configuration type implements a ConfigProviderInterface and owns one or more "documents". The core's providers: settings (settings), fields (field.<bundle>), taxonomy, text formats, path patterns, entity display.

If a module wants to add its own configuration, it implements ConfigProviderInterface: documents(), exportDocument(), diffDocument(), importDocument() and afterImport().

Typical flow

In development add a field → cp:config export → commit to Git → update the files on production → see the difference with cp:config status → cp:config import -f.

Roles (user.role.*.yaml) and workflows (workflow/*.yaml) are files directly; they have no counterpart in the database, so they do not need an export. Edit the file, clear the cache.

Last updated: 13.10.2026