<?php
namespace App\Controller\Api;
use Carbon\Carbon;
use Pimcore\Controller\FrontendController;
use Pimcore\Model\DataObject;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class EventsApiController extends FrontendController
{
private $inputLat;
private $inputLon;
private $inputRadius;
/**
* @Route("/api/get-events", name="events")
*/
public function defaultAction(Request $request)
{
$events = new DataObject\Event\Listing();
$eventsFillup = new DataObject\Event\Listing();
// premium
$inputPremiumOnly = $request->get('premium', '');
$inputSearch = $request->get('q', '');
$limit = intval($request->get('l', 30));
if ($limit < 10) { $limit = 10; }
// date
$inputWeek = intval($request->get("week", 0));
$inputDuration = intval($request->get('duration', 0));
$inputYear = intval($request->get("year", 0));
$inputDate = $request->get("date", "");
// geolocation umkreis
$inputLat = floatval($request->get('lat', 0.0));
$inputLon = floatval($request->get('lon', 0.0));
$inputRadius = intval($request->get('radius', 10));
$this->inputLat = $inputLat;
$this->inputLon = $inputLon;
$this->inputRadius = $inputRadius;
$ids = $request->get('id', null);
if ($ids) {
$ids = explode(',', $ids);
}
$orderKey = ['startDate'];
$events->setOrderKey($orderKey);
$events->setOrder('asc');
// tags
$tags = $request->get("tags", null);
$now = Carbon::now();
// $now->setYear(2022);
// if ($inputWeek >= 0 && $inputWeek < 53) {
// $year = $inputYear > 0 ? $inputYear : date('Y');
// $now = Carbon::now();
// $now->setISODate($year, $inputWeek);
// }
if ($inputDate) {
$now = Carbon::parse($inputDate);
}
$year = $now->yearIso;
$week = $now->week;
// $startOfWeek = Carbon::parse($now)->startOfWeek();
$startOfWeek = Carbon::parse($now)->startOfDay();
$endOfWeek = Carbon::parse($now)->endOfWeek();
if ($inputDuration) {
$endOfWeek = $endOfWeek->addWeeks($inputDuration);
}
$events->setOrderKey("dateStart");
$events->setOrder("asc");
$eventsFillup->setOrderKey("dateStart");
$eventsFillup->setOrder("asc");
$condition = [];
$conditionParams = [];
$conditionFillup = [];
$conditionFillupParams = [];
$doFillup = true;
if ($tags) {
$condition[] = "topic LIKE :topic";
$conditionFillup[] = "topic LIKE :topic";
$conditionParams["topic"] = "%$tags%";
$conditionFillupParams["topic"] = "%$tags%";
}
if ($inputSearch) {
$condition[] = "(Title LIKE :search OR Text LIKE :search)";
$conditionParams["search"] = "%$inputSearch%";
$conditionFillup[] = "(Title LIKE :search OR Text LIKE :search)";
$conditionFillupParams["search"] = "%$inputSearch%";
}
if ($ids) {
$condition[] = "oo_id IN (:ids)";
$conditionParams["ids"] = $ids;
}
$condition[] = '((userEvent = 1 and released = 1) or (userEvent is null or userEvent = 0))';
// if ($startOfWeek && $endOfWeek) {
// $condition[] = "(dateStart >= :start AND dateStart <= :end OR (dateStart <= :start AND dateEnd > 0 AND dateEnd >= :endStart))";
// $conditionParams["start"] = $startOfWeek->getTimestamp();
// $conditionParams["endStart"] = $startOfWeek->getTimestamp();
// $conditionParams["end"] = $endOfWeek->getTimestamp();
// $conditionParams["endEnd"] = $endOfWeek->getTimestamp();
// $conditionFillup[] = "dateStart > :start";
// $conditionFillupParams["start"] = $endOfWeek->getTimestamp();
// }
$events->setCondition(join(' AND ', $condition), $conditionParams);
$data = $this->formatEvents($events);
$topics = DataObject\ClassDefinition::getById(DataObject\Event::classId())->getFieldDefinition("Topic")->getOptions();
$topicsMap = [];
foreach ($topics as $topic) {
$topicsMap[$topic["value"]] = $topic["key"];
}
$returnData = [];
$count = 0;
foreach ($data as $event) {
// $showEvent = (dateStart >= :start AND dateStart <= :end OR (dateStart <= :start AND dateEnd > 0 AND dateEnd >= :endStart))
$start = Carbon::parse($event['dateStart']);
$end = $event['dateEnd'] ? Carbon::parse($event['dateEnd']) : null;
$endOfDay = Carbon::parse($event['dateStart'])->endOfDay();
foreach ($event['otherDates'] as $otherDate) {
$start = Carbon::parse($otherDate['dateStart']);
$end = $otherDate['dateEnd'] ? Carbon::parse($otherDate['dateEnd']) : null;
$showEvent = $start >= $startOfWeek || ($end && $end >= $now) || (!$end && $now < $endOfDay);
if ($showEvent) {
$event['dateStart'] = $otherDate['dateStart'];
$event['dateEnd'] = $otherDate['dateEnd'];
$returnData[] = $event;
}
}
}
usort($returnData, function ($a, $b): int {
return [$a['premium'], $a['dateStart']] <=> [$b['premium'], $b['dateStart']];
});
if (count($returnData) > $limit) {
$returnData = array_slice($returnData, 0, $limit - 1);
}
// if (count($data) < $limit && $doFillup) {
// $eventsFillup->setLimit($limit - count($data));
// $eventsFillup->setCondition(join(' AND ', $conditionFillup), $conditionFillupParams);
// $fillup = $this->formatEvents($eventsFillup);
// $data = array_merge($data, $fillup);
//// foreach ($fillup as $event) {
//// // todo: if $event is already in $data...
//// $data[] = $fillup;
//// }
// }
//
// var_dump($data);
// die();
$response = $this->json([
// "conditionParams" => $condition,
"success" => true,
"topics" => $topicsMap,
"week" => [
"number" => $week,
"date" => $now->toDateTime(),
"start" => $startOfWeek,
"end" => $endOfWeek,
],
"data" => $returnData
]);
$response->setExpires(new \DateTime('+3 minutes'));
return $response;
}
/**
* @Route("/api/get-event/{id}", name="event_by_id")
*/
public function eventAction(int $id, Request $request)
{
$event = DataObject\Event::getById($id);
if (!$event) {
throw $this->createNotFoundException('The event does not exist');
}
$response = $this->json([
"success" => true,
"data" => $this->formatEvent($event)
]);
$response->setExpires(new \DateTime('+3 minutes'));
return $response;
}
public function formatImageGallery($imageGallery) {
$data = [];
foreach ($imageGallery->getItems() as $item) {
$image = $item->getImage();
$data[] = [
"path" => $image->getThumbnail("standardThumbnail")->getPath(),
];
}
return $data;
}
private function formatEvent($event) {
$link = $this->generateUrl('event_detail', ['path' => $event->getKey()]);
$previewVideo = null;
if ($event->getPreview()) {
$tmp = $event->getPreview()->getData();
$previewVideo = $tmp->getPath() . $tmp->getFilename();
}
// you can do this in SQL, but i guess the LISTING model does not allow this...
// SELECT latitude, longitude, SQRT(
// POW(69.1 * (latitude - [startlat]), 2) +
// POW(69.1 * ([startlng] - longitude) * COS(latitude / 57.3), 2)) AS distance
// FROM TableName HAVING distance < 25 ORDER BY distance;
$geo = $event->getGeolocation() ? [$event->getGeolocation()->getLatitude(), $event->getGeolocation()->getLongitude()] : [];
$distance = 0;
if (count($geo) > 0 && $this->inputLat && $this->inputLon) {
$distance = sqrt(
pow(69.1 * ($geo[0] - $this->inputLat), 2) +
pow(69.1 * ($this->inputLon - $geo[1]) * cos($geo[0] / 57.3), 2)
);
}
$otherDates = [];
$otherDates[] = [
"dateStart" => $event->getDateStart(),
"dateEnd" => $event->getDateEnd()
];
/** @var DataObject\Fieldcollection\Data\EventDates $otherDate */
if ($event->getOtherDates()) {
foreach ($event->getOtherDates() as $otherDate) {
$otherDates[] = [
"dateStart" => $otherDate->getDateStart(),
"dateEnd" => $otherDate->getDateEnd()
];
}
}
return [
"id" => $event->getId(),
// content
"topic" => $event->getTopic(),
"title" => $event->getTitle(),
"text" => '', // $event->getText(),
"entry" => $event->getEntry(),
"link" => $event->getLink(),
// location
"dateStart" => $event->getDateStart(),
"dateEnd" => $event->getDateEnd(),
"city" => $event->getCity(),
"street" => $event->getStreet(),
"location_type" => $event->getLocation_type(),
"geolocation" => $geo,
"distance" => $distance,
// additional times
"otherDates" => $otherDates,
// media
"image" => $event->getImage()?->getThumbnail('image1x1')->getPath(),
"imageTitle" => $event->getImage()?->getMetadata('title', 'de') ?? '',
"imageCopyright" => $event->getImage()?->getMetadata('copyright', 'de') ?? '',
"imageGallery" => ($event->getImageGallery() && count($event->getImageGallery()->getItems())) ? $this->formatImageGallery($event->getImageGallery()) : null,
"video_preview" => $previewVideo,
"video" => $event->getVideo(),
// rest
"premium" => $event->getPremium() ? 1 : 2,
"userEvent" => $event->getUserEvent() ? 1 : 0,
"published" => $event->getPublished(),
"createdAt" => date("c", $event->getCreationDate()),
"updatedAt" => date("c", $event->getModificationDate()),
"url" => $link
];
}
public function formatEvents($events) {
$data = [];
/**
* @var int $key
* @var DataObject\Event $event
*/
foreach ($events as $key => $event) {
$data[] = $this->formatEvent($event);
}
return $data;
}
/**
* Calculates the great-circle distance between two points, with
* the Vincenty formula.
* @param float $latitudeFrom Latitude of start point in [deg decimal]
* @param float $longitudeFrom Longitude of start point in [deg decimal]
* @param float $latitudeTo Latitude of target point in [deg decimal]
* @param float $longitudeTo Longitude of target point in [deg decimal]
* @param float $earthRadius Mean earth radius in [m]
* @return float Distance between points in [m] (same as earthRadius)
*/
public static function vincentyGreatCircleDistance(
$latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$lonDelta = $lonTo - $lonFrom;
$a = pow(cos($latTo) * sin($lonDelta), 2) +
pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
$b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);
$angle = atan2(sqrt($a), $b);
return $angle * $earthRadius;
}
}