<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Controller\BookingApi;
use App\Ecommerce\OrderManager\Filter\TicketCatalog;
use App\Model\BookingApi\DateRange;
use App\Model\BookingApi\Event;
use App\Model\BookingApi\EventAgeGroupPrice;
use App\Model\BookingApi\Image;
use App\Model\BookingApi\LocalizedText;
use App\Model\BookingApi\PriceInfos;
use App\Model\BookingApi\SimpleIdTextObject;
use App\Model\BookingApi\TextWithHeadline;
use App\Model\BookingApi\TicketCatalogUpgradeRelation;
use App\Model\BookingApi\TicketInsurance;
use App\Model\BookingApi\TicketValidity;
use App\Model\Shop\Event\EventProduct;
use App\Model\Shop\Event\ShopDynamicPrice;
use App\Model\Shop\Ticket\ShopTicketCatalog;
use App\Model\Type\TenantType;
use App\Traits\EnvironmentTrait;
use Carbon\Carbon;
use OpenApi\Annotations as OA;
use Pimcore\Model\DataObject\Data\ObjectMetadata;
use Pimcore\Model\DataObject\Fieldcollection\Data\CustomDescription;
use Pimcore\Model\DataObject\Fieldcollection\Data\PriceInfo;
use Pimcore\Model\DataObject\ShopEvent;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class ApiCatalogControllerApi
*
* @Route("/bookingAPI/catalog")
*
* @OA\Tag(name="Catalog")
*/
class ApiCatalogControllerApi extends ApiAbstractController
{
use EnvironmentTrait;
/**
* Return all relevant ticket catalog data for marketplace
*
* @param Request $request
*
* @Route("/ticketcatalogs", name="catalog_ticketcatalogs", methods={"POST", "GET"})
*
* @OA\Post(
* path="/bookingAPI/catalog/ticketcatalogs",
* description="Get all relevant ticket catalog data for marketplace",
* tags={"Catalog"},
*
* @OA\RequestBody(
* required=false,
*
* @OA\JsonContent(
*
* @OA\Property( property="pageSize", type="string", example="10"),
* @OA\Property( property="pageNumber", type="string", example="1"),
* )
* ),
*
* @OA\Response(
* response=200,
* description="Successful operation",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="success", type="boolean"),
* @OA\Property(property="total", type="int", example=50),
* @OA\Property(property="pageCurrent", type="int", example=1),
* @OA\Property(property="pageSize", type="int", example=10),
* @OA\Property(property="pageLast", type="int", example=5),
* @OA\Property(property="ticketCatalogs", type="array",
*
* @OA\Items(ref="#/components/schemas/TicketCatalog")
* )
* )
* )
* ),
*
* @OA\Response(
* response=500,
* description="Error",
* ),
*
* @OA\Info (
* title="ticket catalog",
* version="0.0.1"
* )
*
*
* )
*/
public function ticketcatalogsAction(Request $request): JsonResponse
{
$body = $request->getContent();
$requestData = json_decode($body, true);
$pageSize = $requestData ? intval($requestData['pageSize']) : 100;
$pageNumber = $requestData ? intval($requestData['pageNumber']) : 1;
try {
$ticketData = $this->getTicketCatalogsAsJson($pageSize, $pageNumber);
$returnData['success'] = true;
$returnData = array_merge($returnData, $ticketData);
if ($request->get('debug')) {
p_r($returnData);
die();
}
return new JsonResponse($returnData);
} catch (\Throwable $throwable) {
$this->bookingapiLogger->error($throwable->getMessage() . 'Stack Trace: ' . $throwable->getTraceAsString());
return $this->sendErrors([$throwable->getMessage()], 500);
}
}
/**
* Return all relevant event catalog data for marketplace. Quota gets displayed for each day with the timestamp being the beginning of the day
*
* @param Request $request
*
* @Route("/events", name="catalog_events", methods={"POST", "GET"})
*
* @OA\Post(
* path="/bookingAPI/catalog/events",
* description="Get all relevant event catalog data for marketplace. Quota gets displayed for each day with the timestamp being the beginning of the day",
* tags={"Catalog"},
*
* @OA\RequestBody(
* required=false,
*
* @OA\JsonContent(
*
* @OA\Property( property="pageSize", type="string", example="10"),
* @OA\Property( property="pageNumber", type="string", example="1"),
* )
* ),
*
* @OA\Response(
* response=200,
* description="Successful operation",
*
* @OA\MediaType(
* mediaType="application/json",
*
* @OA\Schema(
* type = "object",
*
* @OA\Property(property="success", type="boolean"),
* @OA\Property(property="total", type="int", example=50),
* @OA\Property(property="pageCurrent", type="int", example=1),
* @OA\Property(property="pageSize", type="int", example=10),
* @OA\Property(property="pageLast", type="int", example=5),
* @OA\Property(property="events", type="array",
*
* @OA\Items(ref="#/components/schemas/Event")
* )
* )
* )
* ),
*
* @OA\Response(
* response=500,
* description="Error",
* ),
*
* )
*/
public function eventsAction(Request $request): JsonResponse
{
$body = $request->getContent();
$requestData = json_decode($body, true);
$pageSize = $requestData ? intval($requestData['pageSize']) : 100;
$pageNumber = $requestData ? intval($requestData['pageNumber']) : 1;
try {
$this->setEnvironmentTenant();
$eventData = $this->getEventsAsJson($pageSize, $pageNumber);
$returnData['success'] = true;
$returnData = array_merge($returnData, $eventData);
if ($request->get('debug')) {
p_r($returnData);
die();
}
return new JsonResponse($returnData);
} catch (\Throwable $throwable) {
$this->bookingapiLogger->error($throwable->getMessage() . 'Stack Trace: ' . $throwable->getTraceAsString());
return $this->sendErrors([$throwable->getMessage()], 500);
}
}
/**
* @param int $pageSize
* @param int $pageNumber
*
* @return array<string,mixed>
*/
private function getTicketCatalogsAsJson(int $pageSize, int $pageNumber): array
{
$jsonTicketData = [];
$listing = new \Pimcore\Model\DataObject\ShopTicketCatalog\Listing();
$listing->setOrderKey('sort DESC, name ASC', false);
$listing->addConditionParam('FIND_IN_SET("' . TenantType::MARKETPLACE . '", tenant)');
$listing->setOffset($pageNumber == 1 ? 0 : ($pageSize * ($pageNumber - 1) + 1));
$listing->setLimit($pageSize);
foreach ($listing->load() as $shopTicketCatalog) {
/**
* @var ShopTicketCatalog $shopTicketCatalog
*/
$catalogId = $shopTicketCatalog->getId();
$teaserPrice = $shopTicketCatalog->getTeaserPrice();
$galleryImages = [];
if($shopTicketCatalog->getMainImage()){
$galleryImages[$shopTicketCatalog->getMainImage()->getId()]=$this->formatImage($shopTicketCatalog->getMainImage());
}
foreach($shopTicketCatalog->getGallery() as $galleryImage){
$galleryImages[$galleryImage->getImage()->getId()] = $this->formatImage($galleryImage->getImage());
}
$galleryImages = array_values($galleryImages);
$ticketCatalogItem = new \App\Model\BookingApi\TicketCatalog();
$ticketCatalogItem->id = $catalogId;
$ticketCatalogItem->sort = $shopTicketCatalog->getSort() ?: 0;
$ticketCatalogItem->images = $galleryImages;
$ticketCatalogItem->interests = $this->getInterests($shopTicketCatalog);
$ticketCatalogItem->category = $this->getCategory($shopTicketCatalog);
$ticketCatalogItem->bookingDisabled = boolval($shopTicketCatalog->getIsNotBookable());
$ticketCatalogItem->setIsUpgrade(boolval($shopTicketCatalog->getIsUpgrade()));
if(!$shopTicketCatalog->getIsUpgrade() && count($shopTicketCatalog->getUpgrades())>0){
$upgradeInfo = [];
foreach($shopTicketCatalog->getUpgrades() as $objectMetadata){
$relation = new TicketCatalogUpgradeRelation();
$relation->setUpgradeCatalogId($objectMetadata->getObject()->getId());
$relation->setUpgradeKey($objectMetadata->getData()['name']);
$upgradeInfo[]=$relation;
}
$ticketCatalogItem->setUpgradeCatalogs($upgradeInfo);
}
$ticketCatalogItem->setIsMarketplacePackage(boolval($shopTicketCatalog->getIsMarketplacePackage()));
$validDates = [];
foreach ($shopTicketCatalog->getAllValidDateRanges(Carbon::now()->startOfDay(), 'U000') as $dateRange) {
$validDates[] = new DateRange($dateRange['from'], $dateRange['to']);
}
$ticketCatalogItem->validDates = $validDates;
$ticketCatalogItem->consumerCategories = $this->getConsumerCategories($shopTicketCatalog->getTicketConsumerCategories());
$ticketCatalogItem->ticketProducts = $this->getTicketProducts($shopTicketCatalog);
$ticketCatalogItem->insurances = $this->getInsurances($shopTicketCatalog);
$ticketCatalogItem->createdAt = $shopTicketCatalog->getCreationDate() * 1000;
$ticketCatalogItem->updatedAt = $shopTicketCatalog->getModificationDate() * 1000;
if ($teaserPrice) {
$ticketCatalogItem->priceFrom = $this->formatPrice($teaserPrice);
}
$ticketCatalogItem->setCustomDescriptions($this->getCustomDescriptions($shopTicketCatalog));
$ticketCatalogItem->setPriceInfos($this->getPriceInfos($shopTicketCatalog));
$name = new LocalizedText();
$description = new LocalizedText();
$topLine = new LocalizedText();
$priceDescription = new LocalizedText();
$includeDescription = new LocalizedText();
$bookingNoteDescription = new LocalizedText();
$priceInfoNote = new LocalizedText();
foreach ($this->languages as $language) {
$topLine->$language = $shopTicketCatalog->getTitleMarketplace($language) ?: '';
$name->$language = $shopTicketCatalog->getName($language) ?: '';
$bookingNoteDescription->$language = $shopTicketCatalog->getBookingNoteDescription($language) ?: '';
$includeDescription->$language = $shopTicketCatalog->getIncludeDescription($language) ?: '';
$priceDescription->$language = $shopTicketCatalog->getPriceInfoDescription($language) ?: '';
$priceInfoNote->$language = $shopTicketCatalog->getPriceInfoNote($language) ?: '';
$description->$language = $shopTicketCatalog->getMarketplaceDescription($language) ?: '';
}
$ticketCatalogItem->name = $name;
$ticketCatalogItem->description = $description;
$ticketCatalogItem->topLine = $topLine;
$ticketCatalogItem->priceDescription = $priceDescription;
$ticketCatalogItem->priceInfoNote = $priceInfoNote;
$ticketCatalogItem->includeDescriptions = $includeDescription;
$ticketCatalogItem->bookingNoteDescription = $bookingNoteDescription;
$jsonTicketData[] = $ticketCatalogItem->jsonSerialize();
}
return [
'total' => $listing->getTotalCount(),
'pageCurrent' => intval($pageNumber),
'pageSize' => intval($pageSize),
'pageLast' => intval(ceil($listing->getTotalCount() / $pageSize)),
'ticketCatalogs' => $jsonTicketData,
];
}
/**
* @param int $pageSize
* @param int $pageNumber
*
* @return array<string, mixed>
*/
public function getEventsAsJson(int $pageSize, int $pageNumber): array
{
$listing = new \Pimcore\Model\DataObject\ShopEvent\Listing();
$listing->setOrderKey('sort ASC, name ASC', false);
$listing->addConditionParam('FIND_IN_SET("' . TenantType::MARKETPLACE . '", tenant)');
$listing->setOffset($pageNumber == 1 ? 0 : ($pageSize * ($pageNumber - 1) + 1));
$listing->setLimit($pageSize);
$jsonEventData = [];
foreach ($listing->load() as $shopEvent) {
/**
* @var EventProduct $shopEvent
*/
$apiEvent = new Event();
$apiEvent->id = $shopEvent->getId();
$teaserPrice = $shopEvent->getTeaserPrice();
$apiEvent->sort = intval($shopEvent->getSort());
$apiEvent->images = $this->getEventImages($shopEvent);
$apiEvent->bookable = $shopEvent->isBookable();
$apiEvent->stopSale = intval($shopEvent->getStopSale());
$apiEvent->stopSaleEndOfDay = boolval($shopEvent->getBookableOnDayOfEvent());
$apiEvent->maxParticipantPerBooking = $shopEvent->getMaxParticipantPerBooking() ?: 0;
$apiEvent->ageGroups = $this->getEventAgeGroups($shopEvent);
$apiEvent->validDates = $this->getValidEventDates($shopEvent);
$apiEvent->interests = $this->getInterests($shopEvent);
$apiEvent->displayDates = $shopEvent->getDisplayDates();
$apiEvent->createdAt = $shopEvent->getCreationDate() * 1000;
$apiEvent->updatedAt = $shopEvent->getModificationDate() * 1000;
$apiEvent->priceFrom = $this->formatPrice($teaserPrice);
$apiEvent->category = $this->getCategory($shopEvent);
$apiEvent->setCustomDescriptions($this->getCustomDescriptions($shopEvent));
$apiEvent->setPriceInfos($this->getPriceInfos($shopEvent));
$name = new LocalizedText();
$topLine = new LocalizedText();
$description = new LocalizedText();
$priceDescription = new LocalizedText();
$includeDescription = new LocalizedText();
$priceInfoNote = new LocalizedText();
foreach ($this->languages as $language) {
$name->$language = $shopEvent->getName($language) ?: '';
$topLine->$language = $shopEvent->getTopTitle($language) ?: '';
$description->$language = $shopEvent->getMarketplaceDescription($language) ?: '';
$priceDescription->$language = $shopEvent->getPriceInfoDescription($language) ?: '';
$priceInfoNote->$language = $shopEvent->getPriceInfoNote($language) ?: '';
$includeDescription->$language = $shopEvent->getIncludeDescription($language) ?: '';
}
$apiEvent->setName($name);
$apiEvent->setTopLine($topLine);
$apiEvent->setDescription($description);
$apiEvent->setPriceDescription($priceDescription);
$apiEvent->setPriceInfoNote($priceInfoNote);
$apiEvent->setIncludeDescription($includeDescription);
$jsonEventData[] = $apiEvent->jsonSerialize();
}
return [
'total' => $listing->getTotalCount(),
'pageCurrent' => intval($pageNumber),
'pageSize' => intval($pageSize),
'pageLast' => intval(ceil($listing->getTotalCount() / $pageSize)),
'events' => $jsonEventData,
];
}
/**
* @param ShopTicketCatalog|EventProduct $shopTicketCatalog
*
* @return SimpleIdTextObject[]
*/
private function getInterests(ShopTicketCatalog|EventProduct $shopTicketCatalog): array
{
$interests = [];
foreach ($shopTicketCatalog->getInterestsMarketplace() as $interest) {
$apiObject = new SimpleIdTextObject();
$apiObject->id = $interest->getId();
$text = new LocalizedText();
foreach ($this->languages as $language) {
$text->$language = $interest->getName($language) ?: '';
}
$apiObject->texts = $text;
$interests[] = $apiObject;
}
return $interests;
}
private function getCategory(ShopTicketCatalog|EventProduct $shopTicketCatalog): SimpleIdTextObject
{
$apiObject = new SimpleIdTextObject();
$category = $shopTicketCatalog->getProductCategoryMarketplace();
if ($category) {
$apiObject->id = $category->getId();
$texts = new LocalizedText();
foreach ($this->languages as $language) {
$texts->$language = $category->getName($language) ?: '';
}
$apiObject->texts = $texts;
}
return $apiObject;
}
/**
* @param ShopTicketCatalog|EventProduct $sourceObject
*
* @return PriceInfos[]
*/
private function getPriceInfos(ShopTicketCatalog|EventProduct $sourceObject): array
{
$priceInfos = [];
if ($sourceObject->getPriceInfos()) {
foreach ($sourceObject->getPriceInfos() as $priceInfoItem) {
/**
* @var PriceInfo $priceInfoItem
*/
$priceInfo = new PriceInfos();
$title = new LocalizedText();
$additionalInfo = new LocalizedText();
$ageGroupInfo = new LocalizedText();
$priceInfoText = new LocalizedText();
foreach ($this->languages as $language) {
$title->$language = $priceInfoItem->getTitle($language) ?: '';
$additionalInfo->$language = $priceInfoItem->getAdditionalInfo($language) ?: '';
$ageGroupInfo->$language = $priceInfoItem->getAgeGroup($language) ?: '';
$priceInfoText->$language = $priceInfoItem->getPriceInfoText($language) ?: '';
}
$priceInfo->setTitle($title);
$priceInfo->setAdditionalInfo($additionalInfo);
$priceInfo->setAgeGroupText($ageGroupInfo);
$priceInfo->setPriceInfoText($priceInfoText);
$priceInfos[] = $priceInfo;
}
}
return $priceInfos;
}
/**
* @param ShopTicketCatalog|EventProduct $sourceObject
*
* @return TextWithHeadline[]
*/
private function getCustomDescriptions(ShopTicketCatalog|EventProduct $sourceObject): array
{
$customDescriptions = [];
foreach ($sourceObject->getCustomDescriptions() ?: [] as $customDescriptionItem) {
/**
* @var CustomDescription $customDescriptionItem
*/
$localizedHeadine = new LocalizedText();
$localizedText = new LocalizedText();
$customDescription = new TextWithHeadline();
foreach ($this->languages as $language) {
$localizedHeadine->$language = $customDescriptionItem->getCustomHeadline($language) ?: '';
$localizedText->$language = $customDescriptionItem->getCustomDescription($language) ?: '';
}
$customDescription->headline = $localizedHeadine;
$customDescription->text = $localizedText;
$customDescriptions[] = $customDescription;
}
return $customDescriptions;
}
/**
* @param ShopTicketCatalog $ticketCatalog
*
* @return \App\Model\BookingApi\TicketProduct[]
*/
private function getTicketProducts(ShopTicketCatalog $ticketCatalog): array
{
$siteConfig = $this->siteConfigService->getSiteConfig();
$ticketData = [];
foreach ($ticketCatalog->getTicketProducts() as $ticketProduct) {
/**
* @var \Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\TicketProduct $ticketProduct
*/
$apiTicketProduct = new \App\Model\BookingApi\TicketProduct();
$apiTicketProduct->id = $ticketProduct->getId();
$localizedName = new LocalizedText();
$localizedInfoText = new LocalizedText();
$localizedCatalogInfoText = new LocalizedText();
foreach ($this->languages as $language) {
$localizedName->$language = $ticketProduct->getName($language) ?: '';
$localizedCatalogInfoText->$language = $ticketCatalog->getInfoBubbleDescription($language) ?: '';
$localizedInfoText->$language = $ticketProduct->getDescription($language) ?: '';
}
$apiTicketProduct->name = $localizedName;
$apiTicketProduct->infoText = $localizedInfoText;
$apiTicketProduct->infoTextCatalog = $localizedCatalogInfoText;
//hide acquisition types if set so in config
$acquisitionTypes = array_diff($ticketProduct->getAllowedAcquisitionTypes(), ($siteConfig->getHideAcquisitionTypes() ?? []));
$apiTicketProduct->acquisitionTypes = $acquisitionTypes ?: [];
$apiTicketProduct->defaultAcquisitionType = $ticketProduct->getDefaultAcquisitionType() ?: '';
$apiTicketProduct->personalization = $ticketProduct->getRequirements() ?: [];
$validity = new TicketValidity();
$validity->validityValue = $ticketProduct->getValidityValue();
$validity->validityUnit = $ticketProduct->getValidityUnit();
$apiTicketProduct->validity = $validity;
$ticketData[] = $apiTicketProduct;
}
return $ticketData;
}
/**
* @param ShopTicketCatalog $shopTicketCatalog
*
* @return TicketInsurance[]
*/
private function getInsurances(ShopTicketCatalog $shopTicketCatalog): array
{
$insurances = [];
foreach ($shopTicketCatalog->getInsurances() as $insurance) {
$apiInsurance = new TicketInsurance();
$apiInsurance->id = $insurance->getId();
$localizedName = new LocalizedText();
$localizedDescription = new LocalizedText();
foreach ($this->languages as $language) {
$localizedName->$language = $insurance->getName($language) ?: '';
$localizedDescription->$language = $insurance->getDescription($language) ?: '';
}
$apiInsurance->name = $localizedName;
$apiInsurance->description = $localizedDescription;
$insurances[] = $apiInsurance;
}
return $insurances;
}
/**
* @param ShopEvent $shopEvent
*
* @return Image[]
*/
private function getEventImages(ShopEvent $shopEvent): array
{
$imageData = [];
if($shopEvent->getMainImage()){
$imageData[$shopEvent->getMainImage()->getId()]= $this->formatImage($shopEvent->getMainImage());
}
foreach ($shopEvent->getImages() as $image) {
if ($image instanceof \Pimcore\Model\DataObject\Data\Hotspotimage) {
$image = $image->getImage();
}
$imageData[$image->getId()] = $this->formatImage($image);
}
return array_values($imageData);
}
/**
* @param EventProduct $shopEvent
*
* @return array<mixed>
*/
private function getValidEventDates(EventProduct $shopEvent): array
{
$ranges = $shopEvent->getAllValidDateRanges(Carbon::now()->startOfDay(), null);
$dates = [];
foreach ($ranges as $range) {
$dates[] = [
'from' => $range['from']->getTimestamp() * 1000,
'to' => $range['to']->getTimestamp() * 1000,
'availability' => $this->getAvailability($range['from'], $range['to'], $shopEvent),
];
}
return $dates;
}
/**
* @param Carbon $from
* @param Carbon $to
* @param EventProduct $shopEvent
*
* @return array<int,int>
*/
private function getAvailability(Carbon $from, Carbon $to, EventProduct $shopEvent): array
{
$availability = [];
/**
* @var \App\Ecommerce\AvailabilitySystem\Event\AvailabilitySystem $availabilitySystem
*/
$availabilitySystem = $shopEvent->getAvailabilitySystemImplementation();
$items = $availabilitySystem->getAvailableList($shopEvent, $from, $to);
foreach ($items as $key => $item) {
$timeStampMs = intval($key) * 1000;
$availability[$timeStampMs] = $item->getAvailableQuota() <= 0 ? 0 : $item->getAvailableQuota();
}
return $availability;
}
/**
* @param ShopEvent $shopEvent
*
* @return EventAgeGroupPrice[]
*/
private function getEventAgeGroups(ShopEvent $shopEvent): array
{
$eventAgeGroupsData = [];
$dynamicPrices = $shopEvent->getPrices() ?: [];
foreach ($dynamicPrices as $dynamicPrice) {
/**
* @var ObjectMetadata $dynamicPrice
*/
$groupName = '';
if(in_array('groupname', $dynamicPrice->getColumns())){
if(isset($dynamicPrice->getData()['groupname'])){
$groupName = $dynamicPrice->getData()['groupname'];
}
}
$dynamicPriceObject = $dynamicPrice->getObject();
if ($dynamicPriceObject instanceof ShopDynamicPrice) {
$ageGroupData = new EventAgeGroupPrice();
$ageGroupData->setId($dynamicPriceObject->getId());
$ageGroupData->setPrice($this->formatPrice($dynamicPriceObject->getDefaultPrice()));
$localizedTexts = new LocalizedText();
$localizedGroupName = new LocalizedText();
foreach ($this->languages as $language) {
$localizedTexts->$language = $dynamicPriceObject->getPriceLabel($language);
if(!empty($groupName)) {
$localizedGroupName->$language = $this->translator->trans($groupName, [], null, $language);
}
}
if(!empty($groupName)){
$ageGroupData->setGroupName($groupName);
$ageGroupData->setGroupNameTexts($localizedGroupName);
}
$ageGroupData->setName($localizedTexts);
$eventAgeGroupsData[] = $ageGroupData;
}
}
return $eventAgeGroupsData;
}
}