symfony/spot-hit-notifier
Symfony Notifier transport for Spot-Hit SMS. Configure via SPOTHIT_DSN with your API token and sender (from), with optional settings for long SMS and concatenation count validation. Links to Spot-Hit API docs and Symfony issue/PR channels.
Install the Package (via Composer):
composer require symfony/spot-hit-notifier
Note: Since this is a Symfony package, direct Laravel integration requires a wrapper or Symfony components. Use symfony/http-client and symfony/messenger as alternatives if needed.
Configure DSN in .env:
SPOTHIT_DSN=spothit://YOUR_SPOTHIT_TOKEN@default?from=YOURSENDER&smslong=1
YOUR_SPOTHIT_TOKEN with your Spot-Hit API key.from is optional (default: 5-digit phone number).smslong enables long SMS (set to 1 for messages >160 chars).First Use Case: Send an SMS
Use Laravel’s Notification facade or Symfony’s Notifier component (if integrated via a bridge). Example with Laravel’s Notification:
use Illuminate\Support\Facades\Notification;
use App\Notifications\SpotHitSMS;
Notification::route('spot-hit', '+33612345678')
->notify(new SpotHitSMS('Your verification code is: 123456'));
Create a custom SpotHitSMS notification class extending Illuminate\Notifications\Notification.
Since Laravel lacks native Symfony Notifier support, create a wrapper class to bridge the gap:
// app/Services/SpotHitNotifier.php
namespace App\Services;
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Transport\SpotHitTransport;
use Symfony\Component\Notifier\Message\SmsMessage;
class SpotHitNotifier
{
public function __construct(private Notifier $notifier)
{
$this->notifier = $notifier;
}
public function sendSMS(string $phone, string $message): void
{
$transport = new SpotHitTransport('spothit://' . env('SPOTHIT_DSN'));
$this->notifier->send(new SmsMessage($message), $phone, $transport);
}
}
Register the service in AppServiceProvider:
public function register()
{
$this->app->singleton(SpotHitNotifier::class, function ($app) {
return new SpotHitNotifier(new Notifier());
});
}
Extend Laravel’s NotificationChannel to use Spot-Hit:
// app/Channels/SpotHitChannel.php
namespace App\Channels;
use Illuminate\Notifications\Notification;
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Transport\SpotHitTransport;
use Symfony\Component\Notifier\Message\SmsMessage;
class SpotHitChannel
{
public function __construct(private Notifier $notifier)
{}
public function send($notifiable, Notification $notification)
{
$message = $notification->toSpotHit($notifiable);
$transport = new SpotHitTransport('spothit://' . env('SPOTHIT_DSN'));
$this->notifier->send($message, $notifiable->route['spot-hit'], $transport);
}
}
Register the channel in config/services.php:
'channels' => [
'spot-hit' => [
'driver' => 'spot-hit',
],
],
// app/Notifications/SpotHitSMS.php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use Symfony\Component\Notifier\Message\SmsMessage;
class SpotHitSMS extends Notification
{
public function __construct(private string $message)
{}
public function toSpotHit($notifiable)
{
return new SmsMessage($this->message);
}
}
use App\Notifications\SpotHitSMS;
use Illuminate\Support\Facades\Notification;
Notification::route('spot-hit', '+33612345678')
->notify(new SpotHitSMS('Hello from Laravel!'));
Configure smslong in .env and validate message length in your notification class:
public function toSpotHit($notifiable)
{
$message = $this->message;
if (strlen($message) > 160 && env('SPOTHIT_SMSLONG', 0) !== '1') {
throw new \RuntimeException('Long SMS not enabled in config.');
}
return new SmsMessage($message);
}
Symfony Dependency Conflicts:
HttpClient and Notifier. If conflicts arise (e.g., version mismatches), use Laravel’s HttpClient or Guzzle as a drop-in replacement:
// Replace Symfony's HttpClient with Laravel's
$client = new \Illuminate\Http\Client\PendingRequest();
Symfony\Component\Notifier\Transport\AbstractTransport and override __send() to use Laravel’s HTTP client.DSN Configuration Errors:
SPOTHIT_DSN formats (e.g., missing from or smslong) will throw cryptic exceptions.$dsn = 'spothit://TOKEN@default?from=FROM&smslong=1';
$parts = parse_url($dsn);
// Log $parts['query'] to verify parameters.
Message Length Rejection:
smslongnbr).$expectedSmsCount = ceil(strlen($message) / 160);
if ($expectedSmsCount !== (int) env('SPOTHIT_SMSLONGNBR', 0)) {
throw new \RuntimeException("Message length mismatch. Expected {$expectedSmsCount} SMS.");
}
Rate Limiting:
use Symfony\Component\Notifier\Exception\TransportException;
use Symfony\Component\Notifier\Transport\RetryStrategy;
$transport = new SpotHitTransport($dsn, new RetryStrategy(3, 1000));
Enable Symfony Notifier Debugging:
Add this to config/services.php to log transport interactions:
'notifier' => [
'debug' => env('APP_DEBUG', false),
],
Check Laravel logs for Symfony\Component\Notifier\ entries.
Mock Spot-Hit in Tests:
Use Laravel’s HttpClient mocking to test without hitting Spot-Hit’s API:
use Illuminate\Support\Facades\Http;
Http::fake([
'api.spot-hit.com' => Http::response('{"status":"success"}'),
]);
// Test your notification logic here.
Validate API Responses:
Spot-Hit may return non-200 status codes (e.g., 400 for invalid numbers). Handle these in your transport class:
public function __send(SmsMessage $message, $to): void
{
$response = Http::post('https://api.spot-hit.com/sms', [
'to' => $to,
'message' => $message->getContent(),
]);
if ($response->status() !== 200) {
throw new TransportException('Spot-Hit API error: ' . $response->body());
}
}
Custom Transport Logic:
Extend Symfony\Component\Notifier\Transport\AbstractTransport to add features like:
Event Listeners:
Use Laravel’s events to react to notification failures:
// app/Providers/EventServiceProvider.php
protected $listen = [
\Symfony\Component\Notifier\Exception\TransportException::class => [
\App\Listeners\HandleSpotHitFailure::class,
],
];
Dynamic DSN Configuration: Load the DSN dynamically (e.g., from a database) for multi-tenant apps:
$
How can I help you explore Laravel packages today?