SEO: event meta title with date, Open Graph defaults, cache invalidation
- event detail pages: <title> is "Title – 03.12.2026" (multi-day ranges supported) so recurring events with the same name stay distinguishable; H1 and lists keep the editor's title - meta description fallback for events without teaser/description (date, time, location); a teaser that is only an insert tag (shared series text) also falls back, so a series does not share one description - EventOpenGraphProvider for numero2/contao-opengraph3 (registered only when the bundle is installed, priority above its CalendarEventsProvider): og:title with date, og:description from teaser, event image as og:image - FrontendCacheInvalidator: frontend event/organization edits write via DBAL, so invalidate the same cache tags the backend uses; tag the map and organization listing, invalidate the generic organization tag on backend saves Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bdc8988e28
commit
5bf31e5043
12 files changed
+384
No files matched your search
@@ -29,5 +29,8 @@
|
||||
"contao-components/installer": true,
|
||||
"contao/manager-plugin": true
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"numero2/contao-opengraph3": "Open-Graph-Standardwerte (Titel mit Datum, Teaser, Eventbild) für Event-Detailseiten"
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ services:
|
||||
- ../src/DependencyInjection/
|
||||
- ../src/Contao/Manager/
|
||||
- ../src/EventManagerBundle.php
|
||||
- ../src/OpenGraph/ # nur mit numero2/contao-opengraph3, siehe EventManagerExtension
|
||||
- ../src/Seo/ # statische Helfer, kein Service
|
||||
|
||||
Mummert\EventManagerBundle\EventListener\DataContainer\SetDateAddedCallback:
|
||||
tags:
|
||||
|
||||
@@ -305,3 +305,11 @@ $GLOBALS['TL_DCA']['tl_organization']['fields']['alias']['save_callback'][] = st
|
||||
|
||||
return $value;
|
||||
};
|
||||
|
||||
// Organisationsliste und Karte sind mit dem allgemeinen Tabellen-Tag getaggt
|
||||
// (FrontendCacheInvalidator): beim Speichern im Backend mit invalidieren.
|
||||
$GLOBALS['TL_DCA']['tl_organization']['config']['oninvalidate_cache_tags_callback'][] = static function ($dc, array $tags): array {
|
||||
$tags[] = \Mummert\EventManagerBundle\Service\FrontendCacheInvalidator::ORGANIZATION_TAG;
|
||||
|
||||
return $tags;
|
||||
};
|
||||
@@ -29,11 +29,15 @@ class EventMapController extends AbstractFrontendModuleController
|
||||
|
||||
public function __construct(
|
||||
private readonly MapModuleDataProvider $mapModuleDataProvider,
|
||||
private readonly \Mummert\EventManagerBundle\Service\FrontendCacheInvalidator $cacheInvalidator,
|
||||
) {
|
||||
}
|
||||
|
||||
protected function getResponse(FragmentTemplate $template, ModuleModel $model, Request $request): Response
|
||||
{
|
||||
// Karte zeigt Events aller Kalender und Organisationen: bei Änderungen neu erzeugen
|
||||
$this->tagResponse($this->cacheInvalidator->getMapTags());
|
||||
|
||||
$containerId = sprintf('eventmanager-map-%d', (int) ($model->id ?? 0));
|
||||
$dataElementId = sprintf('%s-data', $containerId);
|
||||
$filterWrapperId = sprintf('%s-filter', $containerId);
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace Mummert\EventManagerBundle\DependencyInjection;
|
||||
|
||||
use Mummert\EventManagerBundle\OpenGraph\EventOpenGraphProvider;
|
||||
use numero2\Opengraph3Bundle\OpenGraph\Provider\ProviderInterface;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
@@ -15,5 +17,14 @@ class EventManagerExtension extends Extension
|
||||
{
|
||||
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
|
||||
$loader->load('services.yaml');
|
||||
|
||||
// Open-Graph-Standardwerte für Events, nur wenn numero2/contao-opengraph3 installiert ist.
|
||||
// Höhere Priorität als dessen eigener CalendarEventsProvider (der erste passende Provider gewinnt).
|
||||
if (interface_exists(ProviderInterface::class)) {
|
||||
$container->register(EventOpenGraphProvider::class, EventOpenGraphProvider::class)
|
||||
->setAutowired(true)
|
||||
->setAutoconfigured(false)
|
||||
->addTag('numero2_opengraph3.provider', ['priority' => 10]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,13 @@ declare(strict_types=1);
|
||||
namespace Mummert\EventManagerBundle\EventListener;
|
||||
|
||||
use Contao\CoreBundle\DependencyInjection\Attribute\AsHook;
|
||||
use Contao\CoreBundle\Routing\ResponseContext\HtmlHeadBag\HtmlHeadBag;
|
||||
use Contao\CoreBundle\Routing\ResponseContext\ResponseContextAccessor;
|
||||
use Contao\StringUtil;
|
||||
use Contao\Template;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Mummert\EventManagerBundle\Seo\EventMetaTitle;
|
||||
|
||||
#[AsHook('parseTemplate', method: 'onParseTemplate')]
|
||||
class EventFullTemplateDataListener
|
||||
@@ -17,6 +20,7 @@ class EventFullTemplateDataListener
|
||||
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
private readonly ResponseContextAccessor $responseContextAccessor,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -41,6 +45,9 @@ class EventFullTemplateDataListener
|
||||
$organizations = $this->fetchOrganizations($eventId);
|
||||
$tags = $this->fetchTags($eventId);
|
||||
|
||||
$this->setMetaTitle($data);
|
||||
$this->setMetaDescriptionFallback($data, $locationRow);
|
||||
|
||||
$template->location_row = $locationRow;
|
||||
$template->organizations = $organizations;
|
||||
$template->tags = $tags;
|
||||
@@ -62,6 +69,75 @@ class EventFullTemplateDataListener
|
||||
$template->tag_fields = $this->toFlatTagFields($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Meta-Titel mit Datum ("Titel – 03.12.2026"), damit wiederkehrende Events
|
||||
* gleichen Namens in den Suchergebnissen unterscheidbar sind. H1 und Listen
|
||||
* zeigen weiterhin nur den Redaktions-Titel.
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*/
|
||||
private function setMetaTitle(array $data): void
|
||||
{
|
||||
$headBag = $this->responseContextAccessor->getResponseContext()?->get(HtmlHeadBag::class);
|
||||
|
||||
if (!$headBag instanceof HtmlHeadBag) {
|
||||
return;
|
||||
}
|
||||
|
||||
$title = EventMetaTitle::build($data);
|
||||
|
||||
if ('' !== $title) {
|
||||
$headBag->setTitle($title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersatz-Description für Events ohne Teaser/Beschreibung (Redakteure pflegen
|
||||
* keine Meta-Description): "Titel am 03.12.2026 um 16:30 Uhr in Ort, Stadt."
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
* @param array<string, string> $locationRow
|
||||
*/
|
||||
private function setMetaDescriptionFallback(array $data, array $locationRow): void
|
||||
{
|
||||
$headBag = $this->responseContextAccessor->getResponseContext()?->get(HtmlHeadBag::class);
|
||||
|
||||
if (!$headBag instanceof HtmlHeadBag) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Vorhandene Description behalten, außer sie ist leer oder stammt aus einem Teaser,
|
||||
// der nur aus einem Insert-Tag besteht (z. B. {{insert_content::ID}} als gemeinsamer
|
||||
// Serien-Teaser: sonst hätten alle Events der Serie dieselbe Description).
|
||||
$hasOwnDescription = '' !== trim(strip_tags((string) ($data['description'] ?? '')));
|
||||
$teaserIsInsertTagOnly = (bool) preg_match('/^\{\{[^{}]+\}\}$/', trim((string) ($data['teaser'] ?? '')));
|
||||
|
||||
if ($hasOwnDescription || ('' !== trim($headBag->getMetaDescription()) && !$teaserIsInsertTagOnly)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$title = html_entity_decode(strip_tags((string) ($data['title'] ?? '')), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$date = EventMetaTitle::formatDate((int) ($data['startDate'] ?? 0), (int) ($data['endDate'] ?? 0));
|
||||
|
||||
if ('' === $date) {
|
||||
return;
|
||||
}
|
||||
|
||||
$description = trim($title).(str_contains($date, '–') ? ' vom '.str_replace('–', ' bis ', $date) : ' am '.$date);
|
||||
|
||||
if (!empty($data['addTime']) && !empty($data['startTime'])) {
|
||||
$description .= ' um '.date('H:i', (int) $data['startTime']).' Uhr';
|
||||
}
|
||||
|
||||
$place = implode(', ', array_filter([trim((string) ($locationRow['title'] ?? '')), trim((string) ($locationRow['city'] ?? ''))]));
|
||||
|
||||
if ('' !== $place) {
|
||||
$description .= ' in '.$place;
|
||||
}
|
||||
|
||||
$headBag->setMetaDescription($description.'. Termin und Infos im Veranstaltungskalender Nossener Land.');
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
private function fetchLocationRow(int $locationId): array
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ class OrganizationListingTemplateDataListener
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
private readonly ?LoggerInterface $logger = null,
|
||||
private readonly \Mummert\EventManagerBundle\Service\FrontendCacheInvalidator $cacheInvalidator,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -27,6 +28,9 @@ class OrganizationListingTemplateDataListener
|
||||
return;
|
||||
}
|
||||
|
||||
// Seite bei Änderungen an Organisationen automatisch aus dem Cache werfen
|
||||
$this->cacheInvalidator->tagOrganizationListing();
|
||||
|
||||
$tbody = $template->tbody;
|
||||
|
||||
if (!\is_array($tbody) || [] === $tbody) {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\EventManagerBundle\OpenGraph;
|
||||
|
||||
use Contao\CalendarEventsModel;
|
||||
use Contao\CoreBundle\InsertTag\InsertTagParser;
|
||||
use Contao\Input;
|
||||
use Contao\ModuleModel;
|
||||
use Contao\StringUtil;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Mummert\EventManagerBundle\Seo\EventMetaTitle;
|
||||
use numero2\Opengraph3Bundle\OpenGraph\OpenGraphReference;
|
||||
use numero2\Opengraph3Bundle\OpenGraph\Provider\ProviderInterface;
|
||||
|
||||
/**
|
||||
* Open-Graph-Standardwerte für Event-Detailseiten (numero2/contao-opengraph3).
|
||||
*
|
||||
* Redakteure pflegen keine OG-Felder. Ohne diesen Provider würden alle Events
|
||||
* Titel und Bild der Website-Root teilen. Werte, die im Event selbst gepflegt
|
||||
* sind (Reiter "OpenGraph"), haben weiterhin Vorrang.
|
||||
*
|
||||
* Wird nur registriert, wenn numero2/contao-opengraph3 installiert ist
|
||||
* (siehe EventManagerExtension).
|
||||
*/
|
||||
class EventOpenGraphProvider implements ProviderInterface
|
||||
{
|
||||
private const DESCRIPTION_LENGTH = 200;
|
||||
|
||||
public function __construct(
|
||||
private readonly InsertTagParser $insertTagParser,
|
||||
private readonly Connection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(ModuleModel $model): bool
|
||||
{
|
||||
return 'eventreader' === $model->type;
|
||||
}
|
||||
|
||||
public function getReference(ModuleModel $model): ?OpenGraphReference
|
||||
{
|
||||
$event = CalendarEventsModel::findPublishedByParentAndIdOrAlias(
|
||||
(string) Input::get('auto_item'),
|
||||
StringUtil::deserialize($model->cal_calendar, true),
|
||||
);
|
||||
|
||||
if (null === $event) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = $event->row();
|
||||
$defaults = [
|
||||
'og_type' => 'website',
|
||||
'og_title' => EventMetaTitle::build($row),
|
||||
'og_description' => $this->buildDescription($row),
|
||||
];
|
||||
|
||||
if ($event->addImage && $event->singleSRC) {
|
||||
$defaults['og_image'] = $event->singleSRC;
|
||||
}
|
||||
|
||||
return new OpenGraphReference($event, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
*/
|
||||
private function buildDescription(array $row): string
|
||||
{
|
||||
foreach (['teaser', 'description'] as $field) {
|
||||
$text = $this->toPlainText((string) ($row[$field] ?? ''));
|
||||
|
||||
if ('' !== $text) {
|
||||
return $this->truncate($text);
|
||||
}
|
||||
}
|
||||
|
||||
// Kein Text gepflegt: Datum, Uhrzeit und Ort als Beschreibung
|
||||
$parts = [EventMetaTitle::formatDate((int) ($row['startDate'] ?? 0), (int) ($row['endDate'] ?? 0))];
|
||||
|
||||
if (!empty($row['addTime']) && !empty($row['startTime'])) {
|
||||
$parts[0] .= ', '.date('H:i', (int) $row['startTime']).' Uhr';
|
||||
}
|
||||
|
||||
$locationId = (int) ($row['location_id'] ?? 0);
|
||||
|
||||
if ($locationId > 0) {
|
||||
$location = $this->connection->fetchAssociative('SELECT title, city FROM tl_location WHERE id = ?', [$locationId]);
|
||||
|
||||
if ($location) {
|
||||
$parts[] = trim(implode(', ', array_filter([(string) $location['title'], (string) $location['city']])));
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' – ', array_filter($parts));
|
||||
}
|
||||
|
||||
private function toPlainText(string $value): string
|
||||
{
|
||||
// Leer oder nur ein Insert-Tag (gemeinsamer Serien-Teaser): lieber Datum und Ort
|
||||
if ('' === trim($value) || preg_match('/^\{\{[^{}]+\}\}$/', trim($value))) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = $this->insertTagParser->replaceInline($value);
|
||||
$value = preg_replace('/<(br|\/p|\/li|\/h[1-6])\b[^>]*>/i', ' ', $value) ?? $value;
|
||||
$value = html_entity_decode(strip_tags($value), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
return trim((string) preg_replace('/\s+/u', ' ', $value));
|
||||
}
|
||||
|
||||
private function truncate(string $text): string
|
||||
{
|
||||
if (mb_strlen($text) <= self::DESCRIPTION_LENGTH) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$cut = mb_substr($text, 0, self::DESCRIPTION_LENGTH);
|
||||
$space = mb_strrpos($cut, ' ');
|
||||
|
||||
return rtrim(false !== $space && $space > 120 ? mb_substr($cut, 0, $space) : $cut, " ,.;:–-").' …';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\EventManagerBundle\Seo;
|
||||
|
||||
/**
|
||||
* Meta-Titel für Event-Detailseiten: Redaktions-Titel plus Datum, z. B.
|
||||
* "Sterne falten – 03.12.2026" oder "Reiterferien – 03.08.–07.08.2027".
|
||||
*
|
||||
* Das Datum erscheint nur im <title> und in og:title, nicht in der H1 und nicht
|
||||
* in den Listen. So bleiben wiederkehrende Veranstaltungen gleichen Namens
|
||||
* für Suchmaschinen unterscheidbar.
|
||||
*/
|
||||
final class EventMetaTitle
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $event Event-Datensatz (title, pageTitle, startDate, endDate)
|
||||
*/
|
||||
public static function build(array $event): string
|
||||
{
|
||||
$base = self::clean((string) ($event['pageTitle'] ?? ''));
|
||||
|
||||
if ('' === $base) {
|
||||
$base = self::clean((string) ($event['title'] ?? ''));
|
||||
}
|
||||
|
||||
$date = self::formatDate((int) ($event['startDate'] ?? 0), (int) ($event['endDate'] ?? 0));
|
||||
|
||||
if ('' === $date) {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return '' === $base ? $date : $base.' – '.$date;
|
||||
}
|
||||
|
||||
public static function formatDate(int $start, int $end = 0): string
|
||||
{
|
||||
if ($start <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($end <= 0 || date('Ymd', $end) === date('Ymd', $start)) {
|
||||
return date('d.m.Y', $start);
|
||||
}
|
||||
|
||||
if (date('Y', $end) === date('Y', $start)) {
|
||||
return date('d.m.', $start).'–'.date('d.m.Y', $end);
|
||||
}
|
||||
|
||||
return date('d.m.Y', $start).'–'.date('d.m.Y', $end);
|
||||
}
|
||||
|
||||
private static function clean(string $value): string
|
||||
{
|
||||
$value = html_entity_decode(strip_tags($value), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
return trim((string) preg_replace('/\s+/u', ' ', $value));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ class EventRepository
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
private readonly FrontendCacheInvalidator $cacheInvalidator,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -71,6 +72,8 @@ class EventRepository
|
||||
['id' => $eventId],
|
||||
['id' => ParameterType::INTEGER],
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId);
|
||||
}
|
||||
|
||||
public function duplicate(int $eventId): ?int
|
||||
@@ -138,6 +141,8 @@ class EventRepository
|
||||
$this->assignTagsToEvent($newEventId, $tagIds);
|
||||
}
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($newEventId);
|
||||
|
||||
return $newEventId;
|
||||
}
|
||||
|
||||
@@ -264,6 +269,8 @@ class EventRepository
|
||||
|
||||
public function delete(int $eventId): void
|
||||
{
|
||||
$calendarId = (int) $this->connection->fetchOne('SELECT pid FROM tl_calendar_events WHERE id = ?', [$eventId]);
|
||||
|
||||
$this->connection->delete(
|
||||
'tl_tags_rel',
|
||||
['ptable' => 'tl_calendar_events', 'field' => 'tags', 'pid' => $eventId],
|
||||
@@ -281,6 +288,8 @@ class EventRepository
|
||||
['id' => $eventId],
|
||||
['id' => ParameterType::INTEGER],
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId, $calendarId);
|
||||
}
|
||||
|
||||
/** @return array<string, mixed>|null */
|
||||
@@ -508,6 +517,8 @@ class EventRepository
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -525,6 +536,8 @@ class EventRepository
|
||||
'id' => ParameterType::INTEGER,
|
||||
],
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -573,6 +586,8 @@ class EventRepository
|
||||
|
||||
$this->assignEventToOrganizations($eventId, $allowedOrganizationIds);
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId);
|
||||
|
||||
return $eventId;
|
||||
}
|
||||
|
||||
@@ -730,6 +745,8 @@ class EventRepository
|
||||
['id' => $eventId],
|
||||
$types,
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateEvent($eventId);
|
||||
}
|
||||
|
||||
/** @return array<int> */
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mummert\EventManagerBundle\Service;
|
||||
|
||||
use Contao\CoreBundle\Cache\CacheTagManager;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
/**
|
||||
* Leert den Contao-Seitencache nach Änderungen aus dem Frontend (Veranstalterbereich).
|
||||
*
|
||||
* Die Repositories schreiben direkt per DBAL. Anders als beim Speichern im Backend
|
||||
* (DC_Table) bekommt Contao davon nichts mit, ohne Invalidierung wären Änderungen
|
||||
* erst nach Ablauf der Cache-Zeit sichtbar.
|
||||
*
|
||||
* Tags wie im Backend: Event selbst + Kalender-Archiv (Listen, Kalender, Karte).
|
||||
*/
|
||||
class FrontendCacheInvalidator
|
||||
{
|
||||
public const ORGANIZATION_TAG = 'contao.db.tl_organization';
|
||||
|
||||
public function __construct(
|
||||
private readonly CacheTagManager $cacheTagManager,
|
||||
private readonly Connection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function invalidateEvent(int $eventId, ?int $calendarId = null): void
|
||||
{
|
||||
$tags = ['contao.db.tl_calendar_events.'.$eventId];
|
||||
|
||||
$calendarId ??= (int) $this->connection->fetchOne('SELECT pid FROM tl_calendar_events WHERE id = ?', [$eventId]);
|
||||
|
||||
if ($calendarId > 0) {
|
||||
$tags[] = 'contao.db.tl_calendar.'.$calendarId;
|
||||
}
|
||||
|
||||
$this->cacheTagManager->invalidateTags($tags);
|
||||
}
|
||||
|
||||
public function invalidateOrganization(int $organizationId): void
|
||||
{
|
||||
$this->cacheTagManager->invalidateTags([self::ORGANIZATION_TAG, self::ORGANIZATION_TAG.'.'.$organizationId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tags für Seiten, die Events aller Kalender und Organisationen zeigen (Karte).
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getMapTags(): array
|
||||
{
|
||||
$tags = [self::ORGANIZATION_TAG];
|
||||
|
||||
foreach ($this->connection->fetchFirstColumn('SELECT id FROM tl_calendar') as $calendarId) {
|
||||
$tags[] = 'contao.db.tl_calendar.'.$calendarId;
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function tagOrganizationListing(): void
|
||||
{
|
||||
$this->cacheTagManager->tagWith([self::ORGANIZATION_TAG]);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ class OrganizationRepository
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
private readonly FrontendCacheInvalidator $cacheInvalidator,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -85,6 +86,8 @@ class OrganizationRepository
|
||||
'id' => ParameterType::INTEGER,
|
||||
],
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateOrganization($organizationId);
|
||||
}
|
||||
|
||||
public function updateLogo(int $organizationId, ?string $logoUuid): void
|
||||
@@ -106,6 +109,8 @@ class OrganizationRepository
|
||||
['id' => $organizationId],
|
||||
$types,
|
||||
);
|
||||
|
||||
$this->cacheInvalidator->invalidateOrganization($organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,6 +213,8 @@ class OrganizationRepository
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
$this->cacheInvalidator->invalidateOrganization($organizationId);
|
||||
}
|
||||
|
||||
/** @return array<int> */
|
||||
|
||||
Reference in new issue
Block a user