This commit is contained in:
@@ -182,6 +182,260 @@ final class ListingCatalog
|
||||
];
|
||||
}
|
||||
|
||||
public function listCategories(?array $groups = null): array
|
||||
{
|
||||
$this->ensureSchema();
|
||||
$params = [];
|
||||
$sql = 'SELECT id, slug, title, category_group FROM listing_categories';
|
||||
if ($groups && $groups !== []) {
|
||||
$placeholders = implode(',', array_fill(0, count($groups), '?'));
|
||||
$sql .= " WHERE category_group IN ($placeholders)";
|
||||
$params = array_values($groups);
|
||||
}
|
||||
$sql .= ' ORDER BY sort_order ASC, title ASC';
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
|
||||
public function listDashboardEntries(int $userId): array
|
||||
{
|
||||
$this->ensureSchema();
|
||||
$stmt = $this->pdo->prepare(
|
||||
'SELECT l.id, l.listing_type, l.title, l.teaser_public, l.visibility, l.status,
|
||||
lp.title AS place_title, lp.city, lp.region, lp.place_kind,
|
||||
lo.starts_at, lo.ends_at, lo.occurrence_type,
|
||||
lc.title AS category_title, lc.slug AS category_slug
|
||||
FROM listings l
|
||||
LEFT JOIN listing_places lp ON lp.id = l.primary_place_id
|
||||
LEFT JOIN listing_occurrences lo ON lo.listing_id = l.id
|
||||
LEFT JOIN listing_category_map lcm ON lcm.listing_id = l.id
|
||||
LEFT JOIN listing_categories lc ON lc.id = lcm.category_id
|
||||
WHERE l.created_by = :uid
|
||||
ORDER BY
|
||||
CASE l.listing_type
|
||||
WHEN "place" THEN 1
|
||||
WHEN "editorial_event" THEN 2
|
||||
ELSE 3
|
||||
END,
|
||||
COALESCE(lo.starts_at, l.created_at) ASC,
|
||||
l.created_at DESC'
|
||||
);
|
||||
$stmt->execute(['uid' => $userId]);
|
||||
return $stmt->fetchAll(\PDO::FETCH_ASSOC) ?: [];
|
||||
}
|
||||
|
||||
public function getDashboardEntry(int $userId, int $listingId): ?array
|
||||
{
|
||||
$this->ensureSchema();
|
||||
$stmt = $this->pdo->prepare(
|
||||
'SELECT l.*, lp.title AS place_title, lp.description AS place_description, lp.street, lp.zip, lp.city, lp.region, lp.lat, lp.lng, lp.place_kind,
|
||||
lo.id AS occurrence_id, lo.starts_at, lo.ends_at, lo.occurrence_type,
|
||||
lc.slug AS category_slug
|
||||
FROM listings l
|
||||
LEFT JOIN listing_places lp ON lp.id = l.primary_place_id
|
||||
LEFT JOIN listing_occurrences lo ON lo.listing_id = l.id
|
||||
LEFT JOIN listing_category_map lcm ON lcm.listing_id = l.id
|
||||
LEFT JOIN listing_categories lc ON lc.id = lcm.category_id
|
||||
WHERE l.id = :id AND l.created_by = :uid
|
||||
LIMIT 1'
|
||||
);
|
||||
$stmt->execute([
|
||||
'id' => $listingId,
|
||||
'uid' => $userId,
|
||||
]);
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function saveDashboardEntry(int $userId, array $data, ?int $listingId = null): int
|
||||
{
|
||||
$this->ensureSchema();
|
||||
$entryType = (string)($data['entry_kind'] ?? 'editorial_event');
|
||||
if (!in_array($entryType, ['place', 'editorial_event'], true)) {
|
||||
throw new \RuntimeException('Ungültiger Eintragstyp.');
|
||||
}
|
||||
|
||||
$title = trim((string)($data['title'] ?? ''));
|
||||
$teaser = trim((string)($data['teaser'] ?? ''));
|
||||
$description = trim((string)($data['description'] ?? ''));
|
||||
$visibility = (string)($data['visibility'] ?? 'public');
|
||||
$placeKind = trim((string)($data['category_slug'] ?? ''));
|
||||
$street = trim((string)($data['street'] ?? ''));
|
||||
$zip = trim((string)($data['zip'] ?? ''));
|
||||
$city = trim((string)($data['city'] ?? ''));
|
||||
$region = trim((string)($data['region'] ?? ''));
|
||||
$lat = isset($data['lat']) && $data['lat'] !== '' ? (float)$data['lat'] : null;
|
||||
$lng = isset($data['lng']) && $data['lng'] !== '' ? (float)$data['lng'] : null;
|
||||
$startsAt = trim((string)($data['starts_at'] ?? ''));
|
||||
|
||||
if ($title === '' || $teaser === '' || $description === '') {
|
||||
throw new \RuntimeException('Bitte fülle Titel, Kurzbeschreibung und Beschreibung aus.');
|
||||
}
|
||||
if (!in_array($visibility, ['public', 'members'], true)) {
|
||||
$visibility = 'public';
|
||||
}
|
||||
if ($entryType === 'editorial_event' && $startsAt === '') {
|
||||
throw new \RuntimeException('Bitte gib für sonstige Veranstaltungen ein Datum an.');
|
||||
}
|
||||
|
||||
$this->pdo->beginTransaction();
|
||||
try {
|
||||
$categoryId = null;
|
||||
if ($placeKind !== '') {
|
||||
$catStmt = $this->pdo->prepare('SELECT id FROM listing_categories WHERE slug = :slug LIMIT 1');
|
||||
$catStmt->execute(['slug' => $placeKind]);
|
||||
$categoryId = $catStmt->fetchColumn();
|
||||
$categoryId = $categoryId !== false ? (int)$categoryId : null;
|
||||
}
|
||||
|
||||
if ($listingId !== null && $listingId > 0) {
|
||||
$existing = $this->getDashboardEntry($userId, $listingId);
|
||||
if (!$existing) {
|
||||
throw new \RuntimeException('Eintrag nicht gefunden.');
|
||||
}
|
||||
$placeId = isset($existing['primary_place_id']) ? (int)$existing['primary_place_id'] : 0;
|
||||
if ($placeId > 0) {
|
||||
$placeStmt = $this->pdo->prepare(
|
||||
'UPDATE listing_places
|
||||
SET title = :title, description = :description, street = :street, zip = :zip, city = :city, region = :region, lat = :lat, lng = :lng, place_kind = :placeKind, updated_at = NOW()
|
||||
WHERE id = :id'
|
||||
);
|
||||
$placeStmt->execute([
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'street' => $street !== '' ? $street : null,
|
||||
'zip' => $zip !== '' ? $zip : null,
|
||||
'city' => $city !== '' ? $city : null,
|
||||
'region' => $region !== '' ? $region : null,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'placeKind' => $placeKind !== '' ? $placeKind : null,
|
||||
'id' => $placeId,
|
||||
]);
|
||||
}
|
||||
$listingStmt = $this->pdo->prepare(
|
||||
'UPDATE listings
|
||||
SET listing_type = :listingType, title = :title, teaser_public = :teaser, description = :description, visibility = :visibility,
|
||||
supports_registration = 0, supports_capacity = 0, supports_pricing = 0, is_recurring = 0, status = "published", updated_at = NOW()
|
||||
WHERE id = :id AND created_by = :uid'
|
||||
);
|
||||
$listingStmt->execute([
|
||||
'listingType' => $entryType,
|
||||
'title' => $title,
|
||||
'teaser' => $teaser,
|
||||
'description' => $description,
|
||||
'visibility' => $visibility,
|
||||
'id' => $listingId,
|
||||
'uid' => $userId,
|
||||
]);
|
||||
|
||||
$this->pdo->prepare('DELETE FROM listing_occurrences WHERE listing_id = :listingId')->execute(['listingId' => $listingId]);
|
||||
if ($entryType === 'editorial_event') {
|
||||
$occStmt = $this->pdo->prepare(
|
||||
'INSERT INTO listing_occurrences (listing_id, occurrence_type, starts_at, status, created_at, updated_at)
|
||||
VALUES (:listingId, :occurrenceType, :startsAt, "scheduled", NOW(), NOW())'
|
||||
);
|
||||
$occStmt->execute([
|
||||
'listingId' => $listingId,
|
||||
'occurrenceType' => 'single',
|
||||
'startsAt' => $startsAt !== '' ? $startsAt : null,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$placeStmt = $this->pdo->prepare(
|
||||
'INSERT INTO listing_places (created_by, source_type, title, description, street, zip, city, region, lat, lng, place_kind, provider_hint, status, created_at, updated_at)
|
||||
VALUES (:uid, "user", :title, :description, :street, :zip, :city, :region, :lat, :lng, :placeKind, "manual", "published", NOW(), NOW())'
|
||||
);
|
||||
$placeStmt->execute([
|
||||
'uid' => $userId,
|
||||
'title' => $title,
|
||||
'description' => $description,
|
||||
'street' => $street !== '' ? $street : null,
|
||||
'zip' => $zip !== '' ? $zip : null,
|
||||
'city' => $city !== '' ? $city : null,
|
||||
'region' => $region !== '' ? $region : null,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'placeKind' => $placeKind !== '' ? $placeKind : null,
|
||||
]);
|
||||
$placeId = (int)$this->pdo->lastInsertId();
|
||||
|
||||
$listingStmt = $this->pdo->prepare(
|
||||
'INSERT INTO listings (created_by, owner_type, listing_type, primary_place_id, title, teaser_public, description, visibility, status, supports_registration, supports_capacity, supports_pricing, is_recurring, created_at, updated_at)
|
||||
VALUES (:uid, "user", :listingType, :placeId, :title, :teaser, :description, :visibility, "published", 0, 0, 0, 0, NOW(), NOW())'
|
||||
);
|
||||
$listingStmt->execute([
|
||||
'uid' => $userId,
|
||||
'listingType' => $entryType,
|
||||
'placeId' => $placeId,
|
||||
'title' => $title,
|
||||
'teaser' => $teaser,
|
||||
'description' => $description,
|
||||
'visibility' => $visibility,
|
||||
]);
|
||||
$listingId = (int)$this->pdo->lastInsertId();
|
||||
|
||||
if ($entryType === 'editorial_event') {
|
||||
$occStmt = $this->pdo->prepare(
|
||||
'INSERT INTO listing_occurrences (listing_id, occurrence_type, starts_at, status, created_at, updated_at)
|
||||
VALUES (:listingId, :occurrenceType, :startsAt, "scheduled", NOW(), NOW())'
|
||||
);
|
||||
$occStmt->execute([
|
||||
'listingId' => $listingId,
|
||||
'occurrenceType' => 'single',
|
||||
'startsAt' => $startsAt !== '' ? $startsAt : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo->prepare('DELETE FROM listing_category_map WHERE listing_id = :listingId')->execute(['listingId' => $listingId]);
|
||||
if ($categoryId !== null) {
|
||||
$mapStmt = $this->pdo->prepare('INSERT INTO listing_category_map (listing_id, category_id) VALUES (:listingId, :categoryId)');
|
||||
$mapStmt->execute([
|
||||
'listingId' => $listingId,
|
||||
'categoryId' => $categoryId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
return (int)$listingId;
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteDashboardEntry(int $userId, int $listingId): void
|
||||
{
|
||||
$this->ensureSchema();
|
||||
$entry = $this->getDashboardEntry($userId, $listingId);
|
||||
if (!$entry) {
|
||||
throw new \RuntimeException('Eintrag nicht gefunden.');
|
||||
}
|
||||
|
||||
$this->pdo->beginTransaction();
|
||||
try {
|
||||
$this->pdo->prepare('DELETE FROM listings WHERE id = :id AND created_by = :uid')->execute([
|
||||
'id' => $listingId,
|
||||
'uid' => $userId,
|
||||
]);
|
||||
if (!empty($entry['primary_place_id'])) {
|
||||
$this->pdo->prepare('DELETE FROM listing_places WHERE id = :id')->execute([
|
||||
'id' => (int)$entry['primary_place_id'],
|
||||
]);
|
||||
}
|
||||
$this->pdo->commit();
|
||||
} catch (\Throwable $e) {
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->rollBack();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function hasTable(string $table): bool
|
||||
{
|
||||
if (array_key_exists($table, $this->tableCache)) {
|
||||
|
||||
Reference in New Issue
Block a user