symfony/slack-notifier
Symfony Slack Notifier lets your app send notifications to Slack via Symfony Notifier. Configure Slack webhook or token-based transport, then deliver messages from your code and notification system with a consistent API alongside other channels.
Install the package:
composer require symfony/slack-notifier
Configure the DSN in .env:
SLACK_DSN=slack://xoxb-YOUR_BOT_TOKEN@default?channel=#general
xoxb-YOUR_BOT_TOKEN with your Slack Bot Token (starts with xoxb-).#general with your target channel (e.g., @user, C1234567890, or #channel-name).Send your first message in a controller or command:
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Message\ChatMessage;
$notifier = new Notifier([], [new SlackTransport($dsn)]);
$notifier->send(new ChatMessage('Hello Slack!'));
Use Slack Notifier to send real-time alerts (e.g., deployment failures, errors):
$notifier->send(
new ChatMessage('Deployment failed on staging!')
->options((new SlackOptions())->block(
(new SlackSectionBlock())
->text('*Error*: Database connection timeout')
->accessory(new SlackButtonBlockElement('View logs', 'https://logs.example.com', 'danger'))
))
);
Build blocks incrementally for complex messages:
$options = (new SlackOptions())
->block(new SlackHeaderBlock('Incident Report'))
->block(new SlackDividerBlock())
->block((new SlackSectionBlock())
->text('Service: *Payment Processor*')
->field('Status', '⚠️ Degraded')
->field('Impact', '10% of users')
)
->block(new SlackActionsBlock()
->button('Acknowledge', 'https://status.example.com/ack', 'primary')
->button('Escalate', 'https://status.example.com/escalate', 'danger')
);
Reuse blocks in templates (e.g., for error handling):
function createErrorBlock(string $error, string $url): SlackSectionBlock {
return (new SlackSectionBlock())
->text("Error: *$error*")
->accessory(new SlackButtonBlockElement('Details', $url, 'danger'));
}
Queue notifications for async delivery:
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Symfony\Component\Notifier\Message\ChatMessage;
class SlackAlert implements ShouldQueue {
use Queueable;
public function __construct(public string $message, public array $options = []) {}
public function handle() {
$notifier = app(Notifier::class);
$notifier->send(new ChatMessage($this->message, $this->options));
}
}
Dispatch via:
SlackAlert::dispatch('Database down!', $slackOptions)->onQueue('slack');
Leverage Laravel Events:
// In EventServiceProvider
protected $listen = [
'deploy:failed' => [SlackAlert::class],
];
Route messages based on context (e.g., environment):
$channel = config('app.env') === 'production' ? '#alerts' : '@dev-team';
$dsn = "slack://{$token}@default?channel={$channel}";
Use environment-specific DSNs:
SLACK_DSN_PROD=slack://xoxb-prod-token@default?channel=#production-alerts
SLACK_DSN_STAGING=slack://xoxb-staging-token@default?channel=@staging-team
Confirmations for critical actions:
$block = (new SlackActionsBlock())
->button(
'Delete Backup',
'https://backup.example.com/delete',
'danger',
'delete_backup',
[
'title' => ['type' => 'plain_text', 'text' => 'Confirm Deletion'],
'text' => ['type' => 'plain_text', 'text' => 'Are you sure?'],
'confirm' => ['type' => 'plain_text', 'text' => 'Delete'],
'deny' => ['type' => 'plain_text', 'text' => 'Cancel'],
]
);
Modal dialogs (via Slack’s open URL scheme):
$block->button(
'Configure',
'https://your-app.com/slack-modal?team={team_id}&channel={channel_id}',
'primary'
);
// Send initial message
$sentMessage = $notifier->send(new ChatMessage('Processing order #123...'));
if ($sentMessage instanceof SlackSentMessage) {
$messageId = $sentMessage->getMessageId();
$channelId = $sentMessage->getChannelId();
}
// Update later
$notifier->send(
new ChatMessage('Order #123 completed!', new UpdateMessageSlackOptions($channelId, $messageId))
);
Invalid DSN Formats:
slack://xoxb-token@default?channel=#channel (Slack requires @user or encoded IDs for DMs/private channels).slack://xoxb-token@default?channel=C1234567890 or @username.Bot Token Permissions:
chat:write and chat:write.public scopes.chat.postMessage in Slack’s API tester first.Rate Limits:
Threading Issues:
threadTs must reference a valid message timestamp. Use SlackSentMessage to capture this:
$sent = $notifier->send(new ChatMessage('Parent message'));
$reply = new ChatMessage('Reply', (new SlackOptions())->threadTs($sent->getMessageId()));
Block Kit Validation:
curl -X POST -H 'Content-type: application/json' \
--data '{"blocks": [YOUR_BLOCKS_JSON]}' \
https://slack.com/api/chat.postMessage?token=xoxb-token&channel=CHANNEL
$notifier = new Notifier([], [], [
'debug' => true, // Logs raw API responses
]);
SlackTransport::getClient() to access the underlying GuzzleHttp\Client for debugging:
$client = $transport->getClient();
$response = $client->request('POST', 'https://slack.com/api/chat.postMessage', [
'json' => $message->toArray(),
]);
SlackOptions::postAt() to schedule messages and avoid rate limits:
$options = (new SlackOptions())->postAt(new \DateTime('+5 minutes'));
Custom Block Types:
Extend SlackBlockInterface to create reusable components:
class CustomBlock implements SlackBlockInterface {
public function toArray(): array {
return ['type' => 'section', 'text' => ['type' => 'mrkdwn', 'text' => $this->content]];
}
}
Transport Decorators:
Wrap SlackTransport to add logic (e.g., logging, retries):
class LoggingSlackTransport extends AbstractTransport {
public function __construct(private SlackTransport $transport) {}
public function send(ChatMessage $message): SentMessage {
\Log::debug('Sending Slack message', $message->toArray());
return $this->transport->send($message);
}
}
Dynamic DSN Resolution:
Override DSN logic in SlackTransportFactory for multi-environment setups:
$factory = new SlackTransportFactory
How can I help you explore Laravel packages today?