You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PHP中获取FCM推送失败Token并更新数据库状态

问题描述

使用fmcSendToServerAction函数向Firebase发送推送通知,收到FCM响应如下:

{"multicast_id":3518989135591430380,"success":1,"failure":2,"canonical_ids":0,"results":[{"error":"NotRegistered"},{"error":"NotRegistered"},{"message_id":"48ccfe8f-b449-491a-b912-fe9cc86ab98e"}]}

已将用户同意接收通知时生成的Token存储在数据库中(ID为37和38的Token推送失败),需要获取这些失败的Token并更新数据库中的激活状态。相关代码如下:

/**
 *
 * @Route("/fcm/send", name="fmc_send_to_server")
 * @Method("GET")
 * @Template(engine="php")
 */
public function fmcSendToServerAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $fcmTokens = $em->getRepository('VlanceBaseBundle:FcmToken')->findAll();
    $registrationIds = array_map(function ($ft) {
        return $ft->getToken();
    }, $fcmTokens);

    $fcmMessages = $em->getRepository('VlanceBaseBundle:FcmNotifyPush')->getFcmNotifyPush();

    $url = "https://fcm.googleapis.com/fcm/send";
    $server_key = "SERVER_MY_KEY";

    if (count($fcmMessages) <= 0) {
        return new Response('No notifications to send to users', 200);
    }

    foreach ($fcmMessages as $message) {
        $messageData = [
            'data' => [
                'title' => $message->getTitle(),
                'body' => $message->getDiscription(),
                'click_action' => $message->getUrl()
            ],
            'registration_ids' => $registrationIds,
        ];

        $options = [
            CURLOPT_URL => $url,
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                "Authorization: Bearer " . $server_key,
                "Content-Type: application/json",
            ],
            CURLOPT_POSTFIELDS => json_encode($messageData),
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_RETURNTRANSFER => true, // Capture the response
        ];

        $curl = curl_init();
        curl_setopt_array($curl, $options);
        $response = curl_exec($curl);
        $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); // Get HTTP response code
        curl_close($curl);
    }
    return new Response('All notifications have been sent to users', 200);
}
解决方案

要实现获取失败Token并更新数据库的需求,可按以下步骤修改代码:

1. 保留Token与数据库实体的关联

原代码仅提取了Token字符串,丢失了对应的FcmToken实体,无法直接关联到数据库记录。需调整逻辑,建立Token到实体的映射:

// 替换原有的$fcmTokens和$registrationIds初始化部分
$em = $this->getDoctrine()->getManager();
$fcmTokens = $em->getRepository('VlanceBaseBundle:FcmToken')->findAll();
$tokenToEntityMap = [];
$registrationIds = [];
foreach ($fcmTokens as $ft) {
    $token = $ft->getToken();
    $registrationIds[] = $token;
    $tokenToEntityMap[$token] = $ft;
}

2. 解析FCM响应并处理失败Token

在获取FCM响应后,解析结果并筛选出NotRegistered的失败项,对应到数据库实体更新状态:

// 在curl_close($curl)后添加以下代码
if ($httpCode === 200 && !empty($response)) {
    $fcmResponse = json_decode($response, true);
    if (isset($fcmResponse['results']) && is_array($fcmResponse['results'])) {
        foreach ($fcmResponse['results'] as $index => $result) {
            // 筛选出未注册的失败Token
            if (isset($result['error']) && $result['error'] === 'NotRegistered') {
                $failedToken = $registrationIds[$index];
                // 通过映射找到对应的数据库实体
                if (isset($tokenToEntityMap[$failedToken])) {
                    $tokenEntity = $tokenToEntityMap[$failedToken];
                    // 更新激活状态(根据你的实体字段调整,这里假设字段为isActive)
                    $tokenEntity->setIsActive(false);
                    $em->persist($tokenEntity);
                }
            }
        }
        // 批量提交数据库更新
        $em->flush();
    }
}

3. 额外注意事项

  • 确保FcmToken实体包含激活状态字段(如isActive),若没有需先添加字段并同步数据库表结构。
  • 若同一批Token需发送多条消息,建议仅在最后一次发送完成后统一处理失败Token,避免重复更新。
  • 可添加日志记录失败的Token信息,方便后续排查问题。

内容的提问来源于stack exchange,提问作者zenke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:56:14