File "notification_helper.php"

Full Path: /home/trinadezambia/public_html/admin_panel/app/Helpers/notification_helper.php
File size: 12 KB
MIME-type: text/x-php
Charset: utf-8

<?php

use App\Jobs\SendFcmNotification;
use App\Models\FcmToken;
use App\Models\User;
use App\Services\CachingService;
use App\Services\FcmTokenService;
use Google\Client;
use Illuminate\Http\Client\Pool;

function send_notification($user, $title, $body, $type, $customData = [])
{
    // Ensure $user is an array
    $userIds = is_array($user) ? $user : [$user];
    // Fetch all active FCM tokens for the users using the new fcm_tokens table
    $fcmTokenService = app(FcmTokenService::class);
    $fcmTokens = $fcmTokenService->getUsersTokens($userIds);

    // If no tokens found, try backward compatibility with old columns
    if ($fcmTokens->isEmpty()) {
        // Fallback to old columns for backward compatibility
        $mobileFcmTokens = User::where('fcm_id', '!=', '')
            ->whereNotNull('fcm_id')
            ->whereIn('id', $userIds)
            ->get()
            ->pluck('fcm_id')
            ->filter();

        $webFcmTokens = User::where('web_fcm', '!=', '')
            ->whereNotNull('web_fcm')
            ->whereIn('id', $userIds)
            ->get()
            ->pluck('web_fcm')
            ->filter();

        if ($mobileFcmTokens->isEmpty() && $webFcmTokens->isEmpty()) {
            return; // No FCM tokens found
        }

        // Migrate old tokens and send notifications
        foreach ($userIds as $userId) {
            $user = User::find($userId);
            if ($user) {
                $fcmTokenService->migrateUserTokens($user);
            }
        }

        // Fetch tokens again after migration
        $fcmTokens = $fcmTokenService->getUsersTokens($userIds);
    }

    if ($fcmTokens->isEmpty()) {
        return; // No FCM tokens found
    }

    // Queue notification jobs for each token
    foreach ($fcmTokens as $fcmToken) {
        // Get school_id from the user
        $user = $fcmToken->user;
        if (!$user || !$user->school_id) {
            continue; // Skip if user or school_id not found
        }

        SendFcmNotification::dispatch(
            $user->school_id,
            $fcmToken->id,
            $title,
            $body,
            $type,
            $customData
        );
    }
}

/**
 * Send FCM notification via cURL
 */
function sendFcmNotification($url, $access_token, $data)
{
    $encodedData = json_encode($data);

    $headers = [
        'Authorization: Bearer ' . $access_token,
        'Content-Type: application/json',
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);

    // Disabling SSL Certificate support temporarly
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $encodedData);

    // Execute post
    $result = curl_exec($ch);
    // dd($result);
    
    if ($result === FALSE) {
        // Log error but continue with other tokens
        error_log('FCM notification failed: ' . curl_error($ch));
    } else {
        // Log response for debugging (optional)
        $response = json_decode($result, true);
        if (isset($response['error'])) {
            error_log('FCM notification error: ' . json_encode($response['error']));
        }
    }

    // Close connection
    curl_close($ch);
}

/**
 * Get web notification icon URL
 */
function getWebNotificationIcon($customData = [])
{
    // Default icon path - adjust based on your project structure
    $defaultIcon = asset('assets/images/favicon.png');

    // If image is provided in customData, use it as icon
    if (isset($customData['image']) && !empty($customData['image'])) {
        // If image is already a full URL, return it
        if (filter_var($customData['image'], FILTER_VALIDATE_URL)) {
            return $customData['image'];
        }
        // Otherwise, it's likely a storage path, convert to URL
        return url($customData['image']);
    }

    return $defaultIcon;
}

function getAccessToken()
{
    $cache = app(CachingService::class);

    $file_name = $cache->getSystemSettings('firebase_service_file');
    $data = explode("storage/", $file_name ?? '');
    $file_name = end($data);

    $file_path = base_path('public/storage/' . $file_name);

    $client = new Client();
    $client->setAuthConfig($file_path);
    $client->setScopes(['https://www.googleapis.com/auth/firebase.messaging']);
    $accessToken = $client->fetchAccessTokenWithAssertion()['access_token'];

    return $accessToken;
}

function buildPayloads(array $userIds, string $title, string $body, string $type, array $customData = [])
{
    // Convert custom data values to strings
    $customDataStrings = array_map(function ($value) {
        return is_array($value) ? json_encode($value) : (string) $value;
    }, $customData);

    // Get FCM tokens from new fcm_tokens table
    $fcmTokenService = app(FcmTokenService::class);
    $fcmTokens = $fcmTokenService->getUsersTokens($userIds);

    // If no tokens found, try backward compatibility with old columns
    if ($fcmTokens->isEmpty()) {
        // Fallback to old columns for backward compatibility
        $mobileTokens = User::whereIn('id', $userIds)
            ->whereNotNull('fcm_id')
            ->where('fcm_id', '!=', '')
            ->pluck('fcm_id')
            ->toArray();

        $webTokens = User::whereIn('id', $userIds)
            ->whereNotNull('web_fcm')
            ->where('web_fcm', '!=', '')
            ->pluck('web_fcm')
            ->toArray();

        // If still no tokens → nothing to send
        if (empty($mobileTokens) && empty($webTokens)) {
            return [];
        }

        // Build payloads from old tokens (for backward compatibility)
        $payloads = [];
        foreach ($mobileTokens as $token) {
            $payloads[] = [
                "message" => [
                    "token" => $token,
                    "notification" => [
                        "title" => $title,
                        "body" => $body,
                    ],
                    "data" => array_merge([
                        "title" => $title,
                        "body" => $body,
                        "type" => $type,
                    ], $customDataStrings),
                    "android" => [
                        "notification" => [
                            "sound" => "default",
                            "click_action" => "FLUTTER_NOTIFICATION_CLICK",
                        ],
                        "priority" => "high"
                    ],
                    "apns" => [
                        "headers" => [
                            "apns-priority" => "10"
                        ],
                        "payload" => [
                            "aps" => [
                                "alert" => [
                                    "title" => $title,
                                    "body" => $body,
                                ],
                                "sound" => "default",
                                "mutable-content" => 1,
                                "content-available" => 1
                            ]
                        ] + $customDataStrings
                    ]
                ]
            ];
        }

        foreach ($webTokens as $token) {
            $icon = getWebNotificationIcon($customData);

            $webNotification = [
                "title" => $title,
                "body" => $body,
                "icon" => $icon,
                "badge" => $icon,
                "requireInteraction" => false,
                "silent" => false,
            ];

            if (!empty($customData['image'])) {
                $img = $customData['image'];
                $webNotification["image"] = filter_var($img, FILTER_VALIDATE_URL) ? $img : url($img);
            }

            $payloads[] = [
                "message" => [
                    "token" => $token,
                    "notification" => [
                        "title" => $title,
                        "body" => $body
                    ],
                    "data" => array_merge([
                        "title" => $title,
                        "body" => $body,
                        "type" => $type,
                    ], $customDataStrings),
                    "webpush" => [
                        "notification" => $webNotification,
                    ]
                ]
            ];
        }

        return $payloads;
    }

    // Build payloads from new fcm_tokens table
    $cache = app(CachingService::class);
    $projectId = $cache->getSystemSettings('firebase_project_id');

    if (!$projectId) {
        return [];
    }

    $payloads = [];

    // Group tokens by device type
    $mobileTokens = $fcmTokens->whereIn('device_type', ['android', 'ios']);
    $webTokens = $fcmTokens->where('device_type', 'web');

    // Build mobile payloads
    foreach ($mobileTokens as $fcmToken) {
        $payloads[] = [
            "message" => [
                "token" => $fcmToken->fcm_token,
                "notification" => [
                    "title" => $title,
                    "body" => $body,
                ],
                "data" => array_merge([
                    "title" => $title,
                    "body" => $body,
                    "type" => $type,
                ], $customDataStrings),
                "android" => [
                    "notification" => [
                        "sound" => "default",
                        "click_action" => "FLUTTER_NOTIFICATION_CLICK",
                    ],
                    "priority" => "high"
                ],
                "apns" => [
                    "headers" => [
                        "apns-priority" => "10"
                    ],
                    "payload" => [
                        "aps" => [
                            "alert" => [
                                "title" => $title,
                                "body" => $body,
                            ],
                            "sound" => "default",
                            "mutable-content" => 1,
                            "content-available" => 1
                        ]
                    ] + $customDataStrings
                ]
            ]
        ];
    }

    // Build web payloads
    foreach ($webTokens as $fcmToken) {
        $icon = getWebNotificationIcon($customData);

        $webNotification = [
            "title" => $title,
            "body" => $body,
            "icon" => $icon,
            "badge" => $icon,
            "requireInteraction" => false,
            "silent" => false,
        ];

        if (!empty($customData['image'])) {
            $img = $customData['image'];
            $webNotification["image"] = filter_var($img, FILTER_VALIDATE_URL) ? $img : url($img);
        }

        $payloads[] = [
            "message" => [
                "token" => $fcmToken->fcm_token,
                "notification" => [
                    "title" => $title,
                    "body" => $body
                ],
                "data" => array_merge([
                    "title" => $title,
                    "body" => $body,
                    "type" => $type,
                ], $customDataStrings),
                "webpush" => [
                    "notification" => $webNotification,
                ]
            ]
        ];
    }

    return $payloads;
}

/**
 * Send all payloads in PARALLEL — super fast.
 */
function sendBulk(array $payloads)
{
    if (empty($payloads)) {
        return;
    }

    $cache = app(CachingService::class);
    $projectId = $cache->getSystemSettings('firebase_project_id');

    if (!$projectId) {
        return;
    }

    $url = "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send";
    $token = getAccessToken();

    Http::pool(function (Pool $pool) use ($payloads, $url, $token) {

        $requests = [];

        foreach ($payloads as $payload) {
            $requests[] = $pool
                ->withToken($token)
                ->post($url, $payload);
        }

        return $requests;
    });
}