Skip to content

Templates

notify_templates holds the subject and body that admins edit. A row is keyed by notify type, channel slot, role and tenant; any of the last three may be null, meaning “any”.

ColumnMeaning
notify_keyNotify type key
channelTemplate slot: mail, messenger, or your own; null = any slot
role_keynull = any role
tenant_idnull = any tenant
subjectUsed by mail
bodyThe text, with your tokens
optionsJSON — e.g. messenger buttons

There is at most one row per combination (unique index nt_lookup over the key with COALESCE on the nullable columns).

Example data:

notify_keychannelrole_keytenant_idsubjectbody
OrderOrderedmailnullnullOrder placedYour order [order:number] has been received.
OrderOrderedmailclientshop-uaThank you for your orderHi [user:name]! Order [order:number]…
OrderOrderedmessengernullnullnullOrder [order:number] placed

The slot is not the delivery channel — several channels can share one slot.

SlotUsed by
mailtoMail(); also the fallback of getMessengerBody()
messengergetMessengerBody() — toArray() (database/broadcast) and your Telegram, SMS, … methods; buttons
a channel’s own slot (telegram, sms, …)getMessengerBody($notifiable, $slot) — overrides messenger, see below
anything elseYour own code: $this->resolveTemplate('push') inside the Notify class

All messengers share the messenger text by default. To give one channel its own text, set its slot in channel_options and pass that slot when building the message:

config/notify-templates.php
'channel_options' => [
'telegram' => ['slot' => 'telegram'],
],
// your base notification
public function toTelegram(mixed $notifiable): TelegramMessage
{
$slot = NotifyTemplates::getChannel('telegram')['slot'];
return TelegramMessage::create()->line($this->getMessengerBody($notifiable, $slot));
}

The telegram row is an override, not a copy: getMessengerBody() reads it only when it has a body, otherwise the messenger row, then the mail row, then defaults.mail.body. Buttons follow the same order per option — a telegram row without options.buttons keeps the buttons of the messenger row. Rows with channel = null are never taken for an override. Without the config entry the slot is messenger and nothing changes.

NotifyTemplates::resolveTemplate('OrderOrdered', 'mail', 'client', 'shop-ua') returns the most specific matching row. Specificity: a matching slot outweighs a matching role, which outweighs a matching tenant.

#channelrole_keytenant_id
1mailclientshop-ua
2mailclientnull
3mailnullshop-ua
4mailnullnull
5nullclientshop-ua
6nullclientnull
7nullnullshop-ua
8nullnullnull

Rows of another role or tenant never match. When the role or tenant is null, only rows with null there match. If nothing matches, the result is null.

The tenant, when not passed, comes from config('notify-templates.tenant_id') — see Multi-tenancy.

When no row matches, or the matching row has an empty subject or body, BaseNotify uses the type’s defaults:

'defaults' => [
'mail' => ['subject' => 'Order placed', 'body' => 'Your order [order:number] has been received.'],
'messenger' => ['body' => 'New order [order:number]'],
],
MethodReturns
getSubjectDefault()defaults.mail.subject or ''
getBodyDefault()defaults.mail.body or ''

Fields are checked separately: a mail row with a body and an empty subject is sent with defaults.mail.subject.

The package substitutes nothing — prepareText() returns the text unchanged. Override it in the Notify class, or once in your base notification class:

protected function prepareText(string $text, mixed $notifiable): string
{
return strtr($text, [
'[order:number]' => $this->order->number,
'[user:name]' => $notifiable->name ?? '',
]);
}

or with fomvasss/laravel-str-tokens:

protected function prepareText(string $text, mixed $notifiable): string
{
return \StrToken::setEntity($notifiable)->setText($text)->replace();
}

prepareText() is applied to the mail subject and body, to getMessengerBody(), and to button text and urls. typeDefinition()['tokens'] is only a list for the editor — keep it in sync with what prepareText() replaces.

The package has no admin UI. Create and update rows with the model (or the class from config('notify-templates.models.notify_template')):

use Fomvasss\NotifyTemplates\Models\NotifyTemplate;
NotifyTemplate::updateOrCreate(
['notify_key' => 'OrderOrdered', 'channel' => 'mail', 'role_key' => 'client', 'tenant_id' => null],
['subject' => 'Order placed', 'body' => 'Your order [order:number] has been received.'],
);

To preview what a recipient gets, resolve the row the same way sending does:

NotifyTemplates::resolveTemplate('OrderOrdered', 'mail', 'client')?->subject
?: OrderOrderedNotify::typeDefinition()['defaults']['mail']['subject'];