Skip to content

Configuration

config/notify-templates.php. The file reads no environment variables — every value is set in the file itself.

KeyDefaultDescription
tables.notify_templatesnotify_templatesTable of the NotifyTemplate model
tables.notify_role_subscriptionsnotify_role_subscriptionsTable of the NotifyRoleSubscription model
tables.notify_user_settingsnotify_user_settingsTable of the NotifyUserSetting model
tables.notify_logsnotify_logsTable of the NotifyLog model
channels['mail', 'telegram', 'sms', 'database', 'broadcast']Channel slugs offered in your admin UI; fallback of getTypeChannels() when a type defines no channels
channel_optionsmail, sms labelsPer-channel metadata for the admin UI: label, template slot, any extra keys
slot_optionsmail, messenger, sms labelsPer-slot metadata for the admin UI: label, subject, any extra keys
default_channels['mail']Channels used when an active subscription has an empty channels list, and the guaranteed-delivery fallback for non-configurable types
tenant_idnullTenant used when no tenant is passed explicitly: null, a string, or a callable returning one
types[]Notify types registered from config, in addition to discovered ones
discover[app_path('Notifications')]Directories scanned for BaseNotify subclasses on every boot; [] disables discovery
models.notify_templateNotifyTemplate::classModel class used to resolve templates
models.notify_role_subscriptionNotifyRoleSubscription::classModel class used to resolve subscriptions
models.notify_user_settingNotifyUserSetting::classModel class used for per-notifiable settings
models.notify_logNotifyLog::classModel class of the delivery log
log.enabledfalseWrite the delivery log
log.retention_days90Age after which model:prune deletes log rows
log.external_id_resolversmail, telegramChannel → ExternalIdResolverInterface class
log.content_resolversmail, telegramChannel → ContentResolverInterface class
log.store_bodytrueStore the sent body in the log; false keeps the subject only

Only the models read these names. The published migrations create tables with the default names hard-coded — to use other names, edit the published migration files as well.

The package does not validate anything against this list. It is what NotifyTemplates::getTypeChannels($key) returns for a type without its own channels, meant for the channel checkboxes of a subscription form. It has no effect on which channels a notification is actually sent through — see Channel resolution.

Metadata of the slugs from channels, read by getChannels() / getChannel() for an admin UI — a matrix header, an icon next to a toggle, a preview tab per template slot. Nothing here affects sending.

'channel_options' => [
'mail' => ['label' => 'Email', 'icon' => 'fas fa-envelope'],
'telegram' => ['icon' => 'fab fa-telegram'],
'sms' => ['label' => 'SMS', 'slot' => 'sms'],
],
KeyDefaultMeaning
labelslug with an upper-case first letterDisplay name
slotmail for mail, messenger for the restTemplate slot the channel renders. A slot of its own lets the channel override the shared messenger text — pass it to getMessengerBody(), see per-channel text
anything else—Returned as is

A channel without an entry gets the defaults, so a new slug needs no entry at all.

Metadata of template slots — the slot of a channel — read by getSlots() / getSlot(). A slot is the template an admin edits; several channels can share one (telegram and whatsapp both render messenger). Keep what the edit form and the preview need here instead of if ($slot === 'mail') in the views:

'slot_options' => [
'mail' => ['label' => 'Email', 'html' => true, 'rows' => 30],
'messenger' => ['label' => 'Messenger', 'rows' => 6],
'sms' => ['label' => 'SMS', 'max_length' => 160],
],
KeyDefaultMeaning
labelslot with an upper-case first letterDisplay name — a tab or card title
subjecttrue for mail, false for the restThe slot has a subject line
anything else—Returned as is

Nothing here affects sending.

Used in two places:

  1. resolveChannels() — an active subscription whose channels is empty or null resolves to default_channels.
  2. BaseNotify::via() — when nothing survived resolution, a type with 'user_configurable' => false falls back to default_channels. Regular types send nothing.

In both cases the values are channel slugs and go through mapChannel(): mail is skipped for a notifiable without a mail route, and your own channels need a mapChannel() entry (Custom channels).

'tenant_id' => null, // single tenant
'tenant_id' => 'shop-ua', // fixed tenant
'tenant_id' => [\App\Support\Tenant::class, 'id'], // callable, evaluated on every resolution

The value is read by resolveTemplate(), resolveChannels(), resolveDelay(), resolveButtons() and the delivery log whenever no explicit tenant is passed. Details — Multi-tenancy.

Same array shape as typeDefinition(). Registered after discovery, so a config entry with the same key replaces the discovered definition. See Notify types.

Every listed directory is scanned recursively on each application boot. See Notify types — auto-discovery.

Point a key to your subclass to add behaviour, for example translations on NotifyTemplate (Multilingual templates). The manager and the log subscriber resolve the class from this config; your own queries should use the same class.

'log' => [
'enabled' => false,
'retention_days' => 90,
'external_id_resolvers' => [
'mail' => \Fomvasss\NotifyTemplates\Resolvers\MailMessageIdResolver::class,
'telegram' => \Fomvasss\NotifyTemplates\Resolvers\TelegramMessageIdResolver::class,
],
'content_resolvers' => [
'mail' => \Fomvasss\NotifyTemplates\Resolvers\MailContentResolver::class,
'telegram' => \Fomvasss\NotifyTemplates\Resolvers\TelegramContentResolver::class,
],
'store_body' => true,
],

log.enabled is read once, when the service provider boots: the event subscriber is registered then or not at all. Changing the value at runtime has no effect. The resolver maps are keyed by the channel exactly as via() returned it — 'mail', 'telegram' or a channel class name. Details — Delivery log.