ContentController.php
- Pfad:
src/Controller/ContentController.php
- Namespace: Controller
- Zeilen: 435 | Größe: 15,078 Bytes
- Geändert: 2025-12-30 22:24:58 | Gescannt: 2025-12-31 10:22:15
Code Hygiene Score: 77
- Dependencies: 80 (25%)
- LOC: 32 (20%)
- Methods: 90 (20%)
- Secrets: 100 (15%)
- Classes: 100 (10%)
- Magic Numbers: 80 (10%)
Issues 2
| Zeile |
Typ |
Beschreibung |
| 396 |
magic_number |
Magic Number gefunden: 1000 |
| 407 |
magic_number |
Magic Number gefunden: 1000 |
Dependencies 12
- extends Framework\Controller
- constructor Domain\Repository\ContentRepositoryInterface
- constructor Application\ContentCollectionService
- constructor UseCases\Content\GenerateContentUseCase
- constructor Infrastructure\AI\ModelRegistry
- use Application\ContentCollectionService
- use Domain\Repository\ContentRepositoryInterface
- use Framework\Controller
- use Infrastructure\AI\ModelRegistry
- use UseCases\Command\CreateContentOrderCommand
- use UseCases\Command\GenerateContentCommand
- use UseCases\Content\GenerateContentUseCase
Klassen 1
-
ContentController
class
Zeile 17
Funktionen 16
-
__construct()
public
Zeile 19
-
index()
public
Zeile 30
-
contentNew()
public
Zeile 45
-
store()
public
Zeile 75
-
show()
public
Zeile 144
-
edit()
public
Zeile 171
-
generate()
public
Zeile 194
-
generationStatus()
public
Zeile 223
-
critique()
public
Zeile 276
-
critiqueStatus()
public
Zeile 298
-
revise()
public
Zeile 367
-
approve()
public
Zeile 391
-
decline()
public
Zeile 402
-
getFirstContractId()
private
Zeile 410
-
renderVersionPartial()
private
Zeile 417
-
renderCritiquePartial()
private
Zeile 426
Versionen 71
-
v71
2025-12-30 22:24 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v70
2025-12-30 22:24 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v69
2025-12-30 22:24 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v68
2025-12-30 22:23 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v67
2025-12-27 12:55 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v66
2025-12-25 13:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v65
2025-12-25 13:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v64
2025-12-25 13:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v63
2025-12-25 13:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v62
2025-12-25 12:52 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v61
2025-12-24 01:29 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v60
2025-12-24 00:41 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v59
2025-12-24 00:31 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v58
2025-12-24 00:18 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v57
2025-12-24 00:12 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v56
2025-12-23 07:51 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v55
2025-12-23 04:45 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v54
2025-12-23 04:42 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v53
2025-12-23 04:24 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v52
2025-12-22 21:19 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Write-Operation
-
v51
2025-12-22 15:42 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v50
2025-12-22 15:42 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v49
2025-12-22 15:40 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v48
2025-12-22 15:40 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v47
2025-12-22 15:40 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v46
2025-12-22 15:40 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v45
2025-12-22 15:38 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v44
2025-12-22 15:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v43
2025-12-22 15:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v42
2025-12-22 15:27 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v41
2025-12-22 08:06 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v40
2025-12-22 02:18 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v39
2025-12-22 02:17 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v38
2025-12-22 02:17 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v37
2025-12-22 02:17 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v36
2025-12-22 02:17 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v35
2025-12-22 02:17 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v34
2025-12-22 02:10 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v33
2025-12-22 02:10 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v32
2025-12-22 01:46 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v31
2025-12-22 01:46 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v30
2025-12-21 17:01 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v29
2025-12-21 14:39 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v28
2025-12-21 14:36 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v27
2025-12-21 14:35 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v26
2025-12-21 14:35 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v25
2025-12-21 14:35 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v24
2025-12-21 14:35 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v23
2025-12-21 14:35 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v22
2025-12-21 09:14 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v21
2025-12-21 09:14 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v20
2025-12-21 04:13 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v19
2025-12-21 04:13 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v18
2025-12-21 04:11 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v17
2025-12-21 04:11 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v16
2025-12-21 04:11 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v15
2025-12-21 04:11 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v14
2025-12-21 03:00 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v13
2025-12-21 02:59 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v12
2025-12-21 02:59 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v11
2025-12-21 02:59 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v10
2025-12-20 19:58 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v9
2025-12-20 19:57 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v8
2025-12-20 19:57 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v7
2025-12-20 19:55 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v6
2025-12-20 19:20 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v5
2025-12-20 19:20 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v4
2025-12-20 19:20 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v3
2025-12-20 19:20 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v2
2025-12-20 19:19 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
-
v1
2025-12-20 19:19 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
Code
<?php
declare(strict_types=1);
namespace Controller;
// @responsibility: HTTP-Endpunkte für Content Studio (Aufträge, Generierung, Kritik)
use Application\ContentCollectionService;
use Domain\Repository\ContentRepositoryInterface;
use Framework\Controller;
use Infrastructure\AI\ModelRegistry;
use UseCases\Command\CreateContentOrderCommand;
use UseCases\Command\GenerateContentCommand;
use UseCases\Content\GenerateContentUseCase;
class ContentController extends Controller
{
public function __construct(
private ContentRepositoryInterface $repository,
private ContentCollectionService $collectionService,
private GenerateContentUseCase $generateUseCase,
private ModelRegistry $modelRegistry
) {
}
/**
* GET /content - List all content orders
*/
public function index(): void
{
$status = $this->getString('status');
$this->view('content.index', [
'title' => 'Content Studio',
'orders' => $this->repository->findAllOrders($status !== '' ? ['status' => $status] : []),
'stats' => $this->repository->getStatistics(),
'currentStatus' => $status,
]);
}
/**
* GET /content/new - Show create form
*/
public function contentNew(): void
{
$lastSettings = $this->repository->getLastOrderSettings();
$this->view('content.new', [
'title' => 'Neuer Content-Auftrag',
'profiles' => $this->repository->findAllProfiles(),
'contracts' => $this->repository->findAllContracts(),
'structures' => $this->repository->findAllStructures(),
'systemPrompts' => $this->repository->findAllSystemPrompts(),
'critics' => $this->repository->findAllCritics(),
'models' => $this->modelRegistry->getChatModels(),
'collections' => $this->collectionService->getAvailable(),
'defaultModel' => $lastSettings['model'],
'defaultCollections' => $lastSettings['collections'],
'defaultContextLimit' => $lastSettings['context_limit'],
'defaultProfileId' => $lastSettings['author_profile_id'],
'defaultContractId' => $lastSettings['contract_id'],
'defaultStructureId' => $lastSettings['structure_id'],
'defaultTemperature' => $lastSettings['temperature'],
'defaultMaxTokens' => $lastSettings['max_tokens'],
'defaultSystemPromptId' => $lastSettings['system_prompt_id'],
'defaultSelectedCritics' => $lastSettings['selected_critics'],
'defaultQualityCheck' => $lastSettings['quality_check'],
]);
}
/**
* POST /content - Store new order
*/
public function store(): void
{
$this->requireCsrf();
$isHtmx = $this->isHtmxRequest();
$command = CreateContentOrderCommand::fromRequest($_POST);
$errors = $command->validate();
if ($errors !== []) {
if ($isHtmx) {
$this->htmxError(implode(' ', $errors));
return;
}
$_SESSION['error'] = implode(' ', $errors);
$this->redirect('/content/new');
}
// Validate collections
$result = $this->collectionService->validateWithCompatibility($command->collections);
if (!$result['valid']) {
if ($isHtmx) {
$this->htmxError('Collection-Fehler: ' . $result['error']);
return;
}
$_SESSION['error'] = 'Collection-Fehler: ' . $result['error'];
$this->redirect('/content/new');
}
$orderId = $this->repository->createOrder([
'title' => $command->title,
'briefing' => $command->briefing,
'author_profile_id' => $command->authorProfileId,
'contract_id' => $command->contractId ?? $this->getFirstContractId(),
'structure_id' => $command->structureId,
'model' => $this->modelRegistry->isValid($command->model) ? $command->model : $this->modelRegistry->getDefaultChatModel(),
'collections' => json_encode($result['collections']),
'context_limit' => $command->contextLimit,
'temperature' => $command->temperature,
'max_tokens' => $command->maxTokens,
'system_prompt_id' => $command->systemPromptId,
'selected_critics' => $command->selectedCritics,
'quality_check' => $command->qualityCheck,
]);
if ($command->shouldGenerate()) {
// Start async generation (non-blocking)
$this->repository->updateGenerationStatus($orderId, 'generating');
$this->generateUseCase->generateAsync(
$orderId,
$this->modelRegistry->isValid($command->model) ? $command->model : $this->modelRegistry->getDefaultChatModel(),
$result['collections'][0] ?? 'documents',
$command->contextLimit
);
}
if ($isHtmx) {
$this->htmxRedirect('/content/' . $orderId);
return;
}
$this->redirect('/content/' . $orderId);
}
/**
* GET /content/{id} - Show order details
*/
public function show(int $id): void
{
$order = $this->repository->findOrder($id);
if ($order === null) {
$this->notFound('Auftrag nicht gefunden');
}
$versions = $this->repository->findVersionsByOrder($id);
$latestVersion = $versions[0] ?? null;
$this->view('content.show', [
'title' => $order['title'],
'order' => $order,
'versions' => $versions,
'latestVersion' => $latestVersion,
'critiques' => $latestVersion ? $this->repository->findCritiquesByVersion($latestVersion['id']) : [],
'sources' => $this->repository->findSourcesByOrder($id),
'models' => $this->modelRegistry->getChatModels(),
'availableCollections' => $this->collectionService->getAvailable(),
'systemPrompts' => $this->repository->findAllSystemPrompts(),
'allCritics' => $this->repository->findAllCritics(),
]);
}
/**
* GET /content/{id}/edit - Show edit form
*/
public function edit(int $id): void
{
$order = $this->repository->findOrder($id);
if ($order === null) {
$this->notFound('Auftrag nicht gefunden');
}
$this->view('content.edit', [
'title' => 'Auftrag bearbeiten',
'order' => $order,
'profiles' => $this->repository->findAllProfiles(),
'contracts' => $this->repository->findAllContracts(),
'structures' => $this->repository->findAllStructures(),
'systemPrompts' => $this->repository->findAllSystemPrompts(),
'critics' => $this->repository->findAllCritics(),
'models' => $this->modelRegistry->getChatModels(),
'collections' => $this->collectionService->getAvailable(),
]);
}
/**
* POST /content/{id}/generate - Start async content generation (HTMX)
*/
public function generate(int $id): void
{
$this->requireCsrf();
$command = GenerateContentCommand::fromRequest($id, $_POST);
if (($errors = $command->validate()) !== []) {
$this->htmxError(implode(' ', $errors));
return;
}
$result = $this->collectionService->validateWithCompatibility([$command->collection]);
if (!$result['valid']) {
$this->htmxError($result['error'] ?? 'Collection-Fehler');
return;
}
// Set status to generating and start async
$this->repository->updateGenerationStatus($id, 'generating');
$this->generateUseCase->generateAsync($id, $command->model, $result['collections'][0], $command->contextLimit);
// Return polling partial
$this->view('content.partials.generating', ['orderId' => $id]);
}
/**
* GET /content/{id}/generation-status - Poll generation status (HTMX)
*/
public function generationStatus(int $id): void
{
$order = $this->repository->findOrder($id);
if ($order === null) {
$this->htmxError('Auftrag nicht gefunden');
return;
}
$status = $order['generation_status'] ?? 'idle';
if ($status === 'completed') {
$version = $this->repository->findLatestVersion($id);
if ($version !== null) {
// Reset status to idle after showing result
$this->repository->updateGenerationStatus($id, 'idle');
$this->renderVersionPartial([
'content' => $version['content'],
'sources' => $this->repository->findSourcesByOrder($id),
'version_number' => $version['version_number'],
]);
return;
}
}
if ($status === 'failed') {
// Reset status after showing error
$error = $order['generation_error'] ?? 'Unbekannter Fehler';
$this->repository->updateGenerationStatus($id, 'idle');
$this->htmxError('Generierung fehlgeschlagen: ' . $error);
return;
}
if ($status === 'generating' || $status === 'queued') {
// Still generating - return polling partial with log
$this->view('content.partials.generating', [
'orderId' => $id,
'log' => $order['generation_log'] ?? '',
'step' => $order['generation_step'] ?? '',
]);
return;
}
// Status is idle - return empty (nothing happening)
$this->html('');
}
/**
* POST /content/{id}/critique - Start async critique round (HTMX)
*/
public function critique(int $id): void
{
$this->requireCsrf();
$version = $this->repository->findLatestVersion($id);
if ($version === null) {
$this->htmxError('Keine Version vorhanden.');
return;
}
// Set status to critiquing and start async
$this->repository->updateCritiqueStatus($id, 'critiquing');
$this->generateUseCase->critiqueAsync($id, $version['id'], $_POST['model'] ?? 'claude-opus-4-5-20251101');
// Return polling partial
$this->view('content.partials.critiquing-live', ['orderId' => $id]);
}
/**
* GET /content/{id}/critique-status - Poll critique status (HTMX)
*/
public function critiqueStatus(int $id): void
{
$order = $this->repository->findOrder($id);
if ($order === null) {
$this->htmxError('Auftrag nicht gefunden');
return;
}
$status = $order['critique_status'] ?? 'idle';
if ($status === 'completed') {
// Get latest version and its critiques
$version = $this->repository->findLatestVersion($id);
if ($version !== null) {
$critiques = $this->repository->findCritiquesByVersion($version['id']);
// Get the latest round
$latestRound = $order['current_critique_round'] ?? 1;
// Filter critiques for latest round
$roundCritiques = array_filter($critiques, fn ($c) => ($c['round'] ?? 0) === $latestRound);
// Reset status to idle after showing result
$this->repository->updateCritiqueStatus($id, 'idle');
// Check if all passed
$allPassed = true;
foreach ($roundCritiques as $c) {
if (!($c['passed'] ?? false)) {
$allPassed = false;
break;
}
}
$this->renderCritiquePartial([
'critiques' => array_values($roundCritiques),
'all_passed' => $allPassed,
'round' => $latestRound,
]);
return;
}
}
if ($status === 'failed') {
$error = $order['critique_error'] ?? 'Unbekannter Fehler';
$this->repository->updateCritiqueStatus($id, 'idle');
$this->htmxError('Kritik fehlgeschlagen: ' . $error);
return;
}
if ($status === 'critiquing') {
// Still critiquing - return polling partial with log
$this->view('content.partials.critiquing-live', [
'orderId' => $id,
'log' => $order['critique_log'] ?? '',
'step' => $order['critique_step'] ?? '',
]);
return;
}
// Status is idle - return empty
$this->html('');
}
/**
* POST /content/{id}/revise - Create revision (HTMX)
*/
public function revise(int $id): void
{
$this->requireCsrf();
$version = $this->repository->findLatestVersion($id);
if ($version === null) {
$this->htmxError('Keine Version vorhanden.');
return;
}
$result = $this->generateUseCase->revise($version['id'], $_POST['model'] ?? 'claude-opus-4-5-20251101');
if ($result->hasError()) {
$this->htmxError('Fehler: ' . $result->getError());
return;
}
$this->renderVersionPartial($result->toArray());
}
/**
* POST /content/{id}/approve - Approve content
*/
public function approve(int $id): void
{
$this->requireCsrf();
$this->repository->updateOrderStatus($id, 'approve');
$this->htmxSuccess('Content genehmigt!');
$this->html('<script>setTimeout(() => window.location.reload(), 1000);</script>');
}
/**
* POST /content/{id}/decline - Decline content
*/
public function decline(int $id): void
{
$this->requireCsrf();
$this->repository->updateOrderStatus($id, 'draft');
$this->htmxAlert('warning', 'Content abgelehnt. Zurück zu Entwurf.');
$this->html('<script>setTimeout(() => window.location.reload(), 1000);</script>');
}
private function getFirstContractId(): ?int
{
$contracts = $this->repository->findAllContracts();
return $contracts !== [] ? (int) $contracts[0]['id'] : null;
}
private function renderVersionPartial(array $result): void
{
$this->view('content.partials.version', [
'content' => $result['content'] ?? '',
'sources' => $result['sources'] ?? [],
'versionNumber' => $result['version_number'] ?? '?',
]);
}
private function renderCritiquePartial(array $result): void
{
$this->view('content.partials.critique', [
'critiques' => $result['critiques'] ?? [],
'allPassed' => $result['all_passed'] ?? false,
'round' => $result['round'] ?? '?',
]);
}
}