### Complete Integration Example: Model, Notification, Listener, and Sending Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoNotificationSent.md Demonstrates the setup for a User model with ExpoPushToken casting, an OrderConfirmed notification using the Expo channel, an event listener to record sent notifications and store push tickets, and the process of sending a notification. ```php // Model setup class User extends Model { use Notifiable; protected function casts(): array { return [ 'expo_token' => ExpoPushToken::class, ]; } public function routeNotificationForExpo(): ?ExpoPushToken { return $this->expo_token; } } // Notification definition class OrderConfirmed extends Notification { public function via($notifiable): array { return ['expo']; } public function toExpo($notifiable): ExpoMessage { return ExpoMessage::create('Order Confirmed') ->body('Your order has been confirmed'); } } // Event listener class RecordNotificationSent implements ShouldQueue { public function handle(ExpoNotificationSent $event): void { \Log::info('Order confirmation sent', [ 'user_id' => $event->notifiable->id, 'tickets' => count($event->tickets), ]); foreach ($event->tickets as $token => $ticketId) { PushTicket::create([ 'user_id' => $event->notifiable->id, 'ticket_id' => $ticketId, 'token' => $token, ]); } } } // Send notification $order = Order::find(1); $order->user->notify(new OrderConfirmed()); // → ExpoNotificationSent event dispatched // → Listener stores tickets in database ``` -------------------------------- ### Complete Production Application Configuration Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md A comprehensive configuration example for a production Laravel application using the Expo notification channel. This includes environment variables, service configuration, logging setup, and model routing. ```dotenv // .env APP_ENV=production EXPO_ACCESS_TOKEN=ExponentPushToken[...] ``` ```php // config/services.php 'expo' => [ 'access_token' => env('EXPO_ACCESS_TOKEN'), ], ``` ```php // config/logging.php 'expo' => [ 'driver' => 'daily', 'path' => storage_path('logs/expo/expo.log'), 'level' => 'info', 'days' => 14, ], ``` ```php // app/Models/User.php protected function casts(): array { return [ 'expo_token' => ExpoPushToken::class, ]; } public function routeNotificationForExpo(): ?ExpoPushToken { return $this->expo_token; } ``` ```php // app/Providers/EventServiceProvider.php protected $listen = [ ExpoNotificationSent::class => [ RecordExpoNotificationSent::class, ], NotificationFailed::class => [ HandleFailedExpoNotifications::class, ], ]; ``` -------------------------------- ### Complete Laravel Expo Notification Example Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/README.md A comprehensive example demonstrating how to set up a User model, create a notification, send it via the Expo channel, and handle the results using events. ```php // 1. Define model with token class User extends Model { use Notifiable; protected function casts(): array { return [ 'expo_token' => ExpoPushToken::class, ]; } public function routeNotificationForExpo(): ?ExpoPushToken { return $this->expo_token; } } // 2. Create notification class OrderNotification extends Notification { public function via($notifiable): array { return ['expo']; } public function toExpo($notifiable): ExpoMessage { return ExpoMessage::create('Order Confirmed') ->body('Your order has been confirmed') ->priority('high') ->playSound() ->data([ 'order_id' => $this->order->id, 'action' => 'view_order', ]); } } // 3. Send notification $user->notify(new OrderNotification()); // 4. Handle results with events class HandleExpoEvents { public function handle(ExpoNotificationSent $event) { // Store tickets for receipt polling foreach ($event->tickets as $token => $ticketId) { PushTicket::create([ 'ticket_id' => $ticketId, 'token' => $token, ]); } } } ``` -------------------------------- ### Install Expo Notifications Channel Source: https://github.com/laravel-notification-channels/expo/blob/main/README.md Install the package via Composer. ```bash composer require laravel-notification-channels/expo ``` -------------------------------- ### Install Zlib Extension on Ubuntu/Debian Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Install the PHP Zlib extension on Ubuntu or Debian systems using apt-get. This is required for automatic payload compression by the Expo gateway. ```bash # Ubuntu/Debian sudo apt-get install php-zlib ``` -------------------------------- ### Exception Handling Examples Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/Exceptions.md Provides examples of how to catch and handle the custom exceptions thrown by the Expo notification channel. ```APIDOC ## Exception Handling Examples ### Catching SendNotification Exceptions ```php use NotificationChannels\Expo\Exceptions\CouldNotSendNotification; try { $user->notify(new OrderNotification()); } catch (CouldNotSendNotification $e) { // Log the error \Log::error('Push notification failed: ' . $e->getMessage()); // Notify admin \Notification::route('mail', 'admin@example.com') ->notify(new AlertNotification('Push notification failed')); // Optionally re-throw or return error response throw $e; } ``` ### Catching Receipt Exceptions ```php use NotificationChannels\Expo\Exceptions\CouldNotGetReceipts; try { $receipts = $gateway->getReceipts($ticketIds); // Process receipts... } catch (CouldNotGetReceipts $e) { \Log::error('Failed to fetch receipts: ' . $e->getMessage()); // Schedule retry dispatch(new RetryReceiptPolling($ticketIds)) ->delay(now()->addMinutes(5)); } ``` ``` -------------------------------- ### Complete Usage Example Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/AsExpoPushToken.md Demonstrates creating a user, reading the token as an ExpoPushToken instance, updating the token, and setting it to null. Includes type checking and comparison. ```php use NotificationChannels\Expo\ExpoPushToken; // Create user with token $user = User::create([ 'name' => 'John Doe', 'expo_token' => 'ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9]', ]); // Reading: automatically cast to ExpoPushToken $token = $user->expo_token; // Type: ExpoPushToken instance // Can call ExpoPushToken methods: echo $token->asString(); // 'ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9]' echo $token; // 'ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9]' json_encode($token); // "ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9]" // Type checking $token instanceof ExpoPushToken; // true // Comparison if ($token->equals('ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9]')) { // Token matches } // Updating: automatically stored as string $user->update([ 'expo_token' => 'ExpoPushToken[newToken123]', ]); // The new token is validated and stored $user->refresh(); echo $user->expo_token; // 'ExpoPushToken[newToken123]' // Setting to null $user->update(['expo_token' => null]); $user->expo_token; // null // Sending notifications $user->notify(new OrderNotification()); // Internally calls routeNotificationForExpo() which returns ExpoPushToken ``` -------------------------------- ### Optional Access Token Configuration Example Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoServiceProvider.md This PHP code shows how to configure an optional Expo access token in the services.php configuration file. ```php 'expo' => [ 'access_token' => env('EXPO_ACCESS_TOKEN'), ] ``` -------------------------------- ### API Integration Example for Registering Device Tokens Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Shows a real-world endpoint in a Laravel API controller for registering device tokens, utilizing the StoreDeviceRequest for validation. ```php namespace App\Http\Controllers\Api; use App\Http\Requests\StoreDeviceRequest; use App\Http\Resources\DeviceResource; use App\Models\Device; class DeviceController { public function store(StoreDeviceRequest $request) { // Token is already validated by StoreDeviceRequest $device = auth()->user()->devices()->create( $request->validated() ); return new DeviceResource($device); } public function update(UpdateDeviceRequest $request, Device $device) { // Authorize user owns device $this->authorize('update', $device); // Token is already validated $device->update($request->validated()); return new DeviceResource($device); } } // Form request with validation rule class StoreDeviceRequest extends FormRequest { public function rules(): array { return [ 'device_id' => ['required', 'string', 'unique_for:devices'], 'expo_token' => ['required', ExpoPushToken::rule()], ]; } public function messages(): array { return [ 'expo_token.regex' => 'The token is not a valid Expo push token.', ]; } } ``` -------------------------------- ### Environment Variable for Access Token Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoServiceProvider.md This example demonstrates how to set the Expo access token in the .env file for enhanced security. ```dotenv EXPO_ACCESS_TOKEN=your-access-token-here ``` -------------------------------- ### Install Zlib Extension on macOS Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Install the PHP Zlib extension on macOS using Homebrew. This enables automatic payload compression for Expo notifications. ```bash # macOS (Homebrew) brew install php@8.3 --with-zlib ``` -------------------------------- ### Install Zlib Extension in Dockerfile Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Enable the Zlib extension for PHP within a Docker environment by adding the `docker-php-ext-install` command to your Dockerfile. This is necessary for payload compression. ```dockerfile # Docker (Dockerfile) RUN docker-php-ext-install zlib ``` -------------------------------- ### Complete ExpoPushToken Usage Example Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md Demonstrates various ways to use the ExpoPushToken class, including creation from user input, conversion to string, JSON serialization, and comparison with other tokens. Also shows model casting usage. ```php use NotificationChannels\Expo\ExpoPushToken; // Create from user input $token = ExpoPushToken::make($request->input('token')); // Convert to string $tokenString = $token->asString(); $tokenString = (string) $token; // Same thing // JSON serialization $json = json_encode(['token' => $token]); // {"token":"ExpoPushToken[abc123]"} // Comparison if ($token->equals($otherToken)) { // They're the same token } // Model casting class User extends Model { protected function casts(): array { return ['expo_token' => ExpoPushToken::class]; } } $user = User::find(1); $user->expo_token instanceof ExpoPushToken; // true ``` -------------------------------- ### ExpoErrorType Type Checking Examples Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/types.md Demonstrates how to use helper methods, direct comparison, access the string value, and utilize match expressions with ExpoErrorType. ```php $errorType = ExpoErrorType::DeviceNotRegistered; // Using helper method if ($errorType->isDeviceNotRegistered()) { // Remove expired token } // Using comparison if ($errorType === ExpoErrorType::MessageTooBig) { // Reduce payload size } // Accessing string value echo $errorType->value; // 'DeviceNotRegistered' // Using in match expression $action = match ($errorType) { ExpoErrorType::DeviceNotRegistered => 'remove_token', ExpoErrorType::MessageTooBig => 'reduce_payload', ExpoErrorType::MessageRateExceeded => 'retry_later', ExpoErrorType::MismatchSenderId => 'alert_admin', ExpoErrorType::InvalidCredentials => 'alert_admin', }; ``` -------------------------------- ### Send Notification via ExpoChannel Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoChannel.md Example of a notification class using the ExpoChannel. Implement the `toExpo()` method to return an `ExpoMessage`. ```php use Illuminate\Notifications\Notification; use NotificationChannels\Expo\ExpoMessage; class AlertNotification extends Notification { public function via($notifiable): array { return ['expo']; // Uses ExpoChannel } public function toExpo($notifiable): ExpoMessage { return ExpoMessage::create('Alert') ->body('Something important happened') ->priority('high'); } } // Usage: $user->notify(new AlertNotification()); ``` -------------------------------- ### ExpoMessage::create() Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoMessage.md Creates a new ExpoMessage instance with an initial title and body. This is the recommended way to start building a notification payload. ```APIDOC ## ExpoMessage::create() ### Description Create a new message instance with an initial title and body. ### Method `public static function create(string $title = '', string $body = ''): self` ### Parameters #### Path Parameters - **title** (string) - Optional - The title to display in the notification - **body** (string) - Optional - The message body to display in the notification ### Response #### Success Response - **self** (self) - The newly created ExpoMessage instance ### Request Example ```php $message = ExpoMessage::create('Welcome', 'You have a new message'); ``` ``` -------------------------------- ### Handle MessageTooBig Error Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Example of how to handle a 'MessageTooBig' error by logging a warning. This suggests reducing the payload size or splitting the message. ```php if ($error->type->isMessageTooBig()) { \Log::warning('Payload too large, consider splitting message', [ 'token' => (string) $error->token, ]); } ``` -------------------------------- ### Complete Usage Example for StoreDeviceRequest Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Demonstrates how to use the ExpoPushToken::rule() within a Laravel FormRequest to validate an Expo push token. Includes custom error messages. ```php use Illuminate\Foundation\Http\FormRequest; use NotificationChannels\Expo\ExpoPushToken; class StoreDeviceRequest extends FormRequest { public function rules(): array { return [ 'device_id' => ['required', 'string', 'min:2', 'max:255'], 'expo_token' => ['required', ExpoPushToken::rule()], ]; } public function messages(): array { return [ 'expo_token.required' => 'A push token is required', 'expo_token.regex' => 'The provided token is not a valid Expo push token', ]; } } // In Controller class DeviceController { public function store(StoreDeviceRequest $request) { // Token is already validated $token = $request->input('expo_token'); auth()->user()->update([ 'expo_token' => $token, ]); return response()->json(['message' => 'Device registered']); } } ``` -------------------------------- ### ExpoPushToken->__toString() Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md Magic method to get the string representation of the Expo push token. ```APIDOC ## instance->__toString() ### Description Get the string representation (magic method). ### Return Type `string` ### Returns The token string ### Example ```php $token = ExpoPushToken::make('ExpoPushToken[abc123]'); echo $token; // ExpoPushToken[abc123] (string) $token; // ExpoPushToken[abc123] ``` ``` -------------------------------- ### Migration: Adding ExpoPushToken Cast to Existing Model Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/AsExpoPushToken.md Guides on migrating existing models to use the ExpoPushToken cast. It shows how to add the cast to the model's `casts` array and explains that existing string tokens remain compatible and are automatically converted upon access. ```php // Before: tokens stored as strings, no validation class User extends Model { // No cast defined } // After: add cast class User extends Model { protected function casts(): array { return [ 'expo_token' => ExpoPushToken::class, ]; } } // Existing string tokens in database are still compatible // They're automatically converted to ExpoPushToken on access $user->refresh(); $user->expo_token instanceof ExpoPushToken; // true ``` -------------------------------- ### Get Rule Instance via ExpoPushToken Convenience Method Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Obtain an ExpoPushTokenRule instance using the `ExpoPushToken::rule()` static method for cleaner integration. ```php use NotificationChannels\Expo\ExpoPushToken; $rule = ExpoPushToken::rule(); // Returns ExpoPushTokenRule instance // Use in form requests public function rules(): array { return [ 'device_token' => ['required', ExpoPushToken::rule()], ]; } ``` -------------------------------- ### Get String Representation of ExpoPushToken Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md The `asString` method returns the raw push token string. This is useful for displaying or logging the token. ```php $token = ExpoPushToken::make('ExpoPushToken[abc123...]'); $string = $token->asString(); // 'ExpoPushToken[abc123...]' ``` -------------------------------- ### User Model Setup for Expo Notifications Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoServiceProvider.md Configures the User model to handle Expo push tokens and define a method to retrieve the Expo token for routing notifications. ```php // app/Models/User.php use Illuminate\Notifications\Notifiable; use NotificationChannels\Expo\ExpoPushToken; class User extends Model { use Notifiable; protected function casts(): array { return [ 'expo_token' => ExpoPushToken::class, ]; } public function routeNotificationForExpo(): ?ExpoPushToken { return $this->expo_token; } } ``` -------------------------------- ### Example Expo Notification Exception Messages Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/Exceptions.md Provides sample error messages for `CouldNotSendNotification` and `CouldNotGetReceipts` that can be logged for debugging purposes. These messages are designed for direct logging without further formatting. ```php // CouldNotSendNotification examples: "You must provide an instance of Notifiable." "Notification is missing the toExpo method." "Expo responded with an error: {\"code\":\"RATE_LIMIT_EXCEEDED\"}" // CouldNotGetReceipts examples: "Expo responded with an error while fetching receipts: {\"code\":\"INVALID_REQUEST\"}" ``` -------------------------------- ### ExpoPushToken Validation Example Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md Illustrates valid and invalid Expo push token formats that will pass or fail validation, respectively. Throws InvalidArgumentException on failure. ```php // Valid tokens: // ExponentPushToken[exKN0aQR5b8ZwU29wZPJk9] // ExpoPushToken[exKN0aQR5b8ZwU29wZPJk9] // Invalid (throws InvalidArgumentException): // ExpoPushToken[short] // Too short // InvalidToken[abc] // Wrong prefix // ExpoPushToken[abc // Missing closing bracket ``` -------------------------------- ### AsExpoPushToken::set() Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/AsExpoPushToken.md Converts an ExpoPushToken instance or a string into a database-compatible string representation. It first normalizes the value using `get()` and then returns its string form or null. ```APIDOC ## AsExpoPushToken::set() ### Description Converts an `ExpoPushToken` instance or a string into a database-compatible string representation. It normalizes the value and returns its string form or null. ### Method `public function set($model, string $key, $value, array $attributes): ?string` ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```php // Assigning a string $user->expo_token = 'ExpoPushToken[xyz789]'; // Assigning an ExpoPushToken instance $user->expo_token = ExpoPushToken::make('ExpoPushToken[xyz789]'); ``` ### Response #### Success Response - **string** (`?string`) - The string representation of the push token or null. #### Response Example ```json { "example": "ExpoPushToken[xyz789]" } ``` ### Throws - **InvalidArgumentException** - If the value is invalid (via `get()`). ``` -------------------------------- ### Complete Expo Notification Failure Handling Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Provides a comprehensive example of handling failed Expo notifications, including logging and specific error type management using a match expression. ```php use Illuminate\Notifications\Events\NotificationFailed; use NotificationChannels\Expo\ExpoChannel; use NotificationChannels\Expo\ExpoError; class HandleFailedExpoNotifications { public function handle(NotificationFailed $event): void { if ($event->channel !== ExpoChannel::NAME) { return; } /** @var ExpoError $error */ $error = $event->data; // Log the failure \Log::warning('Expo notification failed', [ 'type' => $error->type->value, 'message' => $error->message, 'token' => (string) $error->token, 'user_id' => $event->notifiable->id ?? null, ]); // Handle specific error types match (true) { $error->type->isDeviceNotRegistered() => $this->removeToken($event->notifiable), $error->type->isMessageTooBig() => $this->logPayloadIssue($error), $error->type->isMessageRateExceeded() => $this->scheduleRetry($event), $error->type->isMismatchSenderId() => $this->alertAdminMismatch(), $error->type->isInvalidCredentials() => $this->alertAdminCredentials(), default => null, }; } private function removeToken($user): void { $user->update(['expo_token' => null]); } private function logPayloadIssue(ExpoError $error): void { \Log::error('Message payload exceeds 4KiB limit'); } private function scheduleRetry(NotificationFailed $event): void { \Queue::later(60, new RetryNotification( $event->notifiable, $event->notification )); } private function alertAdminMismatch(): void { \Notification::route('mail', 'admin@example.com') ->notify(new ExpoAlert('FCM Sender ID mismatch detected')); } private function alertAdminCredentials(): void { \Notification::route('mail', 'admin@example.com') ->notify(new ExpoAlert('Invalid APN credentials - regenerate immediately')); } } ``` -------------------------------- ### Database Schema for Expo Push Tokens Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Define your database schema to store Expo push tokens. This example shows a single token per user and multiple tokens per user via a separate devices table. ```php // Migration Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->string('expo_token')->nullable(); // Store token here $table->timestamps(); }); // Or for multiple devices Schema::create('devices', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->string('expo_token'); $table->string('device_name')->nullable(); $table->timestamps(); }); ``` -------------------------------- ### Handle MessageRateExceeded Error with Retry Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Example of handling a 'MessageRateExceeded' error by implementing an exponential backoff strategy for retrying notifications using Laravel's queue system. ```php if ($error->type->isMessageRateExceeded()) { // Retry after a delay $delayMinutes = $attempt * 2; // Exponential backoff \Queue::later( now()->addMinutes($delayMinutes), new RetryNotification($user, $notification) ); } ``` -------------------------------- ### Run Tests Source: https://github.com/laravel-notification-channels/expo/blob/main/README.md Execute the project's test suite using Composer. ```bash composer test ``` -------------------------------- ### Expo Configuration with Access Token Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Set up the Expo channel in `config/services.php` with an access token defined via an environment variable. Ensure the corresponding .env file contains the token. ```php // config/services.php return [ // ... other services 'expo' => [ 'access_token' => env('EXPO_ACCESS_TOKEN'), ], ]; // .env EXPO_ACCESS_TOKEN=your-actual-expo-access-token-here ``` -------------------------------- ### Case Sensitivity and Whitespace Validation Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Illustrates that the Expo push token validation is case-sensitive and exact. The 'Valid' example shows an exact case match, while the 'Invalid' examples demonstrate failures due to incorrect case or leading/trailing whitespace. ```php // Valid 'ExpoPushToken[ABC123...]' // Invalid 'exponentpushtoken[abc]' ' ExpoPushToken[abc] ' ``` -------------------------------- ### Instantiate ExpoGatewayUsingGuzzle Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoGatewayUsingGuzzle.md Demonstrates how to create an instance of the ExpoGatewayUsingGuzzle class. You can use it with default settings, provide an Expo API access token for enhanced security, or supply a custom Guzzle handler stack for testing purposes. ```php use NotificationChannels\Expo\Gateway\ExpoGatewayUsingGuzzle; use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; // Default usage (no access token) $gateway = new ExpoGatewayUsingGuzzle(); // With access token $gateway = new ExpoGatewayUsingGuzzle('your-expo-access-token'); // With custom handler (testing) $mock = new MockHandler([ new Response(200, [], json_encode(['data' => []])), ]); $handler = HandlerStack::create($mock); $gateway = new ExpoGatewayUsingGuzzle(null, $handler); ``` -------------------------------- ### Create an InMemory Expo Gateway for Testing Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoGateway.md Implement the ExpoGateway interface for testing purposes. This mock allows you to capture sent notifications and simulate responses. ```php use NotificationChannels\Expo\Gateway\ExpoGateway; use NotificationChannels\Expo\Gateway\ExpoEnvelope; use NotificationChannels\Expo\Gateway\ExpoResponse; class InMemoryExpoGateway implements ExpoGateway { private array $sentEnvelopes = []; private array $storedReceipts = []; public function sendPushNotifications(ExpoEnvelope $envelope): ExpoResponse { $this->sentEnvelopes[] = $envelope; // Generate fake ticket IDs $tickets = []; foreach ($envelope->recipients as $idx => $token) { $tickets[$idx] = 'ticket-' . uniqid(); } return ExpoResponse::ok($tickets); } public function getReceipts(array $ticketIds): array { $receipts = []; foreach ($ticketIds as $id) { $receipts[$id] = $this->storedReceipts[$id] ?? ['status' => 'ok']; } return $receipts; } public function getSentEnvelopes(): array { return $this->sentEnvelopes; } public function storeReceipt(string $ticketId, array $receipt): void { $this->storedReceipts[$ticketId] = $receipt; } } // Usage in tests: public function test_notification_sent_via_expo() { $gateway = new InMemoryExpoGateway(); $this->app->bind(ExpoGateway::class, $gateway); $user = User::factory()->create(); $user->notify(new TestNotification()); $envelopes = $gateway->getSentEnvelopes(); $this->assertCount(1, $envelopes); } ``` -------------------------------- ### Handle MismatchSenderId Error Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Example of handling a 'MismatchSenderId' error by logging a critical message and alerting an administrator about the FCM credential misconfiguration. ```php if ($error->type->isMismatchSenderId()) { \Log::critical('FCM credentials mismatch - all Android notifications failing'); // Alert admin to reconfigure credentials \Notification::route('mail', 'admin@example.com') ->notify(new ConfigurationAlert('FCM Sender ID mismatch')); } ``` -------------------------------- ### Override Expo Gateway Binding for Testing Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Override the ExpoGateway binding in your test setup to use a test implementation, such as an in-memory gateway. ```php // In your test setup use NotificationChannels\Expo\Gateway\ExpoGateway; use Tests\InMemoryExpoGateway; public function setUp(): void { parent::setUp(); // Bind test gateway $this->app->bind(ExpoGateway::class, InMemoryExpoGateway::class); } ``` -------------------------------- ### Create ExpoMessage Instance Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoMessage.md Use the static `create()` factory method to instantiate an ExpoMessage with an initial title and body. ```php use NotificationChannels\Expo\ExpoMessage; $message = ExpoMessage::create('Welcome', 'You have a new message'); ``` -------------------------------- ### Exception Handling for Notification Sending Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/README.md Example of how to catch `CouldNotSendNotification` exceptions when sending notifications via the Expo channel. Logs the error message. ```php try { $user->notify(new Notification()); } catch (CouldNotSendNotification $e) { \Log::error('Notification failed: ' . $e->getMessage()); } ``` -------------------------------- ### Basic Expo Configuration (No Token) Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Configure the Expo channel in `config/services.php` without an access token. This sends notifications without additional security layers. ```php // config/services.php return [ // ... other services 'expo' => [ // No access token - notifications sent without additional security ], ]; ``` -------------------------------- ### Handle InvalidCredentials Error Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Example of handling an 'InvalidCredentials' error by logging a critical message and notifying an administrator to regenerate iOS push credentials. ```php if ($error->type->isInvalidCredentials()) { \Log::critical('Invalid credentials - iOS notifications failing'); \Notification::route('mail', 'admin@example.com') ->notify(new ConfigurationAlert('Invalid APN credentials, regenerate with: expo build:ios -c')); } ``` -------------------------------- ### Create ExpoPushTokenRule Instance Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Use the static `make()` factory method to create a new instance of the ExpoPushTokenRule. ```php use NotificationChannels\Expo\ExpoPushTokenRule; $rule = ExpoPushTokenRule::make(); ``` -------------------------------- ### Check for DeviceNotRegistered Error Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoErrorType.md Example of how to check if an error is of type 'DeviceNotRegistered' and take action, such as removing the invalid Expo push token from the database. ```php if ($error->type->isDeviceNotRegistered()) { $user->update(['expo_token' => null]); } ``` -------------------------------- ### Constructor Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoGatewayUsingGuzzle.md Initializes the ExpoGatewayUsingGuzzle with an optional access token and Guzzle handler stack. The access token is used for authentication, and the handler stack is primarily for testing. ```APIDOC ## Constructor ```php public function __construct( #[SensitiveParameter] ?string $accessToken = null, ?HandlerStack $handler = null ): void ``` **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `$accessToken` | `?string` | `null` | Optional Expo API access token for additional security | | `$handler` | `?HandlerStack` | `null` | Optional Guzzle handler stack for custom HTTP behavior (primarily for testing) | **Notes:** - The `#[SensitiveParameter]` attribute marks the access token as sensitive - If an access token is provided, it's included in the `Authorization: Bearer` header - The handler parameter is useful for testing with mock HTTP responses **Example:** ```php // Default usage (no access token) $gateway = new ExpoGatewayUsingGuzzle(); // With access token $gateway = new ExpoGatewayUsingGuzzle('your-expo-access-token'); // With custom handler (testing) use GuzzleHttp\HandlerStack; use GuzzleHttp\ Handler\MockHandler; use GuzzleHttp\Psr7\Response; $mock = new MockHandler([ new Response(200, [], json_encode(['data' => []])), ]); $handler = HandlerStack::create($mock); $gateway = new ExpoGatewayUsingGuzzle(null, $handler); ``` ``` -------------------------------- ### Get Caster Class for Eloquent Casting Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md Returns the caster class name for Eloquent model casting. This method is called automatically by Laravel. ```php return 'NotificationChannels ollowfollow Expo Casts AsExpoPushToken'; ``` -------------------------------- ### ExpoEnvelope::make() Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoEnvelope.md Creates a new ExpoEnvelope instance. This is the primary way to instantiate an ExpoEnvelope, ensuring that the provided recipients and message are correctly packaged. ```APIDOC ## ExpoEnvelope::make() Creates a new ExpoEnvelope instance. ### Method `public static function make(array $recipients, ExpoMessage $message): self` ### Parameters #### Parameters - **$recipients** (array) - Required - Array of push tokens (must contain at least 1) - **$message** (ExpoMessage) - Required - The message to send to all recipients ### Return Type `self` ### Throws `InvalidArgumentException` if `$recipients` is empty ### Example ```php use NotificationChannels\Expo\Gateway\ExpoEnvelope; use NotificationChannels\Expo\ExpoMessage; use NotificationChannels\Expo\ExpoPushToken; $tokens = [ ExpoPushToken::make('ExpoPushToken[token1]'), ExpoPushToken::make('ExpoPushToken[token2]'), ]; $message = ExpoMessage::create('Hello') ->body('World'); $envelope = ExpoEnvelope::make($tokens, $message); ``` ``` -------------------------------- ### Set Convenience Priority Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoMessage.md Use convenience methods like `default()`, `normal()`, or `high()` as shortcuts to set specific priority levels. ```php $message->high(); // Equivalent to priority('high') ``` -------------------------------- ### Create ExpoEnvelope Instance Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoEnvelope.md Use the static `make` factory method to create an ExpoEnvelope. It requires an array of ExpoPushToken instances and an ExpoMessage. Ensure the recipients array is not empty, as it throws an InvalidArgumentException otherwise. ```php use NotificationChannels\Expo\Gateway\ExpoEnvelope; use NotificationChannels\Expo\ExpoMessage; use NotificationChannels\Expo\ExpoPushToken; $tokens = [ ExpoPushToken::make('ExpoPushToken[token1]'), ExpoPushToken::make('ExpoPushToken[token2]'), ]; $message = ExpoMessage::create('Hello') ->body('World'); $envelope = ExpoEnvelope::make($tokens, $message); ``` -------------------------------- ### Enable Background Launch for iOS Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoMessage.md Use `contentAvailable()` to enable background app launch for notifications on iOS. Ensure your app is configured for background tasks. ```php $message->contentAvailable(true); ``` -------------------------------- ### General Exception Handling for Fetching Receipts Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/Exceptions.md Illustrates how to handle `CouldNotGetReceipts` exceptions, including logging the failure and scheduling a retry mechanism. This ensures that receipt fetching attempts are managed robustly. ```php use NotificationChannels\Expo\Exceptions\CouldNotGetReceipts; try { $receipts = $gateway->getReceipts($ticketIds); // Process receipts... } catch (CouldNotGetReceipts $e) { \Log::error('Failed to fetch receipts: ' . $e->getMessage()); // Schedule retry dispatch(new RetryReceiptPolling($ticketIds)) ->delay(now()->addMinutes(5)); } ``` -------------------------------- ### ExpoMessage::create Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/README.md Fluent builder for constructing notification payloads. Allows setting title, body, priority, sound, and custom data. ```APIDOC ## ExpoMessage::create ### Description Fluent builder for constructing notification payloads. Allows setting title, body, priority, sound, and custom data. ### Method ```php ExpoMessage::create($title, $body) ->priority('high') ->playSound() ->data($array) ->toArray(): array ``` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example None ### Response #### Success Response None #### Response Example None ``` -------------------------------- ### Get Validation Rule for Form Requests Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushToken.md Provides a validation rule instance for validating push tokens within Laravel form requests. Use this rule in your `rules()` method. ```php use NotificationChannels ollowfollow Expo ExpoPushToken; public function rules(): array { return [ 'token' => ['required', ExpoPushToken::rule()], ]; } ``` -------------------------------- ### Expo Configuration for services.php Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/README.md Configuration snippet to add to `config/services.php` to enable the Expo notification channel. Requires an Expo API access token. ```php 'expo' => [ 'access_token' => env('EXPO_ACCESS_TOKEN'), ], ``` -------------------------------- ### Testing Expo Gateway with Service Provider Binding Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoServiceProvider.md Shows how to bind a custom or in-memory Expo gateway implementation to the service container during testing to mock notification sending. ```php // In your test use NotificationChannels\Expo\Gateway\ExpoGateway; use Tests\InMemoryExpoGateway; public function test_notification_sent() { $this->app->bind(ExpoGateway::class, InMemoryExpoGateway::class); // Your test assertions... } ``` -------------------------------- ### Get Per-Recipient Errors from Failed Response Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoResponse.md Retrieve an array of ExpoError instances for each recipient that failed. This is useful when a partial failure occurs. The array keys correspond to the original recipient index. ```php public function errors(): array ``` ```php if ($response->isFailure()) { $errors = $response->errors(); foreach ($errors as $recipientIdx => $error) { \Log::warning("Recipient {$recipientIdx} failed: {$error->message}", [ 'type' => $error->type->value, 'token' => (string) $error->token, ]); } } ``` -------------------------------- ### General Exception Handling for Sending Notifications Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/Exceptions.md Demonstrates a comprehensive approach to catching `CouldNotSendNotification` exceptions. This includes logging the error, notifying an administrator, and optionally re-throwing the exception. ```php use NotificationChannels\Expo\Exceptions\CouldNotSendNotification; try { $user->notify(new OrderNotification()); } catch (CouldNotSendNotification $e) { // Log the error \Log::error('Push notification failed: ' . $e->getMessage()); // Notify admin \Notification::route('mail', 'admin@example.com') ->notify(new AlertNotification('Push notification failed')); // Optionally re-throw or return error response throw $e; } ``` -------------------------------- ### Handle Expo Notification Errors in API Endpoints Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/Exceptions.md Illustrates exception handling for `CouldNotSendNotification` within an HTTP controller method. This example returns a JSON error response to the client when a notification fails to send. ```php namespace App\Http\Controllers; use NotificationChannels\Expo\Exceptions\CouldNotSendNotification; class NotificationController { public function send(Request $request) { try { $user->notify(new CustomNotification($request->input('message'))); return response()->json(['status' => 'sent']); } catch (CouldNotSendNotification $e) { return response()->json( ['error' => 'Failed to send notification'], 500 ); } } } ``` -------------------------------- ### AsExpoPushToken::get() Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/AsExpoPushToken.md Transforms a raw database value into an ExpoPushToken instance. It handles string, null, and existing ExpoPushToken values, throwing an exception for invalid types. ```APIDOC ## AsExpoPushToken::get() ### Description Transforms a database value into an `ExpoPushToken` instance. Handles string, null, and existing `ExpoPushToken` values, throwing an exception for invalid types. ### Method `public function get($model, string $key, $value, array $attributes): ?ExpoPushToken` ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example N/A ### Response #### Success Response - **ExpoPushToken** (`?ExpoPushToken`) - An `ExpoPushToken` instance or null. #### Response Example ```json { "example": "ExpoPushToken[abc123]" } ``` ### Throws - **InvalidArgumentException** - If the value cannot be cast to an `ExpoPushToken`. ``` -------------------------------- ### Type Inference and IDE Autocompletion Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/AsExpoPushToken.md Shows how the ExpoPushToken cast provides type hints, enabling IDE autocompletion for token-related methods. This improves developer experience and reduces errors. ```php $user = User::find(1); // IDE knows $token is ExpoPushToken|null $token = $user->expo_token; // Methods are available $token?->asString(); $token?->equals('ExpoPushToken[...]'); ``` -------------------------------- ### Get Fatal Error Message from Response Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoResponse.md Retrieve the error message for a fatal API error. This is used when the entire request fails. The message typically contains details from the Expo API or the HTTP response. ```php public function message(): string ``` ```python if ($response->isFatal()) { $errorMessage = $response->message(); \Log::error("Fatal Expo API error: {$errorMessage}"); throw new \Exception($errorMessage); } ``` -------------------------------- ### Set Message Time-to-Live (TTL) Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoMessage.md Use `ttl()` or `expiresIn()` to set the message's time-to-live in seconds. This takes precedence over `expiresAt()`. ```php $message->ttl(3600); // Expires in 1 hour ``` -------------------------------- ### Send Push Notifications with Expo Gateway Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoGatewayUsingGuzzle.md This snippet demonstrates how to initialize the ExpoGatewayUsingGuzzle and send push notifications. It includes handling successful responses and errors. ```php use NotificationChannels\Expo\Gateway\ExpoGatewayUsingGuzzle; use NotificationChannels\Expo\Gateway\ExpoEnvelope; use NotificationChannels\Expo\ExpoMessage; use NotificationChannels\Expo\ExpoPushToken; class PushNotificationService { private ExpoGatewayUsingGuzzle $gateway; public function __construct() { $this->gateway = new ExpoGatewayUsingGuzzle( config('services.expo.access_token') ); } public function send(array $tokenStrings, string $title, string $body): void { $tokens = array_map( fn ($token) => ExpoPushToken::make($token), $tokenStrings ); $message = ExpoMessage::create($title)->body($body); $envelope = ExpoEnvelope::make($tokens, $message); $response = $this->gateway->sendPushNotifications($envelope); if ($response->isOk()) { $this->storeTickets($response->tickets()); } elseif ($response->isFailure()) { foreach ($response->errors() as $error) { event(new NotificationFailed( auth()->user(), new TestNotification(), 'expo', $error )); } } else { throw new \Exception("Expo API error: {$response->message()}"); } } public function checkStatus(array $ticketIds): array { return $this->gateway->getReceipts($ticketIds); } private function storeTickets(array $tickets): void { foreach ($tickets as $token => $ticketId) { PushTicket::create([ 'ticket_id' => $ticketId, 'token' => $token, ]); } } } ``` -------------------------------- ### Handle MismatchSenderId Expo Error Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/errors.md This example demonstrates how to log a critical error and notify an administrator when a `MismatchSenderId` error occurs, indicating misconfigured FCM credentials. This helps in quickly addressing issues that prevent all Android notifications from being sent. ```php class HandleFailedExpoNotifications { public function handle(NotificationFailed $event) { if ($event->data->type->isMismatchSenderId()) { \Log::critical('FCM credentials mismatch - Android notifications failing'); // Alert admin to fix credentials \Notification::route('mail', 'admin@example.com') ->notify(new ConfigurationAlert('FCM Sender ID mismatch')); } } } ``` -------------------------------- ### Create an ExpoError instance Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoError.md Use the static `make()` factory method to create an ExpoError instance. This method requires the error type, the push token, and a descriptive message. ```php use NotificationChannels\Expo\ExpoError; use NotificationChannels\Expo\ExpoErrorType; use NotificationChannels\Expo\ExpoPushToken; $token = ExpoPushToken::make('ExpoPushToken[abc123]'); $error = ExpoError::make( ExpoErrorType::DeviceNotRegistered, $token, 'The device is no longer registered' ); ``` -------------------------------- ### Expo Configuration for Multiple Environments Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/configuration.md Configure the Expo channel's access token dynamically based on the application environment using a `match` expression in `config/services.php`. This allows different tokens for production, staging, and development. ```php // config/services.php return [ 'expo' => [ 'access_token' => match (env('APP_ENV')) { 'production' => env('EXPO_ACCESS_TOKEN'), 'staging' => env('EXPO_STAGING_TOKEN'), default => null, // Development uses no token }, ], ]; // .env.production EXPO_ACCESS_TOKEN=prod-token-here // .env.staging EXPO_STAGING_TOKEN=staging-token-here // .env (development) # EXPO_ACCESS_TOKEN not set ``` -------------------------------- ### Handling Null/Empty Expo Push Tokens Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/api-reference/ExpoPushTokenRule.md Demonstrates how to use the 'required' validation rule in conjunction with ExpoPushTokenRule to handle null or empty Expo push tokens. The first example fails with 'validation.string' for null, while the second fails with 'validation.required'. ```php // This will fail with 'validation.string' on null ['expo_token' => [ExpoPushTokenRule::make()]] // This will fail with 'validation.required' on null ['expo_token' => ['required', ExpoPushTokenRule::make()]] ``` -------------------------------- ### Reduce Expo Notification Payload Size Source: https://github.com/laravel-notification-channels/expo/blob/main/_autodocs/errors.md This example shows how to reduce the size of an Expo notification payload to avoid the `MessageTooBig` error, which occurs when the total notification payload exceeds 4096 bytes. It contrasts a potentially too-large message with a reduced version containing only essential data. ```php // Before: Might exceed limit $message = ExpoMessage::create('Long Title Here') ->body('Very long body text...') ->data(['field1' => 'long content', 'field2' => 'more data', ...]); // After: Reduced payload $message = ExpoMessage::create('Title') ->body('Body') ->data(['id' => 123]); // Just essential data ```