nexylan/slack-bundle
Symfony bundle that integrates the Slack API into your application. Configure Slack clients and tokens, inject services via the container, and send messages or notifications from your Symfony app with minimal setup.
Install:
composer require nexylan/slack-bundle
The bundle auto-registers on Symfony Flex (Nexy\SlackBundle\NexySlackBundle), otherwise enable it in config/bundles.php.
Configure the webhook — this is the first place to look. You need an incoming webhook URL from your Slack workspace (Slack UI → App → Incoming Webhooks → Add New Webhook). The minimal config:
# config/packages/slack.yaml
slack:
endpoint: '%env(SLACK_WEBHOOK_URL)%'
channel: '#general' # default channel
username: 'My Bot' # display name
icon: ':robot_face:' # emoji shortcut or https URL
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/XXXXXXXX
First use case — inject the client (Nexy\Slack\Client) and send a message. It's the only service you normally need:
use Nexy\Slack\Client;
final class DeployNotifier
{
public function __construct(private Client $slack) {}
public function notify(string $version): void
{
$this->slack->send(":rocket: Deployed *{$version}* to production.");
}
}
That's it — one config file, one service, one send() call. Anything more (attachments, multiple channels) is variation on that theme.
Attachments for structured messages. Webhook text is plain-ish markdown; rich messages use attachments, which the underlying nexylan/slack library models with a fluent API:
$this->slack->attach([
'fallback' => 'Deploy failed',
'color' => 'danger',
'title' => 'Deploy #1234 failed',
'title_link' => 'https://ci.example.com/builds/1234',
'fields' => [
['title' => 'Environment', 'value' => 'production', 'short' => true],
['title' => 'Commit', 'value' => 'a1b2c3d', 'short' => true],
],
])->send('Build failure');
Per-message overrides. The configured channel, username, and icon are defaults — you can override them per send on the fluent chain:
$this->slack->to('#alerts')->from('Deploy Bot')->send('...');
This is the single most useful trick in the package: one webhook, many channels.
Wire it into Symfony notifications/events rather than calling it ad-hoc. A listener on kernel.exception or a domain event keeps the Slack call out of your business code and makes it testable. Slack calls are I/O — never let them sit in a transaction or on a request path if you can queue instead.
Async sending via Messenger. The bundle ships no async support; wrap sends in a SendSlackMessage message + handler and dispatch it. This gives you retries, failure transport, and keeps web requests fast when Slack is slow:
#[AsMessageHandler]
final class SendSlackMessageHandler
{
public function __construct(private Client $slack) {}
public function __invoke(SendSlackMessage $m): void
{
$this->slack->to($m->channel)->send($m->text);
}
}
Testing. The client is a concrete class, so either mock it ($this->createMock(Client::class)) and assert on send()/attach() calls, or bind a fake in a test kernel. For integration tests, point the config at a fake HTTP server or stub the transport in the underlying Guzzle client.
Multiple workspaces. There is no built-in multi-client support. If you need two webhooks, define a second service manually, or decorate Client with a small dispatcher that holds several instances — cleaner than trying to abuse the bundle config.
composer constraints fiddling or a fork. The underlying nexylan/slack is a fork of maknz/slack, and both have long release gaps. Plan for the possibility of vendoring or patching.usleep() or (better) queue with a rate-limited worker.icon is ambiguous by design. A value starting with : is treated as an emoji (:ghost:), anything else as an image URL. A typo silently renders a broken avatar, not an error.link_names must be explicit. @user and #channel mentions only render as mentions if link_names: true is set; otherwise they appear as literal text. Same trap as most Slack libraries.allow_markdown and markdown_in_attachments control which attachment fields get parsed. A field that "isn't formatting" is usually missing from markdown_in_attachments.mrkdwn elements, sections) won't be available here — decide up front whether you're OK shipping legacy-format messages.ok vs. invalid_payload/channel_not_found). The error is always in Slack's response, never thrown as a nice exception by default — wrap send() if you need loud failure.Nexy\Slack\Client with a facade you own, or (b) skip the bundle and instantiate the underlying library directly with your own factory — the bundle is only sugar over one service anyway.How can I help you explore Laravel packages today?