<?php
namespace App\Entity\Vs;
use App\Util\UrlSlug;
use Doctrine\ORM\Mapping as ORM;
use IntlDateFormatter as SystemIntlDateFormatter;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity]
#[ORM\Table(name: 'vs')]
class Vs
{
public const int MAX_WIDTH_VSIMAGE = 1024;
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;
#[ORM\Column(name: 'uuid', type: 'string', length: 36)]
private string $uuid;
#[ORM\Column(name: 'titel', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\NotBlank]
private ?string $titel = null;
#[ORM\Column(name: 'adresse', type: 'string', length: 200, nullable: true)]
private ?string $adresse = null;
#[ORM\ManyToOne(targetEntity: VsRegion::class, inversedBy: 'vss')]
private ?VsRegion $region = null;
#[ORM\ManyToOne(targetEntity: VsRubrik::class, inversedBy: 'vss')]
#[Assert\NotBlank]
private ?VsRubrik $rubrik = null;
#[ORM\ManyToOne(targetEntity: VsStatus::class, inversedBy: 'vss')]
private ?VsStatus $status = null;
#[ORM\ManyToOne(targetEntity: VsAbo::class, inversedBy: 'vss')]
#[Assert\NotBlank]
private ?VsAbo $abo = null;
/**
* HINWEIS:
* Assert/Validierung erfolgt unten über Validation Callback
*/
#[ORM\ManyToOne(targetEntity: VsTyp::class, inversedBy: 'vss')]
private ?VsTyp $typ = null;
#[ORM\Column(name: 'beschreibung', type: 'text', length: 300, unique: false, nullable: true)]
private ?string $beschreibung = null;
#[ORM\Column(name: 'beginnzeit', type: 'time', nullable: true)]
#[Assert\NotBlank]
private ?\DateTimeInterface $beginnzeit = null;
#[ORM\ManyToOne(targetEntity: VsBeginnAuswahl::class, inversedBy: 'vss')]
private ?VsBeginnAuswahl $beginn_auswahl = null;
#[ORM\Column(name: 'endzeit', type: 'time', nullable: true)]
#[Assert\NotBlank]
private ?\DateTimeInterface $endzeit = null;
#[ORM\Column(name: 'startdatum', type: 'date', length: 20, unique: false, nullable: true)]
#[Assert\NotBlank]
private ?\DateTimeInterface $startdatum = null;
#[ORM\Column(name: 'enddatum', type: 'date', length: 20, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['dauerveranstaltung', 'ohnefixdatum'])]
#[Assert\GreaterThan(propertyPath: 'startdatum', message: "Datum ungültig, dies muss ein Datum nach dem 'von Datum' sein!", groups: ['dauerveranstaltung', 'ohnefixdatum'])]
private ?\DateTimeInterface $enddatum = null;
#[ORM\Column(name: 'url', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\Url(protocols: ['http', 'https'])]
private ?string $url = null;
#[ORM\Column(name: 'videolink', type: 'string', length: 250, unique: false, nullable: true)]
#[Assert\Regex(
pattern: '#^(https://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]+|https://vimeo\.com/\d+)$#',
message: 'Ungültige URL. Erlaubte URL Format Beispiel: YouTube-Format: https://www.youtube.com/watch?v=ZtqOylLqyCU | Vimeo Format: https://vimeo.com/253989945'
)]
private ?string $videolink = null;
#[ORM\Column(name: 'googlemaps', type: 'string', length: 600, unique: false, nullable: true)]
#[Assert\Regex(
pattern:"#^<iframe src=\"https://www.google.com/maps/embed.*></iframe>$#",
message:"Der 'Karten einbett-Code' ist ungültig"
)]
private ?string $googlemaps = null;
#[ORM\Column(name: 'ticketsbestellen', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\Url(protocols: ['http', 'https'])]
private ?string $ticketsbestellen = null;
#[ORM\Column(name: 'export_uristier', type: 'boolean', length: 4, unique: false, nullable: true)]
private bool $export_uristier;
#[ORM\Column(name: 'export_uw', type: 'boolean', length: 4, unique: false, nullable: true)]
private bool $export_uw;
#[ORM\Column(name: 'export_1_enddatum', type: 'datetime', length: 8, unique: false, nullable: true)]
private ?\DateTimeInterface $export_1_enddatum = null;
#[ORM\Column(name: 'kveranstalter', type: 'string', length: 100, unique: false, nullable: true)]
private ?string $kveranstalter = null;
#[ORM\Column(name: 'kname', type: 'string', length: 100, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $kname = null;
#[ORM\Column(name: 'kvorname', type: 'string', length: 100, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $kvorname = null;
#[ORM\Column(name: 'kstrasse', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $kstrasse = null;
#[ORM\Column(name: 'kplz', type: 'string', length: 5, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $kplz = null;
#[ORM\Column(name: 'kort', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $kort = null;
#[ORM\Column(name: 'ktel', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
private ?string $ktel = null;
#[ORM\Column(name: 'kemail', type: 'string', length: 200, unique: false, nullable: true)]
#[Assert\NotBlank(groups: ['kundendaten'])]
#[Assert\Email(message: "Diese E-mail '{{ value }}' ist nicht gültig.", groups: ['kundendaten'])]
private ?string $kemail = null;
#[ORM\Column(name: 'kkontaktieren', type: 'boolean', length: 4, unique: false, nullable: true)]
private bool $kkontaktieren = false;
#[ORM\Column(name: 'kbemerkung', type: 'text', length: 200, unique: false, nullable: true)]
private ?string $kbemerkung = null;
#[ORM\Column(name: 'lastupdate', type: 'datetime', length: 10, unique: false, nullable: true)]
private \DateTimeInterface $lastupdate;
#[ORM\Column(name: 'vs_image', type: 'json', nullable: true)]
private ?array $vsImage = [];
public function __construct()
{
$this->uuid = self::generateNewUuid();
$this->setLastupdate();
}
public static function generateNewUuid(): string
{
return Uuid::uuid4()->toString();
}
public function getId(): ?int
{
return $this->id;
}
public function getUuid(): string
{
return $this->uuid;
}
public function setUuid($uuid): void
{
$this->uuid = $uuid;
}
public function getTitel(): ?string
{
return $this->titel;
}
public function setTitel(?string $titel): static
{
$this->titel = $titel;
return $this;
}
public function getSlug(): array|string|null
{
return UrlSlug::fromString($this->titel);
}
public function getAdresse(): ?string
{
return $this->adresse;
}
public function setAdresse(?string $adresse): static
{
$this->adresse = $adresse;
return $this;
}
public function getBeschreibung(): ?string
{
return $this->beschreibung;
}
public function setBeschreibung(?string $beschreibung): static
{
$this->beschreibung = $beschreibung;
return $this;
}
public function getBeginnzeit(): ?\DateTimeInterface
{
return $this->beginnzeit;
}
public function setBeginnzeit(?\DateTimeInterface$beginnzeit): static
{
$this->beginnzeit = $beginnzeit;
return $this;
}
public function getEndzeit(): ?\DateTimeInterface
{
return $this->endzeit;
}
public function setEndzeit(?\DateTimeInterface $endzeit): static
{
$this->endzeit = $endzeit;
return $this;
}
public function getStartdatum(): ?\DateTimeInterface
{
return $this->startdatum;
}
public function setStartdatum(?\DateTimeInterface $startdatum): static
{
$this->startdatum = $startdatum;
return $this;
}
public function getEnddatum(): ?\DateTimeInterface
{
return $this->enddatum;
}
public function setEnddatum(?\DateTimeInterface $enddatum): static
{
$this->enddatum = $enddatum;
return $this;
}
public function getUrl(bool $useLinktextFormat = false): ?string
{
$url = $this->url;
if ($useLinktextFormat) {
$url = str_replace(['https://', 'http://'], '', $url);
}
return $url;
}
public function setUrl(?string $url): static
{
$this->url = $url;
return $this;
}
public function getVideolink(): ?string
{
return $this->videolink;
}
public function setVideolink(?string $videolink): static
{
$this->videolink = $videolink;
return $this;
}
public function getGooglemaps(): ?string
{
return $this->googlemaps;
}
public function setGooglemaps(?string $googlemaps): void
{
$this->googlemaps = $googlemaps;
}
public function getTicketsbestellen(): ?string
{
return $this->ticketsbestellen;
}
public function setTicketsbestellen(?string $ticketsbestellen): static
{
$this->ticketsbestellen = $ticketsbestellen;
return $this;
}
public function getExportUristier(): bool
{
return $this->export_uristier;
}
public function setExportUristier(bool $exportUristier): static
{
$this->export_uristier = $exportUristier;
return $this;
}
public function getExportUw(): bool
{
return $this->export_uw;
}
public function setExportUw(bool $export_uw): static
{
$this->export_uw = $export_uw;
return $this;
}
public function getRegion(): ?VsRegion
{
return $this->region;
}
public function setRegion(?VsRegion $region): static
{
$this->region = $region;
return $this;
}
public function getRubrik(): ?VsRubrik
{
return $this->rubrik;
}
public function setRubrik(?VsRubrik $rubrik): static
{
$this->rubrik = $rubrik;
return $this;
}
public function getStatus(): ?VsStatus
{
return $this->status;
}
public function setStatus(?VsStatus $status): static
{
$this->status = $status;
return $this;
}
public function getAbo(): ?VsAbo
{
return $this->abo;
}
public function setAbo(?VsAbo $abo): static
{
$this->abo = $abo;
return $this;
}
public function getTyp(): ?VsTyp
{
return $this->typ;
}
public function setTyp(?VsTyp $typ): static
{
$this->typ = $typ;
return $this;
}
public function getBeginnAuswahl(): ?VsBeginnAuswahl
{
return $this->beginn_auswahl;
}
public function setBeginnAuswahl(?VsBeginnAuswahl $beginn_auswahl): static
{
$this->beginn_auswahl = $beginn_auswahl;
return $this;
}
public function getExport1Enddatum(): ?\DateTimeInterface
{
return $this->export_1_enddatum;
}
public function setExport1Enddatum(?\DateTimeInterface $export_1_enddatum): static
{
$this->export_1_enddatum = $export_1_enddatum;
return $this;
}
public function getKveranstalter(): ?string
{
return $this->kveranstalter;
}
public function setKveranstalter(?string $kveranstalter): static
{
$this->kveranstalter = $kveranstalter;
return $this;
}
public function getKname(): ?string
{
return $this->kname;
}
public function setKname(?string $kname): static
{
$this->kname = $kname;
return $this;
}
public function getKvorname(): ?string
{
return $this->kvorname;
}
public function setKvorname(?string $kvorname): static
{
$this->kvorname = $kvorname;
return $this;
}
public function getKstrasse(): ?string
{
return $this->kstrasse;
}
public function setKstrasse(?string $kstrasse): static
{
$this->kstrasse = $kstrasse;
return $this;
}
public function getKplz(): ?string
{
return $this->kplz;
}
public function setKplz(?string $kplz): static
{
$this->kplz = $kplz;
return $this;
}
public function getKort(): ?string
{
return $this->kort;
}
public function setKort(?string $kort): static
{
$this->kort = $kort;
return $this;
}
public function getKtel(): ?string
{
return $this->ktel;
}
public function setKtel(?string $ktel): static
{
$this->ktel = $ktel;
return $this;
}
public function getKemail(): ?string
{
return $this->kemail;
}
public function setKemail(?string $kemail): static
{
$this->kemail = $kemail;
return $this;
}
public function isKkontaktieren(): bool
{
return $this->kkontaktieren;
}
public function setKkontaktieren(bool $kkontaktieren): static
{
$this->kkontaktieren = $kkontaktieren;
return $this;
}
public function getKbemerkung(): ?string
{
return $this->kbemerkung;
}
public function setKbemerkung(?string $kbemerkung): static
{
$this->kbemerkung = $kbemerkung;
return $this;
}
public function getLastupdate(): \DateTimeInterface
{
return $this->lastupdate;
}
public function setVsImage(string $filename, ?string $alt = '', ?string $caption = ''): void
{
$vsImage = VsImage::createNonExistImage();
if ($filename) {
$vsImage = VsImage::create($filename, $alt, $caption);
}
$this->vsImage['filename'] = $vsImage->getFilename();
$this->vsImage['alt'] = $vsImage->getAlt();
$this->vsImage['caption'] = $vsImage->getCaption();
}
public function getVsImage(): VsImage
{
if ($this->vsImage && array_key_exists('filename', $this->vsImage)) {
$fileName = $this->vsImage['filename'];
$alt = array_key_exists('alt', $this->vsImage) ? $this->vsImage['alt'] : '';
$caption = array_key_exists('caption', $this->vsImage) ? $this->vsImage['caption'] : '';
return VsImage::create($fileName, $alt, $caption, $this->baseObjectServerDirPath(), $this->baseObjectWebDirPath());
}
return VsImage::createNonExistImage();
}
public static function baseObjectServerDirPathStatic($uuid): string
{
return __DIR__.'/../../../var/data/public/vs/images/'.substr((string) $uuid, 0, 2).'/'.$uuid;
}
public function baseObjectServerDirPath(): string
{
return self::baseObjectServerDirPathStatic($this->uuid);
}
public static function baseObjectWebDirPathStatic($uuid): string
{
return '/data/vs/images/'.substr((string) $uuid, 0, 2).'/'.$uuid;
}
public function baseObjectWebDirPath(): string
{
return self::baseObjectWebDirPathStatic($this->uuid);
}
//---- Helper Function
public function getVideolinkYouTubeId(): string
{
if (!$this->getVideolink() || !str_contains($this->getVideolink(), 'www.youtube.com')) {
return '';
}
// in der Annahme, das URL im folgenden Format abgelegt ist:
// https://www.youtube.com/watch?v=ZtqOylLqyCU
return explode('v=', $this->getVideolink())[1];
}
public function getVideolinkVimeoId(): string
{
if (!$this->getVideolink() || !str_contains($this->getVideolink(), 'vimeo.com')) {
return '';
}
return trim(parse_url($this->getVideolink(), PHP_URL_PATH), '/');
}
public function formatZeit(): string
{
// Zeit Formatierung
$zeit = '';
$beginnAuswahlPrefix = $this->getBeginnAuswahl() ? $this->getBeginnAuswahl().' ' : '';
$beginnzeit = $this->getBeginnzeit() ? $this->getBeginnzeit()->format('G.i') : '';
$endZeit = $this->getEndzeit() ? $this->getEndzeit()->format('G.i') : '';
if ($beginnzeit && $endZeit) {
$zeit = $beginnAuswahlPrefix.$beginnzeit.'–'.$endZeit.' Uhr';
} elseif ($beginnzeit) {
$zeit = $beginnAuswahlPrefix.$beginnzeit.' Uhr';
} elseif ($endZeit) {
$zeit = 'Bis '.lcfirst($beginnAuswahlPrefix).$endZeit.' Uhr';
}
return $zeit;
}
public function formatStartEndDatumUndZeit(): string
{
// Zeit Formatierung
$datumZeit = $this->formatZeit();
if (!$this->getTyp()) {
return $datumZeit;
}
if ($this->getTyp()->isEinzelveranstaltung()) {
if ($datumZeit) {
$datumZeit = ', '.lcfirst((string) $datumZeit);
}
$datumZeit = SystemIntlDateFormatter::formatObject($this->getStartdatum(), 'EEEE, d. MMMM').$datumZeit;
}
// wenn Dauerveranstaltung, dann anstatt Zeit, nur Von Bis Datum ausgeben
if ($this->getTyp()->isDauerveranstaltung()) {
$startDatFormat = 'd.';
// Falls Monat/Jahr Kombination unterschiedlich, Monat auch in Startdatum ausgeben
// Bsp: "Vom 1. bis 30. Juni", oder "Vom 1. Januar bis 31. Dezember,"
// Evtl. noch Jahr Ausgabe Optimierung nötig!
if ($this->getStartdatum()->format('mY') != $this->getEnddatum()->format('mY')) {
$startDatFormat = 'd. MMMM';
}
$datumZeit = 'Vom '.SystemIntlDateFormatter::formatObject($this->getStartdatum(), $startDatFormat).' bis '.SystemIntlDateFormatter::formatObject($this->getEnddatum(), 'd. MMMM');
} elseif ($this->getTyp()->isOhneFixdatum()) {
// Alles zurücksetzen, da hier weder Datum noch Uhrzeit genutzt werden
$datumZeit = '';
}
// Zeit
return $datumZeit;
}
public function formatStartEndDatum(): false|string
{
$datum = '';
if (!$this->getTyp()) {
return $datum;
}
if ($this->getTyp()->isEinzelveranstaltung()) {
$datum = SystemIntlDateFormatter::formatObject($this->getStartdatum(), 'EEEE, d. MMMM');
}
// wenn Dauerveranstaltung, dann anstatt Zeit, nur Vom-Bis Datum ausgeben
if ($this->getTyp()->isDauerveranstaltung()) {
$startDatFormat = 'd.';
// Falls Monat/Jahr Kombination unterschiedlich, Monat auch in Startdatum ausgeben
// Bsp: "Vom 1. bis 30. Juni, " oder "Vom 1. Januar bis 31. Dezember, "
// Evtl. noch Jahr Ausgabe Optimierung nötig!
if ($this->getStartdatum()->format('mY') != $this->getEnddatum()->format('mY')) {
$startDatFormat = 'd. MMMM';
}
$datum = 'Vom '.SystemIntlDateFormatter::formatObject($this->getStartdatum(), $startDatFormat).' bis '.SystemIntlDateFormatter::formatObject($this->getEnddatum(), 'd. MMMM');
}
// Zeit
return $datum;
}
#[ORM\PostRemove]
public function removeFiles(): void
{
$fs = new Filesystem();
$fs->remove($this->baseObjectServerDirPath());
}
#[ORM\PostPersist]
#[ORM\PostUpdate]
public function removeUnusedFiles(): void
{
$serverPath = $this->baseObjectServerDirPath();
// Auflistung aller effektiv benötigter Filenamen dieses Objects, damit diese erhalten bleiben
$usedFiles_ = [];
if ($this->getVsImage()->getFilename()) {
$usedFiles_[] = $this->getVsImage()->getFilename();
$usedFiles_[] = $this->getVsImage()->getFilenameThumb();
}
// Ende
// Lösche alle Files aus dem Objektverzeichnis, welche nicht im Objekt vorhanden sind
$fs = new Filesystem();
if ($fs->exists($serverPath)) {
$finder = new Finder();
$finder->files()->in($serverPath);
foreach ($finder as $file) {
if (!in_array($file->getFilename(), $usedFiles_)) {
$fs->remove($file->getRealPath());
}
}
}
// Ende
}
// Diese Funktionen wird vor dem definitiven Speichern aufgerufen
#[Assert\Callback]
public function validate(ExecutionContextInterface $context): void
{
// Feld Typ nur darf "null" sein, wenn Feld Status "nicht zugewiesen" besitzt
if (!$this->getTyp() && ($this->getStatus() && !$this->getStatus()->isNichtZugewiesen())) {
$context->buildViolation('Typ muss gesetzt sein, da Status ungleich "Nicht zugewiesen"!')
->atPath('typ')
->addViolation();
}
// Max Length Titel und Beschreibung checken
if ($this->getAbo()) {
$maxLengthTitel = $this->getAbo()->getMaxLengthVsTitel() ?: null;
$maxLengthBeschreibung = $this->getAbo()->getMaxLengthVsBeschreibung();
if ($maxLengthTitel !== null && mb_strlen($this->getTitel()) > $maxLengthTitel) {
$context->buildViolation('Zu viele Zeichen verwendet, max. Zeichenlänge = '.$maxLengthTitel)
->atPath('titel')
->addViolation();
}
if ($maxLengthBeschreibung !== null && mb_strlen($this->getBeschreibung()) > $maxLengthBeschreibung) {
$context->buildViolation('Zu viele Zeichen verwendet, max. Zeichenlänge = '.$maxLengthBeschreibung)
->atPath('beschreibung')
->addViolation();
}
}
}
#[ORM\PrePersist]
#[ORM\PreUpdate]
public function setLastupdate(): void
{
$this->lastupdate = new \DateTime();
}
#[ORM\PrePersist]
#[ORM\PreUpdate]
public function calculateExport1Enddatum(): void
{
if (!$this->getTyp()) {
return;
}
// Nur eine Dauerveranstaltung soll dieses Feld belegt haben
if (!$this->getTyp()->isDauerveranstaltung()) {
$this->setExport1Enddatum(null);
return;
}
if ($this->getEnddatum() && !$this->getExport1Enddatum()) {
$this->setExport1Enddatum($this->getEnddatum());
}
}
#[ORM\PrePersist]
#[ORM\PreUpdate]
public function cleanUnusedFields(): void
{
// End Datum
if ($this->getTyp() && $this->getTyp()->isEinzelveranstaltung()) {
$this->setEnddatum(null);
}
// Begin/Endzeit
if ($this->getTyp() && !$this->getTyp()->isEinzelveranstaltung()) {
$this->setBeginnAuswahl(null);
$this->setBeginnzeit(null);
$this->setEndzeit(null);
}
// Bild
if (!$this->getAbo()->isPremium()) {
$this->setVsImage('');
}
}
}