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

Symfony Mercure实时聊天组问题:发送更新失败+消息未入库

问题原因及解决方法

以下是导致你遇到的Mercure发布失败、消息未存入数据库的核心问题及对应修复方案:


1. 实体未持久化,消息无法存入数据库

你创建了Message实体并关联了用户和会话,但未调用EntityManagerInterface::persist()将新实体托管到Doctrine,仅执行flush()不会保存新创建的Message对象。

修复:在$groupConversation->addMessage($message)后添加持久化代码:

$this->em->persist($message);

2. Private更新未指定目标订阅者,Mercure发布被拒绝

当$groupConversation->getPrivate()返回true时,Mercure私有更新必须通过targets参数指定允许接收更新的订阅者JWT主题(通常是用户ID或专属标识),否则Mercure服务器会直接拒绝发布请求,抛出“Failed to send an update”错误。

修复:从会话用户集合中提取订阅主题,作为Update的第6个参数传入:

// 提取会话中所有用户的Mercure订阅主题(示例为/user/{用户ID})
$targets = [];
foreach ($groupConversation->getUsers() as $conversationUser) {
    $targets[] = '/user/' . $conversationUser->getId();
}

// 构建Update时传入targets参数
$update = new Update(
    '/messages/' . $groupConversation->getId(),
    // ... 其他参数 ...
    $groupConversation->getPrivate(),
    'message-' . Uuid::v4(),
    'message',
    $targets // 添加目标订阅者
);

3. JSON编码包含不可序列化对象,Update内容无效

$groupConversation->getUsers()返回的是User实体集合,直接传入json_encode()会因对象无法序列化导致编码失败,Mercure无法处理无效JSON内容,进而发布失败。

修复:将用户集合转换为可序列化的数组(仅保留必要字段):

'to' => array_map(function(User $u) {
    return ['id' => $u->getId(), 'username' => $u->getUsername()];
}, $groupConversation->getUsers()->toArray()),

4. 路由参数与方法参数不匹配,会话对象获取异常

add方法的路由定义是/{id}/add,但方法参数是GroupConversation $groupConversation,Symfony参数转换器默认按名称匹配路由参数,名称不匹配会导致无法正确获取会话对象,后续关联操作失效。

修复:修改路由参数名称与方法参数一致:

/**
 * @Route("/{groupConversation}/add", name="add", requirements={"groupConversation" : "\d+"})
 */

5. 操作顺序不合理,易导致数据不一致

当前代码先发布Mercure更新再保存数据库,若Mercure发布成功但数据库保存失败,会出现“消息已推送但未持久化”的不一致情况。

修复:调整顺序,先保存数据库再发布Mercure更新:

try {
    // 先持久化并保存到数据库
    $this->em->persist($message);
    $this->em->flush();

    // 再构建并发布Mercure更新
    $date = new \DateTime('now');
    // ... 构建Update代码 ...
    $hub->publish($update);
} catch (\Exception $e) {
    throw $e;
}

修改后的完整add方法示例

/**
 * Create new message
 *
 * @Route("/{groupConversation}/add", name="add", requirements={"groupConversation" : "\d+"})
 */
public function add(Request $request,
                    HubInterface $hub,
                    GroupConversation $groupConversation): Response
{
    $this->denyAccessUnlessGranted('ROLE_USER');
    $user = $this->security->getUser();
    if(!($user)) {
        $this->addFlash('error', 'Utilisateur incorrect.');
        return $this->redirectToRoute('app_login');
    }

    $content = $request->get('message-box', null);

    if ($content) {
        $message = new Message();
        $message->setCreated(new \DateTime('now'));
        $message->setUpdated(new \DateTime('now'));
        $message->setContent($content);
        $message->setMine(true);
        $message->setSeen(false);
        $message->setUser($user);
        $groupConversation->addMessage($message);

        try {
            // 先持久化消息到数据库
            $this->em->persist($message);
            $this->em->flush();

            // 提取会话用户的Mercure订阅主题
            $targets = [];
            foreach ($groupConversation->getUsers() as $conversationUser) {
                $targets[] = '/user/' . $conversationUser->getId();
            }

            $date   = new \DateTime('now');
            $update = new Update(
                '/messages/' . $groupConversation->getId(),
                json_encode([
                    'conversation'  => 'Nouveau message conversation :' . $groupConversation->getName(),
                    'message'       => $content,
                    'from'          => $user->getUsername(),
                    'to'            => array_map(function(User $u) {
                        return ['id' => $u->getId(), 'username' => $u->getUsername()];
                    }, $groupConversation->getUsers()->toArray()),
                    'date'          => $date->format('H:i'),
                ]),
                $groupConversation->getPrivate(),
                'message-' . Uuid::v4(),
                'message',
                $targets
            );

            $hub->publish($update);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    return $this->redirectToRoute('messages_browse', ['groupConversation' => $groupConversation->getId()] );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 15:09:12