value)) { throw new InvalidArgumentException('Cursor value cannot be empty'); } } /** * Create cursor from base64 encoded value */ public static function fromEncoded(string $encodedValue, Direction $direction = Direction::ASC): self { $decoded = base64_decode($encodedValue, true); if ($decoded === false) { throw new InvalidArgumentException('Invalid cursor encoding'); } return new self($decoded, $direction); } /** * Create cursor from field value */ public static function fromField(string $field, mixed $value, Direction $direction = Direction::ASC): self { $serializer = new JsonSerializer(); try { $cursorValue = $serializer->serialize([$field => $value]); } catch (SerializeException $e) { throw new InvalidArgumentException('Cannot encode cursor value: ' . $e->getMessage(), 0, $e); } return new self($cursorValue, $direction); } /** * Get base64 encoded cursor value */ public function toEncoded(): string { return base64_encode($this->value); } /** * Parse cursor value as array */ public function parseValue(): array { $serializer = new JsonSerializer(); try { $parsed = $serializer->deserialize($this->value); } catch (DeserializeException $e) { throw new InvalidArgumentException('Cursor value is not valid JSON: ' . $e->getMessage(), 0, $e); } if (!is_array($parsed)) { throw new InvalidArgumentException('Cursor value is not an array'); } return $parsed; } /** * Get field value from cursor */ public function getFieldValue(string $field): mixed { $parsed = $this->parseValue(); return $parsed[$field] ?? null; } /** * Check if cursor is for ascending direction */ public function isAscending(): bool { return $this->direction->isAscending(); } /** * Check if cursor is for descending direction */ public function isDescending(): bool { return $this->direction->isDescending(); } /** * Create new cursor with different direction */ public function withDirection(Direction $direction): self { return new self($this->value, $direction); } public function toString(): string { return $this->toEncoded(); } public function __toString(): string { return $this->toString(); } }