Symfony 6.3添加ManyToOne关联后用户会话丢失及刷新异常求助
Symfony 6.3.1 用户会话丢失问题解决方法
问题场景
使用Symfony 6.3.1 + Security Bundle(含SAML OneLogin、KnpU OAuth2认证),实体结构为CoreUser/CoreAd作为映射超类,User继承CoreUser,Ad继承CoreAd。为CoreAd添加与User的ManyToOne关联后,访问app_view_advertisement路由查看Ad详情时会话丢失,刷新页面抛出异常:
You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.
未添加该关联时功能正常。
问题原因
- 关联类型不匹配:
CoreAd中owner字段声明为超类CoreUser,Doctrine无法正确识别子类User的实体映射信息,导致加载关联时生成的用户对象缺少必要的标识符元数据。 - 用户对象序列化缺失标识符:Symfony Security序列化用户会话时,未保留Doctrine所需的标识符字段,导致刷新会话时无法重新获取用户实体。
解决方法
1. 修正CoreAd的关联实体类型
将CoreAd中owner字段的目标实体改为实际的User类,同时修正getter/setter的类型声明:
// Core\AdBundle\src\Entity\CoreAd.php namespace Core\AdBundle\src\Entity; use App\Entity\User; use Core\UserBundle\src\Entity\ObjectTrait; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping\JoinColumn; use Symfony\Component\Serializer\Annotation\Groups; #[ORM\MappedSuperclass] class CoreAd { use ObjectTrait; #[Groups(['extended.item'])] #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\ManyToOne(targetEntity: User::class)] #[JoinColumn(name: 'ownerId', referencedColumnName: 'id')] private User $owner; // ... 现有代码 ... public function getOwner(): User { return $this->owner; } public function setOwner(User $owner): CoreAd { $this->owner = $owner; return $this; } }
2. 确保用户实体序列化包含标识符
在CoreUser中实现Serializable接口,明确序列化必要的字段(包括id标识符):
// Core\UserBundle\src\Entity\CoreUser.php namespace Core\UserBundle\src\Entity; use Core\UserBundle\src\Entity\Types\EmailType; use Core\UserBundle\src\Repository\CoreUserRepository; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; use Doctrine\ORM\Mapping as ORM; use Serializable; #[ORM\MappedSuperclass(repositoryClass: CoreUserRepository::class)] class CoreUser implements UserInterface, PasswordAuthenticatedUserInterface, CoreUserInterface, Serializable { use ObjectTrait; // ... 现有代码 ... public function serialize(): string { return serialize([ $this->id, $this->hashEmail, $this->password, $this->roles, ]); } public function unserialize(string $data): void { [$this->id, $this->hashEmail, $this->password, $this->roles] = unserialize($data, ['allowed_classes' => false]); } }
3. 验证Security用户提供者配置
检查config/packages/security.yaml,确保用户提供者指向实际的User实体:
security: providers: app_user_provider: entity: class: App\Entity\User property: hashEmail
4. 避免代理对象序列化问题
在控制器中提前加载关联的用户实体,避免Doctrine代理对象被序列化:
// Core\AdBundle\src\Controller\AdController.php public function view(Ad $advertisement): Response { // 提前加载owner实体,确保不是未初始化的代理对象 $advertisement->getOwner()->getId(); return $this->render('@CoreAd/advertisement/view.html.twig', [ 'ad' => $advertisement ]); }
同时在Twig模板中仅渲染需要的用户字段,避免输出整个用户对象:
{# @CoreAd/advertisement/view.html.twig #} <div class="ad-details"> <h2>{{ ad.title }}</h2> <p>发布者:{{ ad.owner.email }}</p> {# 其他字段... #} </div>
5. 清理缓存并更新数据库结构
修改实体映射后,执行以下命令清理缓存并更新数据库:
php bin/console cache:clear php bin/console doctrine:schema:update --force
内容的提问来源于stack exchange,提问作者perrine
相关产品推荐
相关产品推荐

