Files
Critters/single-critter.php
T
pauljd 9805d5056d
pauljd/davidson-ci: Davidson CI / quality (push) Successful in 14s
pauljd/davidson-ci: Davidson CI / ai-review (push) Successful in 1m5s
issues identified by Davidson CI
2026-09-09 22:49:46 +01:00

190 lines
9.1 KiB
PHP

<?php
require __DIR__ . '/session.php';
require __DIR__ . '/db.php';
start_app_session();
if (!isset($_SESSION['username']) || !isset($_SESSION['user_id'])) {
header('Location: /');
exit;
}
$pdo = getDbConnection();
$critterId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
function fetchOwnedCritter(PDO $pdo, int $userId, int $critterId)
{
$stmt = $pdo->prepare('
SELECT c.id AS id, c.author_id AS author_id, c.isFavorite AS isFavorite, c.isArchived AS isArchived,
c.adjective_id AS adjective_id, c.noun_id AS noun_id, c.verb_id AS verb_id, c.location_id AS location_id,
a.value AS adjective, n.value AS noun, v.value AS verb, l.value AS location, c.image AS image
FROM critters c
JOIN adjectives a ON a.id = c.adjective_id
JOIN nouns n ON n.id = c.noun_id
JOIN verbs v ON v.id = c.verb_id
JOIN locations l ON l.id = c.location_id
WHERE c.id = :id AND c.author_id = :author_id
');
$stmt->execute(['id' => $critterId, 'author_id' => $userId]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
$critter = null;
if ($critterId !== null && $critterId !== false) {
$critter = fetchOwnedCritter($pdo, (int) $_SESSION['user_id'], $critterId);
}
$isOwner = (bool) $critter;
$relatedCritters = [];
if ($critter) {
$relatedStmt = $pdo->prepare('
SELECT id, image FROM critters
WHERE author_id = :author_id
AND adjective_id = :adjective_id
AND noun_id = :noun_id
AND verb_id = :verb_id
AND location_id = :location_id
AND id != :current_id
ORDER BY id DESC
');
$relatedStmt->execute([
'author_id' => $critter['author_id'],
'adjective_id' => $critter['adjective_id'],
'noun_id' => $critter['noun_id'],
'verb_id' => $critter['verb_id'],
'location_id' => $critter['location_id'],
'current_id' => $critter['id'],
]);
$relatedCritters = array_values(array_filter(
$relatedStmt->fetchAll(PDO::FETCH_ASSOC),
fn($row) => !empty($row['image'])
));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $critter && $isOwner && csrf_verify($_POST['csrf_token'] ?? null)) {
$action = $_POST['action'] ?? '';
if ($action === 'toggle_favorite') {
$newValue = $critter['isFavorite'] ? 0 : 1;
$stmt = $pdo->prepare('UPDATE critters SET isFavorite = :value WHERE id = :id');
$stmt->execute(['value' => $newValue, 'id' => $critterId]);
} elseif ($action === 'toggle_archived') {
$newValue = $critter['isArchived'] ? 0 : 1;
$stmt = $pdo->prepare('UPDATE critters SET isArchived = :value WHERE id = :id');
$stmt->execute(['value' => $newValue, 'id' => $critterId]);
}
header('Location: single-critter.php?id=' . $critterId);
exit;
}
$pageTitle = 'View Critter';
?>
<!DOCTYPE html>
<html lang="en">
<?php require __DIR__ . '/lib/head.php'; ?>
<body class="min-h-screen flex flex-col">
<?php require __DIR__ . '/lib/header.php'; ?>
<div class="flex-1 max-w-md mx-auto px-4 py-10 text-center">
<?php if ($critter): ?>
<?php if (!empty($critter['image'])): ?>
<img
src="critter-images/<?php echo htmlspecialchars($critter['image'], ENT_QUOTES, 'UTF-8'); ?>"
alt="AI-generated image of this critter"
class="w-full aspect-square object-cover rounded-lg border border-gray-200 shadow-sm mb-4"
>
<?php endif; ?>
<h1 class="text-2xl font-semibold mb-2">
<?php echo htmlspecialchars(ucwords($critter['adjective'] . ' ' . $critter['noun']), ENT_QUOTES, 'UTF-8'); ?>
</h1>
<p class="text-lg">
The <?php
echo htmlspecialchars($critter['adjective'], ENT_QUOTES, 'UTF-8') . ' '
. htmlspecialchars($critter['noun'], ENT_QUOTES, 'UTF-8') . ' '
. htmlspecialchars($critter['verb'], ENT_QUOTES, 'UTF-8') . ' '
. htmlspecialchars($critter['location'], ENT_QUOTES, 'UTF-8');
?>
</p>
<div class="flex justify-center gap-4 mt-6">
<?php if ($isOwner): ?>
<form action="single-critter.php?id=<?php echo (int) $critterId; ?>" method="post">
<?php echo csrf_field(); ?>
<input type="hidden" name="action" value="toggle_favorite">
<button
type="submit"
class="px-4 py-2 rounded-md font-medium border <?php echo $critter['isFavorite'] ? 'bg-yellow-400 border-yellow-400 text-yellow-950 hover:bg-yellow-500' : 'bg-white border-gray-300 text-gray-800 hover:bg-gray-100'; ?>"
>
<?php echo $critter['isFavorite'] ? 'Unfavorite' : 'Favorite'; ?>
</button>
</form>
<form action="single-critter.php?id=<?php echo (int) $critterId; ?>" method="post">
<?php echo csrf_field(); ?>
<input type="hidden" name="action" value="toggle_archived">
<button
type="submit"
class="px-4 py-2 rounded-md font-medium border <?php echo $critter['isArchived'] ? 'bg-gray-800 border-gray-800 text-white hover:bg-gray-700' : 'bg-white border-gray-300 text-gray-800 hover:bg-gray-100'; ?>"
>
<?php echo $critter['isArchived'] ? 'Unarchive' : 'Archive'; ?>
</button>
</form>
<?php endif; ?>
<form action="create-critter.php" method="get">
<input type="hidden" name="adjective_id" value="<?php echo (int) $critter['adjective_id']; ?>">
<input type="hidden" name="noun_id" value="<?php echo (int) $critter['noun_id']; ?>">
<input type="hidden" name="verb_id" value="<?php echo (int) $critter['verb_id']; ?>">
<input type="hidden" name="location_id" value="<?php echo (int) $critter['location_id']; ?>">
<button type="submit" class="px-4 py-2 rounded-md font-medium border bg-white border-gray-300 text-gray-800 hover:bg-gray-100">
Remix
</button>
</form>
</div>
<div class="mt-8 pt-6 border-t border-gray-200 text-center">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Quick Filters</h2>
<ul class="space-y-2">
<li>
<a href="/?filter_noun=<?php echo (int) $critter['noun_id']; ?>" class="hover:underline">
Show me all my <?php echo htmlspecialchars($critter['noun'], ENT_QUOTES, 'UTF-8'); ?> critters
</a>
</li>
<li>
<a href="/?filter_adjective=<?php echo (int) $critter['adjective_id']; ?>" class="hover:underline">
Show me all my <?php echo htmlspecialchars($critter['adjective'], ENT_QUOTES, 'UTF-8'); ?> critters
</a>
</li>
<li>
<a href="/?filter_verb=<?php echo (int) $critter['verb_id']; ?>" class="hover:underline">
Show me any critter that I've created that <?php echo htmlspecialchars($critter['verb'], ENT_QUOTES, 'UTF-8'); ?>
</a>
</li>
<li>
<a href="/?filter_location=<?php echo (int) $critter['location_id']; ?>" class="hover:underline">
Show me all my critters <?php echo htmlspecialchars($critter['location'], ENT_QUOTES, 'UTF-8'); ?>
</a>
</li>
</ul>
</div>
<?php if (!empty($relatedCritters)): ?>
<div class="mt-8 pt-6 border-t border-gray-200 text-center">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Other critters with this same recipe</h2>
<div class="grid grid-cols-4 gap-2">
<?php foreach ($relatedCritters as $related): ?>
<a href="single-critter.php?id=<?php echo (int) $related['id']; ?>">
<img
src="critter-images/<?php echo htmlspecialchars($related['image'], ENT_QUOTES, 'UTF-8'); ?>"
alt="AI-generated image of a matching critter"
class="w-full aspect-square object-cover rounded-lg border border-gray-200 shadow-sm"
>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php else: ?>
<p>Critter not found.</p>
<?php endif; ?>
</div>
<?php require __DIR__ . '/lib/footer.php'; ?>
</body>
</html>