migrate to gtea from bistbucket
This commit is contained in:
@@ -0,0 +1,885 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\RestaurantDelivery\Services\Firebase;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Kreait\Firebase\Database;
|
||||
use Kreait\Firebase\Exception\FirebaseException;
|
||||
use Kreait\Firebase\Factory;
|
||||
use Kreait\Firebase\Messaging;
|
||||
use Kreait\Firebase\Messaging\CloudMessage;
|
||||
use Kreait\Firebase\Messaging\Notification;
|
||||
|
||||
class FirebaseService
|
||||
{
|
||||
protected ?Database $database = null;
|
||||
|
||||
protected ?Messaging $messaging = null;
|
||||
|
||||
protected ?Factory $factory = null; // nullable
|
||||
|
||||
protected array $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = config('restaurant-delivery');
|
||||
$this->initializeFirebase();
|
||||
}
|
||||
|
||||
protected function initializeFirebase(): void
|
||||
{
|
||||
if (! $this->config['enabled']) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->factory = (new Factory)
|
||||
->withServiceAccount($this->config['credentials_path'])
|
||||
->withDatabaseUri($this->config['database_url']);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Firebase initialization failed', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDatabase(): ?Database
|
||||
{
|
||||
if (! $this->database && $this->factory) {
|
||||
$this->database = $this->factory->createDatabase();
|
||||
}
|
||||
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
public function getMessaging(): ?Messaging
|
||||
{
|
||||
if (! $this->messaging && $this->factory) {
|
||||
$this->messaging = $this->factory->createMessaging();
|
||||
}
|
||||
|
||||
return $this->messaging;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rider Location Operations
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update rider's live location in Firebase
|
||||
*/
|
||||
public function updateRiderLocation(
|
||||
int|string $riderId,
|
||||
float $latitude,
|
||||
float $longitude,
|
||||
?float $speed = null,
|
||||
?float $bearing = null,
|
||||
?float $accuracy = null
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['riders_location']);
|
||||
|
||||
$locationData = [
|
||||
'lat' => $latitude,
|
||||
'lng' => $longitude,
|
||||
'speed' => $speed ?? 0,
|
||||
'bearing' => $bearing ?? 0,
|
||||
'accuracy' => $accuracy ?? 0,
|
||||
'timestamp' => time() * 1000, // JavaScript timestamp
|
||||
'updated_at' => now()->toIso8601String(),
|
||||
];
|
||||
|
||||
$database->getReference($path)->set($locationData);
|
||||
|
||||
// Cache locally for quick access
|
||||
Cache::put(
|
||||
"rider_location_{$riderId}",
|
||||
$locationData,
|
||||
config('restaurant-delivery.cache.ttl.rider_location')
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update rider location in Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rider's current location from Firebase
|
||||
*/
|
||||
public function getRiderLocation(int|string $riderId): ?array
|
||||
{
|
||||
// Try cache first
|
||||
$cached = Cache::get("rider_location_{$riderId}");
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['riders_location']);
|
||||
$snapshot = $database->getReference($path)->getSnapshot();
|
||||
|
||||
return $snapshot->exists() ? $snapshot->getValue() : null;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to get rider location from Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if rider location is stale
|
||||
*/
|
||||
public function isRiderLocationStale(int|string $riderId): bool
|
||||
{
|
||||
$location = $this->getRiderLocation($riderId);
|
||||
|
||||
if (! $location || ! isset($location['timestamp'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$lastUpdate = (int) ($location['timestamp'] / 1000); // Convert from JS timestamp
|
||||
$staleThreshold = $this->config['location']['stale_threshold'];
|
||||
|
||||
return (time() - $lastUpdate) > $staleThreshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if rider is considered offline
|
||||
*/
|
||||
public function isRiderOffline(int|string $riderId): bool
|
||||
{
|
||||
$location = $this->getRiderLocation($riderId);
|
||||
|
||||
if (! $location || ! isset($location['timestamp'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$lastUpdate = (int) ($location['timestamp'] / 1000);
|
||||
$offlineThreshold = $this->config['location']['offline_threshold'];
|
||||
|
||||
return (time() - $lastUpdate) > $offlineThreshold;
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rider Status Operations
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update rider online/offline status
|
||||
*/
|
||||
public function updateRiderStatus(
|
||||
int|string $riderId,
|
||||
string $status,
|
||||
?array $metadata = null
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['rider_status']);
|
||||
|
||||
$statusData = [
|
||||
'status' => $status, // online, offline, busy, on_delivery
|
||||
'updated_at' => now()->toIso8601String(),
|
||||
'timestamp' => time() * 1000,
|
||||
];
|
||||
|
||||
if ($metadata) {
|
||||
$statusData = array_merge($statusData, $metadata);
|
||||
}
|
||||
|
||||
$database->getReference($path)->set($statusData);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update rider status in Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'status' => $status,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rider's current status
|
||||
*/
|
||||
public function getRiderStatus(int|string $riderId): ?array
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['rider_status']);
|
||||
$snapshot = $database->getReference($path)->getSnapshot();
|
||||
|
||||
return $snapshot->exists() ? $snapshot->getValue() : null;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to get rider status from Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Delivery Tracking Operations
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize delivery tracking in Firebase
|
||||
*/
|
||||
public function initializeDeliveryTracking(
|
||||
int|string $deliveryId,
|
||||
array $deliveryData
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
|
||||
$trackingData = [
|
||||
'delivery_id' => $deliveryId,
|
||||
'status' => $deliveryData['status'] ?? 'pending',
|
||||
'rider_id' => $deliveryData['rider_id'] ?? null,
|
||||
'restaurant' => [
|
||||
'id' => $deliveryData['restaurant_id'] ?? null,
|
||||
'name' => $deliveryData['restaurant_name'] ?? null,
|
||||
'lat' => $deliveryData['pickup_latitude'],
|
||||
'lng' => $deliveryData['pickup_longitude'],
|
||||
'address' => $deliveryData['pickup_address'] ?? null,
|
||||
],
|
||||
'customer' => [
|
||||
'lat' => $deliveryData['drop_latitude'],
|
||||
'lng' => $deliveryData['drop_longitude'],
|
||||
'address' => $deliveryData['drop_address'] ?? null,
|
||||
],
|
||||
'rider_location' => null,
|
||||
'route' => $deliveryData['route'] ?? null,
|
||||
'eta' => $deliveryData['eta'] ?? null,
|
||||
'distance' => $deliveryData['distance'] ?? null,
|
||||
'created_at' => now()->toIso8601String(),
|
||||
'updated_at' => now()->toIso8601String(),
|
||||
];
|
||||
|
||||
$database->getReference($path)->set($trackingData);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to initialize delivery tracking in Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update delivery tracking with rider location
|
||||
*/
|
||||
public function updateDeliveryTracking(
|
||||
int|string $deliveryId,
|
||||
array $updates
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
|
||||
$updates['updated_at'] = now()->toIso8601String();
|
||||
|
||||
$database->getReference($path)->update($updates);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update delivery tracking in Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update delivery status in Firebase
|
||||
*/
|
||||
public function updateDeliveryStatus(
|
||||
int|string $deliveryId,
|
||||
string $status,
|
||||
?array $metadata = null
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$statusPath = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_status']);
|
||||
$trackingPath = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
|
||||
$statusConfig = config("restaurant-delivery.delivery_flow.statuses.{$status}");
|
||||
|
||||
$statusData = [
|
||||
'status' => $status,
|
||||
'label' => $statusConfig['label'] ?? $status,
|
||||
'description' => $statusConfig['description'] ?? null,
|
||||
'color' => $statusConfig['color'] ?? '#6B7280',
|
||||
'timestamp' => time() * 1000,
|
||||
'updated_at' => now()->toIso8601String(),
|
||||
];
|
||||
|
||||
if ($metadata) {
|
||||
$statusData = array_merge($statusData, $metadata);
|
||||
}
|
||||
|
||||
// Update both status and tracking paths
|
||||
$database->getReference($statusPath)->set($statusData);
|
||||
$database->getReference($trackingPath.'/status')->set($status);
|
||||
$database->getReference($trackingPath.'/status_data')->set($statusData);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update delivery status in Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'status' => $status,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get delivery tracking data
|
||||
*/
|
||||
public function getDeliveryTracking(int|string $deliveryId): ?array
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
$snapshot = $database->getReference($path)->getSnapshot();
|
||||
|
||||
return $snapshot->exists() ? $snapshot->getValue() : null;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to get delivery tracking from Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rider location for a specific delivery
|
||||
*/
|
||||
public function updateDeliveryRiderLocation(
|
||||
int|string $deliveryId,
|
||||
float $latitude,
|
||||
float $longitude,
|
||||
?float $speed = null,
|
||||
?float $bearing = null,
|
||||
?float $eta = null,
|
||||
?float $remainingDistance = null
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
|
||||
$locationData = [
|
||||
'rider_location' => [
|
||||
'lat' => $latitude,
|
||||
'lng' => $longitude,
|
||||
'speed' => $speed ?? 0,
|
||||
'bearing' => $bearing ?? 0,
|
||||
'timestamp' => time() * 1000,
|
||||
],
|
||||
'eta' => $eta,
|
||||
'remaining_distance' => $remainingDistance,
|
||||
'updated_at' => now()->toIso8601String(),
|
||||
];
|
||||
|
||||
$database->getReference($path)->update($locationData);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update delivery rider location in Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store route polyline for delivery
|
||||
*/
|
||||
public function updateDeliveryRoute(
|
||||
int|string $deliveryId,
|
||||
array $route
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
|
||||
$database->getReference($path.'/route')->set($route);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update delivery route in Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove delivery tracking data (cleanup after delivery)
|
||||
*/
|
||||
public function removeDeliveryTracking(int|string $deliveryId): bool
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$trackingPath = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_tracking']);
|
||||
$statusPath = str_replace('{delivery_id}', (string) $deliveryId, $this->config['paths']['delivery_status']);
|
||||
|
||||
$database->getReference($trackingPath)->remove();
|
||||
$database->getReference($statusPath)->remove();
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to remove delivery tracking from Firebase', [
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rider Assignment Operations
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add delivery to rider's assignment list
|
||||
*/
|
||||
public function addRiderAssignment(
|
||||
int|string $riderId,
|
||||
int|string $deliveryId,
|
||||
array $deliveryInfo
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['rider_assignments']);
|
||||
|
||||
$assignmentData = [
|
||||
'delivery_id' => $deliveryId,
|
||||
'status' => 'assigned',
|
||||
'restaurant' => $deliveryInfo['restaurant'] ?? null,
|
||||
'customer_address' => $deliveryInfo['customer_address'] ?? null,
|
||||
'pickup_location' => $deliveryInfo['pickup_location'] ?? null,
|
||||
'drop_location' => $deliveryInfo['drop_location'] ?? null,
|
||||
'assigned_at' => now()->toIso8601String(),
|
||||
'timestamp' => time() * 1000,
|
||||
];
|
||||
|
||||
$database->getReference($path.'/'.$deliveryId)->set($assignmentData);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to add rider assignment in Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove delivery from rider's assignment list
|
||||
*/
|
||||
public function removeRiderAssignment(
|
||||
int|string $riderId,
|
||||
int|string $deliveryId
|
||||
): bool {
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['rider_assignments']);
|
||||
|
||||
$database->getReference($path.'/'.$deliveryId)->remove();
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to remove rider assignment from Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'delivery_id' => $deliveryId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active assignments for a rider
|
||||
*/
|
||||
public function getRiderAssignments(int|string $riderId): array
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$path = str_replace('{rider_id}', (string) $riderId, $this->config['paths']['rider_assignments']);
|
||||
$snapshot = $database->getReference($path)->getSnapshot();
|
||||
|
||||
return $snapshot->exists() ? $snapshot->getValue() : [];
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to get rider assignments from Firebase', [
|
||||
'rider_id' => $riderId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Push Notifications
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Send push notification to a device
|
||||
*/
|
||||
public function sendPushNotification(
|
||||
string $token,
|
||||
string $title,
|
||||
string $body,
|
||||
?array $data = null
|
||||
): bool {
|
||||
try {
|
||||
$messaging = $this->getMessaging();
|
||||
if (! $messaging) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$message = CloudMessage::withTarget('token', $token)
|
||||
->withNotification(Notification::create($title, $body));
|
||||
|
||||
if ($data) {
|
||||
$message = $message->withData($data);
|
||||
}
|
||||
|
||||
$messaging->send($message);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to send push notification', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send push notification to multiple devices
|
||||
*/
|
||||
public function sendMulticastNotification(
|
||||
array $tokens,
|
||||
string $title,
|
||||
string $body,
|
||||
?array $data = null
|
||||
): array {
|
||||
try {
|
||||
$messaging = $this->getMessaging();
|
||||
if (! $messaging) {
|
||||
return ['success' => 0, 'failure' => count($tokens)];
|
||||
}
|
||||
|
||||
$message = CloudMessage::new()
|
||||
->withNotification(Notification::create($title, $body));
|
||||
|
||||
if ($data) {
|
||||
$message = $message->withData($data);
|
||||
}
|
||||
|
||||
$report = $messaging->sendMulticast($message, $tokens);
|
||||
|
||||
return [
|
||||
'success' => $report->successes()->count(),
|
||||
'failure' => $report->failures()->count(),
|
||||
'invalid_tokens' => $report->invalidTokens(),
|
||||
];
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to send multicast notification', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['success' => 0, 'failure' => count($tokens)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to a topic
|
||||
*/
|
||||
public function sendTopicNotification(
|
||||
string $topic,
|
||||
string $title,
|
||||
string $body,
|
||||
?array $data = null
|
||||
): bool {
|
||||
try {
|
||||
$messaging = $this->getMessaging();
|
||||
if (! $messaging) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$message = CloudMessage::withTarget('topic', $topic)
|
||||
->withNotification(Notification::create($title, $body));
|
||||
|
||||
if ($data) {
|
||||
$message = $message->withData($data);
|
||||
}
|
||||
|
||||
$messaging->send($message);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to send topic notification', [
|
||||
'topic' => $topic,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe device to a topic
|
||||
*/
|
||||
public function subscribeToTopic(string $token, string $topic): bool
|
||||
{
|
||||
try {
|
||||
$messaging = $this->getMessaging();
|
||||
if (! $messaging) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$messaging->subscribeToTopic($topic, [$token]);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to subscribe to topic', [
|
||||
'topic' => $topic,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe device from a topic
|
||||
*/
|
||||
public function unsubscribeFromTopic(string $token, string $topic): bool
|
||||
{
|
||||
try {
|
||||
$messaging = $this->getMessaging();
|
||||
if (! $messaging) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$messaging->unsubscribeFromTopic($topic, [$token]);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to unsubscribe from topic', [
|
||||
'topic' => $topic,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Utility Methods
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set a value at a custom path
|
||||
*/
|
||||
public function set(string $path, mixed $value): bool
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database->getReference($path)->set($value);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to set value in Firebase', [
|
||||
'path' => $path,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update values at a custom path
|
||||
*/
|
||||
public function update(string $path, array $values): bool
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database->getReference($path)->update($values);
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to update value in Firebase', [
|
||||
'path' => $path,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from a custom path
|
||||
*/
|
||||
public function get(string $path): mixed
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$snapshot = $database->getReference($path)->getSnapshot();
|
||||
|
||||
return $snapshot->exists() ? $snapshot->getValue() : null;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to get value from Firebase', [
|
||||
'path' => $path,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete value at a custom path
|
||||
*/
|
||||
public function delete(string $path): bool
|
||||
{
|
||||
try {
|
||||
$database = $this->getDatabase();
|
||||
if (! $database) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$database->getReference($path)->remove();
|
||||
|
||||
return true;
|
||||
} catch (FirebaseException $e) {
|
||||
Log::error('Failed to delete value from Firebase', [
|
||||
'path' => $path,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user