Files
michaelschiemer/src/Framework/Pagination/ValueObjects/Cursor.php
Michael Schiemer 5050c7d73a docs: consolidate documentation into organized structure
- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
2025-10-05 11:05:04 +02:00

125 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Pagination\ValueObjects;
use App\Framework\Serializer\Json\JsonSerializer;
use App\Framework\Serializer\Exception\SerializeException;
use App\Framework\Serializer\Exception\DeserializeException;
use InvalidArgumentException;
/**
* Cursor for cursor-based pagination
*/
final readonly class Cursor
{
public function __construct(
public string $value,
public Direction $direction = Direction::ASC
) {
if (empty($this->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();
}
}