$collections */ public function __construct( public int $id, public string $title, public string $briefing, public string $model, public array $collections, public int $contextLimit, public GenerationStatus $generationStatus, public CritiqueStatus $critiqueStatus, public ?int $authorProfileId, public ?int $contractId, public ?int $structureId, public \DateTimeImmutable $createdAt, public ?\DateTimeImmutable $updatedAt, public ?int $latestVersionId = null, public ?string $authorProfileName = null, public ?string $contractName = null, public ?string $structureName = null, ) {} /** * Create from database row. * * @param array $row */ public static function fromDatabaseRow(array $row): self { return new self( id: (int) $row['id'], title: (string) $row['title'], briefing: (string) $row['briefing'], model: (string) $row['model'], collections: self::parseCollections($row['collections'] ?? ''), contextLimit: (int) ($row['context_limit'] ?? 3), generationStatus: GenerationStatus::from((string) ($row['generation_status'] ?? 'idle')), critiqueStatus: CritiqueStatus::from((string) ($row['critique_status'] ?? 'idle')), authorProfileId: isset($row['author_profile_id']) ? (int) $row['author_profile_id'] : null, contractId: isset($row['contract_id']) ? (int) $row['contract_id'] : null, structureId: isset($row['structure_id']) ? (int) $row['structure_id'] : null, createdAt: new \DateTimeImmutable($row['created_at'] ?? 'now'), updatedAt: isset($row['updated_at']) ? new \DateTimeImmutable($row['updated_at']) : null, latestVersionId: isset($row['latest_version_id']) ? (int) $row['latest_version_id'] : null, authorProfileName: $row['author_profile_name'] ?? null, contractName: $row['contract_name'] ?? null, structureName: $row['structure_name'] ?? null, ); } /** * Parse comma-separated collections or JSON array. * * @return array */ private static function parseCollections(string|array $collections): array { if (is_array($collections)) { return $collections; } if ($collections === '') { return []; } // Try JSON first $decoded = json_decode($collections, true); if (is_array($decoded)) { return $decoded; } // Fall back to comma-separated return array_filter(array_map('trim', explode(',', $collections))); } /** * Check if generation can be started. */ public function canStartGeneration(): bool { return $this->generationStatus->canStart(); } /** * Check if critique can be started. */ public function canStartCritique(): bool { return $this->critiqueStatus->canStart() && $this->latestVersionId !== null; } }