symfony serialize() 作为成员变量从 __sleep() 返回但不存在

symfony serialize() returned as member variable from __sleep() but does not exist

在做了一些与此操作无关的实体更改后,我突然开始收到此错误

Notice: serialize(): "questionPools" returned as member variable from __sleep() but does not exist

操作看起来像这样:

/**
 * @param UserProvider $userProvider
 * @param Exam $exam
 * @param ExamService $examService
 * @Route("/start/{id}", name="/start")
 */
public function startAction(UserProvider $userProvider, Exam $exam, ExamService $examService)
{
    try {
        $userExam = $examService->startExam($exam);
        $userExam->setUser($userProvider->getUser());
        $this->db()->persist($userExam);
        $this->db()->flush();
    } catch (RuntimeException $exception) {
        $this->addFlash('danger', $exception->getMessage());
        return $this->redirectToRoute('userExam/available');
    } catch (Exception $exception) {
        $this->addFlash('danger', $exception->getMessage());
        return $this->redirectToRoute('userExam/available');
    }
    return $this->redirectToRoute('userExam/fill', ['hash' => $userExam->getHash()]);
}

堆栈跟踪指向供应商,因为错误发生在重定向期间。
“questionPools”是 Exam 实体的 属性:

/**
 * Class Exam
 * @package App\Entity
 * @ORM\Entity(repositoryClass="App\Repository\ExamRepository")
 * @ORM\Table(name="exam",indexes={@ORM\Index(name="search_idx", columns={"start", "stop"})})
 */
class Exam implements EntityInterface
{
    // Time in minutes
    const DEFAULT_TIME = 30;
    const DEFAULT_THRESHOLD = 80;

    use IdTrait;

    /**
     * @var Collection|QuestionPool[]
     * @ORM\OneToMany(targetEntity="App\Entity\QuestionPool",
     *     mappedBy="exam",
     *     cascade={"persist", "remove"},
     *     orphanRemoval=true,
     *     )
     */
    private Collection $questionPools;

startExam(Exam $exam) ExamService class 的方法不会改变这个集合,只会基于它创建一个 UserExam 对象。它应该传递这个对象,我不明白为什么会抛出一些序列化错误。
我认为它与缓存有关,但我什至手动删除了 /var/cache/dev 目录以确保它不是那个。
为了清楚起见,这里是堆栈跟踪:

ErrorException:
Notice: serialize(): "questionPools" returned as member variable from __sleep() but does not exist

  at W:\wamp64\www\exams\vendor\symfony\security-http\Firewall\ContextListener.php:178
  at Symfony\Component\Security\Http\Firewall\ContextListener->onKernelResponse(object(ResponseEvent), 'kernel.response', object(TraceableEventDispatcher))
     (W:\wamp64\www\exams\vendor\symfony\event-dispatcher\Debug\WrappedListener.php:117)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(object(ResponseEvent), 'kernel.response', object(TraceableEventDispatcher))
     (W:\wamp64\www\exams\vendor\symfony\event-dispatcher\EventDispatcher.php:230)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners(array(object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener), object(WrappedListener)), 'kernel.response', object(ResponseEvent))
     (W:\wamp64\www\exams\vendor\symfony\event-dispatcher\EventDispatcher.php:59)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch(object(ResponseEvent), 'kernel.response')
     (W:\wamp64\www\exams\vendor\symfony\event-dispatcher\Debug\TraceableEventDispatcher.php:151)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch(object(ResponseEvent), 'kernel.response')
     (W:\wamp64\www\exams\vendor\symfony\http-kernel\HttpKernel.php:190)
  at Symfony\Component\HttpKernel\HttpKernel->filterResponse(object(RedirectResponse), object(Request), 1)
     (W:\wamp64\www\exams\vendor\symfony\http-kernel\HttpKernel.php:178)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (W:\wamp64\www\exams\vendor\symfony\http-kernel\HttpKernel.php:79)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (W:\wamp64\www\exams\vendor\symfony\http-kernel\Kernel.php:195)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (W:\wamp64\www\exams\public\index.php:20)                

请指教,我真的迷路了。谢谢。

EntityInterface 看起来像这样:

interface EntityInterface
{
    public function getId(): ?int;
}

而 IdTrait 是:

use Doctrine\ORM\Mapping as ORM;

trait IdTrait
{
    /**
     * @var int
     * @ORM\Column(type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private int $id;

    /**
     * @return int
     */
    public function getId(): ?int
    {
        if (empty($this->id)) {
            return null;
        }
        return $this->id;
    }

    /**
     * @param int $id
     */
    public function setId(int $id): void
    {
        $this->id = $id;
    }
}

此外,导致问题的重定向导致的操作:

    /**
 * @param UserProvider $userProvider
 * @param Request $request
 * @param ExamService $examService
 * @param string $hash
 * @return JsonResponse|RedirectResponse|Response
 * @Route("/fill/{hash}", name="/fill")
 */
public function fillAction(
    UserProvider $userProvider,
    Request $request,
    ExamService $examService,
    string $hash
)
{
    /** @var UserExam $userExam */
    $userExam = $this->getRepo(UserExam::class)->findOneBy(['hash' => $hash]);
    if (!$userProvider->checkUser($userExam->getUser())) {
        throw $this->createNotFoundException('Exam belongs to someone else.');
    }
    if (!$userExam->isOngoing()) {
        return $this->redirectToRoute('userExam/show', ['id' => $userExam->getId()]);
    }
    $form = $this->createForm(UserExamFormType::class, $userExam);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        try {
            $exam = $examService->checkExam($userExam, $form->get('finish')->isClicked());
            $this->db()->persist($exam);
            $this->db()->flush();
            $this->db()->refresh($userExam);
            if (!$userExam->isOngoing()) {
                return $this->redirectToRoute('userExam/show', ['id' => $userExam->getId()]);
            }
        } catch (Exception $exception) {
            $this->addFlash('danger', $exception->getMessage());
        }
    }
    return $this->response(
        [
            'form' => $form->createView(),
            'pageName' => $userExam->getExam()->getName()
        ]
    );

}

我正在使用当前版本的 Symfony (5.2.4) 和 Doctrine (2.7 ORM)

通过聊天,我们发现根据文档需要有 SerializationInterface - https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-are-refreshed-from-the-session

我为考试实体实现了可序列化接口,方法如下所示:

public function serialize()
{
    return json_encode(['id' => $this->id]);
}

public function unserialize($serialized)
{
    $data = json_decode($serialized, true);
    $this->id = $data['id'];
}

有效,没有错误,但我认为这只是一个丑陋的解决方法。