Routing with PHP Attributes
PHP Attributes provide a clean, decorator-based alternative to defining routes using the TelegramRouter facade. This approach keeps route definitions close to their handler methods, improving code organization and readability.
Overview
Section titled “Overview”Instead of registering routes through the facade:
TelegramRouter::onCommand('/start', function(CommandData $data) { // Handle /start command});You can define routes using attributes directly on controller methods:
#[OnCommand('/start')]public function handleStart(CommandData $data) { // Handle /start command}Getting Started
Section titled “Getting Started”Basic Usage
Section titled “Basic Usage”Create a controller class and decorate methods with routing attributes:
<?php
namespace App\Telegram\Handlers;
use HybridGram\Core\Routing\Attributes\OnCommand;use HybridGram\Core\Routing\Attributes\OnTextMessage;use HybridGram\Core\Routing\RouteData\CommandData;use HybridGram\Core\Routing\RouteData\TextMessageData;
class BotHandler{ #[OnCommand('/start')] public function handleStart(CommandData $data): void { $telegram = app(\HybridGram\Telegram\TelegramBotApi::class); $telegram->sendMessage($data->getChat()->id, 'Welcome!'); }
#[OnTextMessage] public function handleMessage(TextMessageData $data): void { // Handle all text messages }}Registration
Section titled “Registration”Routes defined with attributes are automatically discovered and registered during application bootstrap. The framework scans your application’s classes and registers any routes defined with routing attributes.
To enable attribute-based routing, ensure the AttributeRouteRegistrar is called in your application’s service provider or bootstrap file.
Available Attributes
Section titled “Available Attributes”Message Handling
Section titled “Message Handling”OnTextMessage
Section titled “OnTextMessage”Handle text messages:
#[OnTextMessage]public function handleMessage(TextMessageData $data): void { // Handle all text messages}
#[OnTextMessage(pattern: 'hello')]public function handleGreeting(TextMessageData $data): void { // Handle messages containing 'hello'}OnCommand
Section titled “OnCommand”Handle Telegram commands:
#[OnCommand('/start')]public function handleStart(CommandData $data): void { // Handle /start command}
#[OnCommand('/user:*')]public function handleUserCommand(CommandData $data): void { // Handle /user:* with parameters}OnCallbackQuery
Section titled “OnCallbackQuery”Handle inline button presses:
#[OnCallbackQuery(pattern: 'menu:*')]public function handleMenuCallback(CallbackQueryData $data): void { // Handle callback queries like 'menu:home'}Media Handling
Section titled “Media Handling”Handle various media types:
#[OnPhoto]public function handlePhoto(PhotoData $data): void {}
#[OnDocument]public function handleDocument(DocumentData $data): void {}
#[OnAudio]public function handleAudio(AudioData $data): void {}
#[OnVideo]public function handleVideo(VideoData $data): void {}
#[OnVoice]public function handleVoice(VoiceData $data): void {}
#[OnLocation]public function handleLocation(LocationData $data): void {}
#[OnContact]public function handleContact(ContactData $data): void {}Chat Member Events
Section titled “Chat Member Events”#[OnChatMember]public function handleChatMember(ChatMemberUpdatedData $data): void {}
#[OnMyChatMember]public function handleBotChatMember(ChatMemberUpdatedData $data): void {}Other Events
Section titled “Other Events”#[OnPoll]public function handlePoll(PollData $data): void {}
#[OnInlineQuery]public function handleInlineQuery(InlineQueryData $data): void {}
#[OnAny]public function handleAny(UpdateData $data): void {}
#[OnFallback]public function handleFallback(FallbackData $data): void {}Filtering & Conditions
Section titled “Filtering & Conditions”Chat Types
Section titled “Chat Types”Limit routes to specific chat types:
use HybridGram\Core\Routing\Attributes\ChatTypes;use HybridGram\Core\Routing\ChatType;
#[OnCommand('/admin')]#[ChatTypes([ChatType::PRIVATE, ChatType::GROUP])]public function handleAdminCommand(CommandData $data): void { // Only works in private chats and groups}Bot Selection
Section titled “Bot Selection”Target specific bots:
use HybridGram\Core\Routing\Attributes\ForBot;
#[OnCommand('/start')]#[ForBot('main')]public function handleStart(CommandData $data): void { // Only for 'main' bot}Middleware
Section titled “Middleware”Apply middleware to routes:
use HybridGram\Core\Routing\Attributes\TgMiddlewares;use App\Telegram\Middleware\AuthMiddleware;
#[OnCommand('/admin')]#[TgMiddlewares([AuthMiddleware::class])]public function handleAdmin(CommandData $data): void { // AuthMiddleware runs before this handler}State-Based Routing
Section titled “State-Based Routing”Route based on user state:
use HybridGram\Core\Routing\Attributes\FromUserState;use HybridGram\Core\Routing\Attributes\ToUserState;
#[OnTextMessage]#[FromUserState('waiting_name')]#[ToUserState('name_received')]public function handleNameInput(TextMessageData $data): void { // Only processes if user is in 'waiting_name' state // Transitions to 'name_received' state after execution}Chat State
Section titled “Chat State”Route based on chat state:
use HybridGram\Core\Routing\Attributes\FromChatState;use HybridGram\Core\Routing\Attributes\ToChatState;
#[OnTextMessage]#[FromChatState('setup_mode')]public function handleSetup(TextMessageData $data): void { // Only when chat is in 'setup_mode'}Combining Attributes
Section titled “Combining Attributes”You can combine multiple attributes on a single method:
#[OnTextMessage(pattern: 'price:*')]#[ForBot('main')]#[ChatTypes([ChatType::PRIVATE])]#[FromUserState('shopping')]public function handlePriceQuery(TextMessageData $data): void { // This handler only triggers when ALL conditions are met: // - Message text contains pattern 'price:*' // - Bot is 'main' // - Chat is private // - User is in 'shopping' state}Best Practices
Section titled “Best Practices”Organization
Section titled “Organization”Group related handlers in dedicated controller classes:
<?php
namespace App\Telegram\Handlers;
class CommandHandler{ #[OnCommand('/start')] public function handleStart(CommandData $data): void {}
#[OnCommand('/help')] public function handleHelp(CommandData $data): void {}}
class MessageHandler{ #[OnTextMessage] public function handleMessage(TextMessageData $data): void {}}Discoverable Paths
Section titled “Discoverable Paths”Ensure your handler classes are in discoverable locations. By default, the framework scans:
app/Telegram/app/Handlers/
Configure additional paths in your configuration as needed.
Type Safety
Section titled “Type Safety”Always type-hint the data parameter:
// ✅ Good - type hints prevent bugs#[OnCommand('/start')]public function handleStart(CommandData $data): void {}
// ❌ Avoid - loses type safety#[OnCommand('/start')]public function handleStart($data): void {}Comparison with Facade-Based Routing
Section titled “Comparison with Facade-Based Routing”Facade-Based (Traditional)
Section titled “Facade-Based (Traditional)”TelegramRouter::onCommand('/start', function(CommandData $data) { // ...});
TelegramRouter::onTextMessage(function(TextMessageData $data) { // ...});Attribute-Based (Modern)
Section titled “Attribute-Based (Modern)”class BotHandler{ #[OnCommand('/start')] public function handleStart(CommandData $data): void { // ... }
#[OnTextMessage] public function handleMessage(TextMessageData $data): void { // ... }}Both approaches work equally well. Choose based on your project’s preference:
- Attributes: Better for large projects with many handlers
- Facade: Better for small projects or when all routes are in one place
Advanced Topics
Section titled “Advanced Topics”Custom Attributes
Section titled “Custom Attributes”You can create custom attributes that extend TelegramRouteAttribute:
use HybridGram\Core\Routing\Attributes\TelegramRouteAttribute;use HybridGram\Core\Routing\TelegramRouteBuilder;
#[Attribute(Attribute::TARGET_METHOD)]final class OnVIP implements TelegramRouteAttribute{ public function registerRoute(TelegramRouteBuilder $builder, \Closure|string|array $action): void { // Custom registration logic $builder->onTextMessage($action) ->middleware(VIPCheckMiddleware::class); }}Attribute Caching
Section titled “Attribute Caching”In production, attribute routes are cached for better performance. Run:
php artisan config:cacheTo clear the cache during development, use:
php artisan config:clearSee Also
Section titled “See Also”- Basic Routing — Overview of routing concepts
- Middleware — Using middleware with routes
- States — Managing user and chat states