activationTokensCache->getItem(self::KEY_PREFIX . $token->tokenValue); $item->set($this->serialize($token)); $item->expiresAfter(self::TTL_SECONDS); $this->activationTokensCache->save($item); // Also store by ID for direct access $idItem = $this->activationTokensCache->getItem(self::KEY_PREFIX . 'id:' . $token->id); $idItem->set($token->tokenValue); $idItem->expiresAfter(self::TTL_SECONDS); $this->activationTokensCache->save($idItem); } #[Override] public function findByTokenValue(string $tokenValue): ?ActivationToken { $item = $this->activationTokensCache->getItem(self::KEY_PREFIX . $tokenValue); if (!$item->isHit()) { return null; } /** @var array{id: string, token_value: string, user_id: string, email: string, tenant_id: string, role: string, school_name: string, created_at: string, expires_at: string, used_at: string|null} $data */ $data = $item->get(); return $this->deserialize($data); } #[Override] public function get(ActivationTokenId $id): ActivationToken { // First get the token value from the ID index $idItem = $this->activationTokensCache->getItem(self::KEY_PREFIX . 'id:' . $id); if (!$idItem->isHit()) { throw ActivationTokenNotFoundException::withId($id); } /** @var string $tokenValue */ $tokenValue = $idItem->get(); $token = $this->findByTokenValue($tokenValue); if ($token === null) { throw ActivationTokenNotFoundException::withId($id); } return $token; } #[Override] public function delete(ActivationTokenId $id): void { // Get token value first $idItem = $this->activationTokensCache->getItem(self::KEY_PREFIX . 'id:' . $id); if ($idItem->isHit()) { /** @var string $tokenValue */ $tokenValue = $idItem->get(); $this->activationTokensCache->deleteItem(self::KEY_PREFIX . $tokenValue); } $this->activationTokensCache->deleteItem(self::KEY_PREFIX . 'id:' . $id); } #[Override] public function deleteByTokenValue(string $tokenValue): void { $token = $this->findByTokenValue($tokenValue); if ($token !== null) { $this->activationTokensCache->deleteItem(self::KEY_PREFIX . 'id:' . $token->id); } $this->activationTokensCache->deleteItem(self::KEY_PREFIX . $tokenValue); } /** * @return array{id: string, token_value: string, user_id: string, email: string, tenant_id: string, role: string, school_name: string, created_at: string, expires_at: string, used_at: string|null} */ private function serialize(ActivationToken $token): array { return [ 'id' => (string) $token->id, 'token_value' => $token->tokenValue, 'user_id' => $token->userId, 'email' => $token->email, 'tenant_id' => (string) $token->tenantId, 'role' => $token->role, 'school_name' => $token->schoolName, 'created_at' => $token->createdAt->format(DateTimeImmutable::ATOM), 'expires_at' => $token->expiresAt->format(DateTimeImmutable::ATOM), 'used_at' => $token->usedAt?->format(DateTimeImmutable::ATOM), ]; } /** * @param array{id: string, token_value: string, user_id: string, email: string, tenant_id: string, role: string, school_name: string, created_at: string, expires_at: string, used_at: string|null} $data */ private function deserialize(array $data): ActivationToken { return ActivationToken::reconstitute( id: ActivationTokenId::fromString($data['id']), tokenValue: $data['token_value'], userId: $data['user_id'], email: $data['email'], tenantId: TenantId::fromString($data['tenant_id']), role: $data['role'], schoolName: $data['school_name'], createdAt: new DateTimeImmutable($data['created_at']), expiresAt: new DateTimeImmutable($data['expires_at']), usedAt: $data['used_at'] !== null ? new DateTimeImmutable($data['used_at']) : null, ); } }