usersCache->getItem(self::KEY_PREFIX . $user->id); $item->set($this->serialize($user)); $this->usersCache->save($item); // Save email index for lookup $emailItem = $this->usersCache->getItem(self::EMAIL_INDEX_PREFIX . $this->normalizeEmail($user->email)); $emailItem->set((string) $user->id); $this->usersCache->save($emailItem); } public function findById(UserId $id): ?User { $item = $this->usersCache->getItem(self::KEY_PREFIX . $id); if (!$item->isHit()) { return null; } /** @var array{id: string, email: string, role: string, tenant_id: string, school_name: string, statut: string, hashed_password: string|null, date_naissance: string|null, created_at: string, activated_at: string|null, consentement_parental: array{parent_id: string, eleve_id: string, date_consentement: string, ip_address: string}|null} $data */ $data = $item->get(); return $this->deserialize($data); } public function findByEmail(Email $email): ?User { $emailItem = $this->usersCache->getItem(self::EMAIL_INDEX_PREFIX . $this->normalizeEmail($email)); if (!$emailItem->isHit()) { return null; } /** @var string $userId */ $userId = $emailItem->get(); return $this->findById(UserId::fromString($userId)); } public function get(UserId $id): User { $user = $this->findById($id); if ($user === null) { throw UserNotFoundException::withId($id); } return $user; } /** * @return array */ private function serialize(User $user): array { $consentement = $user->consentementParental; return [ 'id' => (string) $user->id, 'email' => (string) $user->email, 'role' => $user->role->value, 'tenant_id' => (string) $user->tenantId, 'school_name' => $user->schoolName, 'statut' => $user->statut->value, 'hashed_password' => $user->hashedPassword, 'date_naissance' => $user->dateNaissance?->format('Y-m-d'), 'created_at' => $user->createdAt->format('c'), 'activated_at' => $user->activatedAt?->format('c'), 'consentement_parental' => $consentement !== null ? [ 'parent_id' => $consentement->parentId, 'eleve_id' => $consentement->eleveId, 'date_consentement' => $consentement->dateConsentement->format('c'), 'ip_address' => $consentement->ipAddress, ] : null, ]; } /** * @param array{ * id: string, * email: string, * role: string, * tenant_id: string, * school_name: string, * statut: string, * hashed_password: string|null, * date_naissance: string|null, * created_at: string, * activated_at: string|null, * consentement_parental: array{parent_id: string, eleve_id: string, date_consentement: string, ip_address: string}|null * } $data */ private function deserialize(array $data): User { $consentement = null; if ($data['consentement_parental'] !== null) { $consentementData = $data['consentement_parental']; $consentement = ConsentementParental::accorder( parentId: $consentementData['parent_id'], eleveId: $consentementData['eleve_id'], at: new DateTimeImmutable($consentementData['date_consentement']), ipAddress: $consentementData['ip_address'], ); } return User::reconstitute( id: UserId::fromString($data['id']), email: new Email($data['email']), role: Role::from($data['role']), tenantId: TenantId::fromString($data['tenant_id']), schoolName: $data['school_name'], statut: StatutCompte::from($data['statut']), dateNaissance: $data['date_naissance'] !== null ? new DateTimeImmutable($data['date_naissance']) : null, createdAt: new DateTimeImmutable($data['created_at']), hashedPassword: $data['hashed_password'], activatedAt: $data['activated_at'] !== null ? new DateTimeImmutable($data['activated_at']) : null, consentementParental: $consentement, ); } private function normalizeEmail(Email $email): string { return strtolower(str_replace(['@', '.'], ['_at_', '_dot_'], (string) $email)); } }