<?php
/**
* Elements DeMI
*
* This source file is available under the elements DeMI license version 1
*
* @copyright Copyright (c) elements.at New Media Solutions GmbH (https://www.elements.at/)
* @license elements DeMI Lizenz Version 1 (https://www.elements.at/de/demi-lizenz)
*/
namespace Elements\Demi\Accommodation\Search\Service\Vacancies;
use DateInterval;
use DateTime;
use Elements\Demi\Accommodation\Search\Parameter;
use Elements\Demi\Accommodation\Search\Parameter\RoomRow;
use Elements\Demi\Accommodation\Search\ResultSet;
use Elements\Demi\Accommodation\Search\ResultSet\Accommodation;
use Elements\Demi\Accommodation\Search\ResultSet\Product;
use Elements\Demi\Accommodation\Search\Service\Vacancies\Local\SortFactory;
use Elements\Demi\Deskline\Constant\SearchParameter\AccommodationInterface;
use Elements\Demi\Model\AccommodationProduct;
use Elements\Demi\Model\AccommodationServiceProvider;
use Elements\Demi\Model\HousePackageMaster;
use Elements\Demi\Model\Stars;
use Elements\Demi\Vacancies\Dependency\VacancyDependencyContainer;
use Exception;
use JetBrains\PhpStorm\Pure;
use Pimcore;
/**
* Class Local
*/
class Local extends AbstractAdapter
{
protected string $adapterCachePostFix = 'Local';
protected int $count = -1;
protected array $sortFactories = [];
protected array $workResultPerFunction = [];
protected array $failedTasks = [];
protected array $workResult = [];
public function __construct(VacancyDependencyContainer $dc)
{
parent::__construct($dc);
}
#[Pure]
public function count(): ?int
{
return $this->resultSet->getTotalCount();
}
protected function detailSearch(): mixed
{
$searchParams = $this->getSearchParameter();
$cacheId = $this->getCacheId();
if (!($builtResult = $this->getDc()->getFromCache($cacheId))) {
$resultBuilder = $this->getResultBuilder();
$resultArrays = [];
foreach ($searchParams->getRoomrows() as $roomrow) {
$selectFields = [];
$selectFields[] = 'dacco.fid';
$selectFields[] = 'dacco.isTop';
$selectFields[] = 'dacco.bestPrice';
$selectFields[] = 'dacco.oo_id';
$selectFields[] = 'p.product_id';
$selectFields[] = 'p.productType';
$selectFields[] = 'p.serviceId';
$selectFields[] = 'p.availabilityReference';
$selectFields[] = 'p.kurtaxeObjId';
$selectFields[] = 'max(p.gap_id) as gap_id';
$selectFields[] = 'max(p.vacancies) vacancies';
$selectFields[] = 'dacco.position__longitude as lng';
$selectFields[] = 'dacco.position__latitude as lat ';
if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
$selectFields[] = 'hpMasters.src_id as hpMaster_id';
}
// Map Stuff
if ($searchParams->getExtendedResultset()) {
$selectFields[] = 'stars.starsNumbers as starNr';
$selectFields[] = 'stars.isSuperior as starSup';
$selectFields[] = 'addressColl.addressLine1';
$selectFields[] = 'addressColl.addressLine2';
$selectFields[] = 'addressColl.zipCode';
$selectFields[] = 'addressColl.town';
$selectFields[] = 'addressColl.city';
$selectFields[] = 'addressColl.country';
$selectFields[] = 'dacco.' . $this->getDc()->getRatingAverageColumn() . ' as ratingAverage';
$selectFields[] = 'dacco.' . $this->getDc()->getRatingCountColumn() . ' as ratingCount';
$selectFields[] = 'dacco.name as accoName';
}
// Map Stuff END
$query = 'SELECT ' . implode(', ', $selectFields) . ' ';
$query .= $this->getSqlFromPart();
if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
// House Package Master
$dummyHpMaster = new HousePackageMaster();
$query .= ' INNER JOIN ' . $this->getDc()->getUsedPimcoreDb() . '.object_relations_' . $dummyHpMaster->getClassId() . ' hpMasters ON hpMasters.dest_id = p.product_id';
}
if ($searchParams->getExtendedResultset() || $searchParams->getStarsTableNeeded()) {
$dummyStar = new Stars();
$query .= ' LEFT JOIN ' . $this->getDc()->getUsedPimcoreDb() . '.object_' . $dummyStar->getClassId() . ' stars ON dacco.stars__id = stars.oo_id ';
}
// Map Stuff
if ($searchParams->getExtendedResultset()) {
$dummyAcco = new AccommodationServiceProvider();
$query .= ' LEFT JOIN ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiAddress_' . $dummyAcco->getClassId() . " addressColl ON addressColl.o_id = dacco.oo_id AND addressColl.fieldname = 'addresses' AND addressColl.addressType = 'Object' ";
}
// Map Stuff END
$query .= $this->getCondition($roomrow);
// Map Stuff
if ($searchParams->getExtendedResultset()) {
$query .= ' AND dacco.name IS NOT NULL ';
}
// Map Stuff END
// Closed Periods
if ($searchParams->getDateFrom() && $searchParams->getDateTo()) {
$dummyAcco = new AccommodationServiceProvider();
$query .= ' AND dacco.oo_id NOT IN (SELECT o_id FROM ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiClosedPeriods_' . $dummyAcco->getClassId() . '
WHERE (' . $searchParams->getDateFrom()->getTimestamp() . ' BETWEEN ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiClosedPeriods_' . $dummyAcco->getClassId() . '.start AND ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiClosedPeriods_' . $dummyAcco->getClassId() . '.end)
OR (' . $searchParams->getDateTo()->getTimestamp() . ' BETWEEN ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiClosedPeriods_' . $dummyAcco->getClassId() . '.start AND ' . $this->getDc()->getUsedPimcoreDb() . '.object_collection_demiClosedPeriods_' . $dummyAcco->getClassId() . '.end)) ';
}
// Closed Periods end
if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
//make sure only published HPM are used
$query .= ' AND hpMasters.src_id in (select o_id from ' . $this->getDc()->getUsedPimcoreDb() . '.object_' . $dummyHpMaster->getClassId() . ' where o_published = 1 and active=1)';
}
$query .= ' group by p.product_id';
$baseOrder = $this->getBaseOrderBy();
if (!empty($baseOrder)) {
$query .= ' ORDER BY ' . $baseOrder;
} else {
$query .= ' ORDER BY NULL';
}
//print_r($this->readifySql($query, $this->getBindParamsLegacy($roomrow)));die();
if (isset($_GET['readifySql']) && $_GET['readifySql'] === '1d' && Pimcore::inDebugMode()) {
print_r($this->readifySql($query, $this->getBindParams($roomrow)));
die();
}
$time = (round(microtime(true) * 1000));
$stmt = $this->getDc()->getDb()->executeQuery($query, $this->getBindParams($roomrow));
$timeTaken = (round(microtime(true) * 1000)) - $time;
if (isset($_GET['gimmetime']) && $_GET['gimmetime'] && Pimcore::inDebugMode()) {
p_r('query ' . $timeTaken);
}
if (isset($_GET['readifySql']) && $_GET['readifySql'] === '1' && Pimcore::inDebugMode()) {
print_r($this->readifySql($query, $this->getBindParams($roomrow)));
print_r($timeTaken);
}
$resultArrays[] = $stmt->fetchAll();
}
if (count($resultArrays) > 1) {
if ($searchParams->getUseMultiLineMerge()) {
$builtResult = $this->mergeMultipleLines($resultArrays);
} else {
$builtResult = $this->intersectResults($resultArrays);
}
$this->setResultSetMinpriceAndRange($builtResult);
} else {
$finishedArray = $resultArrays[0];
$resultBuilder->setCheckBookable(true);
$builtResult = $resultBuilder->buildResult($finishedArray);
}
if (isset($_GET['readifyResult'])) {
if ($_GET['readifyResult'] === '1' && Pimcore::inDebugMode()) {
p_r($resultArrays);
p_r($builtResult);
}
if ($_GET['readifyResult'] === '1d' && Pimcore::inDebugMode()) {
p_r($resultArrays);
p_r($builtResult);
die();
}
}
$this->getDc()->saveToCache($builtResult, $cacheId);
}
return $builtResult;
}
public function searchVacancies(bool $force = false): mixed
{
$searchParams = $this->getSearchParameter();
$this->setSearchParameter($searchParams);
$cacheid = $this->getDc()->getIdGenerator()?->generateCompleteCacheKey($searchParams);
if ($force || !$cacheid || !$builtResult = $this->getDc()->getFromCache($cacheid)) {
$builtResult = null;
if ($this->doBaseSearch()) {
$builtResult = $this->detailSearch();
}
$withResult = false;
if ($builtResult) {
$withResult = true;
}
// "refilter" also for Acco List
$customOrder = $searchParams->getRefilterCustomOrder();
if ($this->doRefilter($withResult) || !$builtResult || !empty($customOrder) || $searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
$builtResult = $this->reFilterDetails($builtResult);
}
$resultBuilder = $this->getResultBuilder();
//do PHP filtering and sorting after everything has been built
if ($searchParams->getSkipBaseFilter()) {
$builtResultAccommodations = $builtResult->getAccommodations();
} else {
if (method_exists($this, 'beforePhpSortingAndFiltering')) {
$this->beforePhpSortingAndFiltering($builtResult);
}
$builtResultAccommodations = $builtResult->getAccommodations();
$this->iterateFilterResultSet($builtResultAccommodations);
$this->setResultSetMinpriceAndRange($builtResult);
$this->sortForPrice($builtResultAccommodations);
if (!$searchParams->isNoDate()) {
$this->getSatisfactionForAccommodations($builtResultAccommodations);
}
}
if ($searchParams->getTopFirst() > 0 || $searchParams->getAllTopsFirst()) {
$topCount = null;
if (!$searchParams->getAllTopsFirst()) {
$topCount = $searchParams->getTopFirst();
}
$resultBuilder->sortTopsFirst($builtResultAccommodations, $topCount);
}
$builtResult->setAccommodations($builtResultAccommodations);
$builtResult->setTotalCount(count($builtResultAccommodations));
$this->getDc()->saveToCache($builtResult, $cacheid);
}
$this->resultSet = $builtResult;
if ($searchParams->getDateFrom() && $searchParams->getDateFrom()->getTimestamp() < mktime(23, 59, 59, 3, 15, 2020)) {
$builtResult->setAccommodations([]);
$builtResult->setTotalCount(0);
}
return $builtResult;
}
public function getSatisfactionForAccommodations(array &$accommodations): void
{
foreach ($accommodations as $accoId => $acco) {
if (!$this->getSatisfaction($acco)) {
unset($accommodations[$accoId]);
}
}
}
public function intersectResults(array $resultArrays): ResultSet
{
$resultSets = [];
foreach ($resultArrays as $roomrowIndex => $resultArray) {
$resultSets[] = $this->getResultBuilder()->buildResult($resultArray, 'EUR', $roomrowIndex);
}
$intersectedAccos = array_keys($resultSets[0]->getAccommodations());
foreach ($resultSets as $index => $resultSet) {
if ($index == 0) {
continue;
}
$intersectedAccos = array_intersect($intersectedAccos, array_keys($resultSet->getAccommodations()));
}
$actualResultSet = $resultSets[0];
$actualResultSet->removeAllBut($intersectedAccos, false, false);
foreach ($resultSets as $index => $resultSet) {
if ($index == 0) {
continue;
}
$resultSet->removeAllBut($intersectedAccos, false, false);
$accos = $resultSet->getAccommodations();
foreach ($accos as $acco) {
$actualResultSet->addRoomRowToAcco($acco);
}
}
return $actualResultSet;
}
protected function refilterNeededFromFilters(): bool
{
$searchParams = $this->getSearchParameter();
foreach ($this->refilterKeys as $rKey) {
$getter = 'get' . ucfirst($rKey);
$is = 'is' . ucfirst($rKey);
if (method_exists($searchParams, $getter)) {
$check = $searchParams->$getter();
} elseif (method_exists($searchParams, $is)) {
$check = $searchParams->$is();
} else {
continue;
}
if (!empty($check)) {
return true;
}
}
return false;
}
private function doRefilter(bool $withResultSet = false): bool
{
$searchParams = $this->getSearchParameter();
if ($this->refilterNeededFromFilters()) {
return true;
}
foreach ((array)$searchParams->getOrderKey() as $oKey) {
if (in_array($oKey, $this->refilterOrderKeys, false)) {
return true;
}
}
if ($withResultSet) {
return false;
}
$orderKeys = (array)$searchParams->getOrderKey();
$baseOrderKeys = array_keys($this->baseOrderKeys);
foreach ($orderKeys as $orderKey) {
if (!in_array($orderKey, $baseOrderKeys, false)) {
return true;
}
}
return false;
}
protected function doBaseSearch(): bool
{
return $this->getSearchParameter()->validateRequiredVacancySearchParameter();
}
public function mergeMultipleLines(array $resultArrays): ResultSet
{
return $this->intersectResults($resultArrays);
}
public function getSatisfaction(Accommodation $acco): bool
{
return $this->getSatisfactionWithType($acco, 'Package') || $this->getSatisfactionWithType($acco, 'Accommodation');
}
public function getSatisfactionWithType(Accommodation $acco, string $type): bool
{
$roomRows = $acco->getRoomRows();
$rowCount = count($roomRows);
$tmpRoomRows = $roomRows;
$vacancies = [];
foreach ($roomRows as $roomRow) {
foreach ($roomRow->getProducts() as $product) {
if ($product->getProductType() == $type) {
$idToUse = (int)$product->getServiceId();
if ($product->getAvailabilityReference() === 'Gap') {
$idToUse = $product->getGapId();
} elseif ($product->getAvailabilityReference() === 'Product') {
$idToUse = $product->getProductId();
}
if (!isset($vacancies[$idToUse])) {
$vacancies[$idToUse] = (int)$product->getUnits();
}
}
}
}
foreach ($roomRows as $index => $roomRow) {
if (!$roomRow->getRowObject()) {
return true;
}
$neededVacancies = $roomRow->getRowObject()->getUnits();
$products = $roomRow->getProducts();
$tmpAllProducts = [];
for ($i = $index + 1; $i < $rowCount; $i++) {
$tmpAllProducts += $tmpRoomRows[$i]->getProducts();
}
unset($tmpRoomRows[$index]);
$productDiff = array_diff_key($products, $tmpAllProducts);
$productDiff = array_filter($productDiff, static function ($prod) use ($type) {
return $prod->getProductType() == $type;
});
$tmpAllProducts = array_filter($tmpAllProducts, static function ($prod) use ($type) {
return $prod->getProductType() == $type;
});
$products = array_filter($products, static function ($prod) use ($type) {
return $prod->getProductType() == $type;
});
/**
* @var $key
* @var Product $pDiff
*/
foreach ($productDiff as $key => $pDiff) {
//$neededVacancies = $neededVacancies - $pDiff->getUnits();
$idToUse = 0;
if ($pDiff->getAvailabilityReference() === 'Service') {
$idToUse = $pDiff->getServiceId();
} elseif ($pDiff->getAvailabilityReference() === 'Gap') {
$idToUse = $pDiff->getGapId();
} elseif ($pDiff->getAvailabilityReference() === 'Product') {
$idToUse = $pDiff->getProductId();
}
if(isset($vacancies[$idToUse])) {
$unitsToUse = $vacancies[$idToUse];
if ($neededVacancies < $unitsToUse) {
$vacancies[$idToUse] -= $neededVacancies;
$neededVacancies = 0;
} else {
$vacancies[$idToUse] = 0;
$neededVacancies -= $unitsToUse;
}
}
if ($neededVacancies == 0) {
break;
}
$products[$key] = $pDiff;
}
if ($neededVacancies == 0) {
continue; //row already satisfied
}
// now check intersected Products
$reverseProductDiff = array_intersect_key($tmpAllProducts, $products);
foreach ($reverseProductDiff as $rProduct) {
$useFromProduct = null;
foreach ($tmpRoomRows as $tmpRoomRow) {
$tmpUseFromProducts = $tmpRoomRow->getMaxPossibleVacanciesForProduct($rProduct, $type);
if (($useFromProduct === null || $tmpUseFromProducts < $useFromProduct) && !($tmpUseFromProducts < 0)) { // function returns -1 if products does not exists in row
$useFromProduct = $tmpUseFromProducts;
}
}
if ($useFromProduct == 0) {
continue; //product needed for other rows
}
if ($neededVacancies < $useFromProduct) {
$subtractFromProduct = $neededVacancies;
$neededVacancies = 0;
} else {
$subtractFromProduct = $useFromProduct;
$neededVacancies -= $subtractFromProduct;
}
foreach ($roomRows as $tmpRoomRow) {
$tmpRoomRow->subtractFromProduct($subtractFromProduct, $rProduct);
}
if ($neededVacancies == 0) {
break; //satisfied
}
}
if ($neededVacancies != 0) {
return false; //I can't get no satisfaction
}
}
return true;
}
public function getCacheId(): ?string
{
if ($this->getDc()->getIdGenerator()) {
return $this->getDc()->getIdGenerator()->generateVacancyKey($this->getSearchParameter());
}
return null;
}
protected function getSqlFromPart(): string
{
$dummyAcco = new AccommodationServiceProvider();
$pimcoreDB = $this->getDc()->getUsedPimcoreDb();
$demiDB = $this->getDc()->getVacanciesDb();
return "FROM $pimcoreDB.object_" . $dummyAcco->getClassId() . " dacco
INNER JOIN $demiDB." . $this->getDc()->getOccupancyTableName() . " occupancy ON occupancy.acco_id = dacco.o_id
INNER JOIN $demiDB." . $this->getDc()->getFromtoTableName() . " fromto ON occupancy.id = fromto.occu_id
INNER JOIN $demiDB." . $this->getDc()->getFromtoProductsTableName() . ' p ON fromto.id = p.fromto_id';
}
protected function getCondition(RoomRow $roomrow): string
{
$searchParams = $this->getSearchParameter();
$accoidadd = '';
$prodidadd = '';
$accoids = $searchParams->getAccoIds();
$excludeAccoids = $searchParams->getExcludeAccoIds();
$accoidaddArray = [];
if (!empty($accoids)) {
$ids = [];
foreach ($accoids as $accoid) {
$ids[] = (int)$accoid;
}
$accoidaddArray[] = 'dacco.oo_id IN (' . implode(', ', $ids) . ') ';
}
if (!empty($excludeAccoids)) {
$ids = [];
foreach ($excludeAccoids as $accoid) {
$ids[] = (int)$accoid;
}
$accoidaddArray[] = 'dacco.oo_id NOT IN (' . implode(', ', $ids) . ') ';
}
if (!empty($accoidaddArray)) {
$accoidadd = ' AND ( ' . implode(' AND ', $accoidaddArray) . ' ) ';
}
$preSelectedProductIds = [];
$productIds = (array)$searchParams->getProductIds();
foreach ($productIds as $prodId) {
$preSelectedProductIds[] = (int)$prodId;
}
if (!empty($preSelectedProductIds)) {
$prodidadd = ' AND ';
$prodidadd .= 'p.product_id IN (' . implode(', ', $preSelectedProductIds) . ') ';
}
$arrivalDay = 'arrival' . $searchParams->getDateFrom()->format('D');
$departureDay = 'departure' . $searchParams->getDateTo()->format('D');
$string =
' WHERE
dacco.o_published = 1 AND
occupancy.beds = ? AND
fromto.period >= ?
AND DATEDIFF(?, fromto.fromDate) >= ?
AND DATEDIFF(fromto.toDate, ?) >= ?
AND NOT EXISTS (
SELECT 1
FROM ' . $this->getDc()->getVacanciesDb() . '.' . $this->getDc()->getArrivalTemplateTableName() . ' arrival
WHERE arrival.product_id = p.product_id
AND arrival.gap_id = p.gap_id
AND ( ( ? BETWEEN arrival.dateFrom AND arrival.dateTo
OR ? BETWEEN arrival.dateFrom AND arrival.dateTo
OR arrival.dateFrom BETWEEN ? AND ?
OR arrival.dateTo BETWEEN ? AND ?)
AND ? BETWEEN arrival.daysUntilArrivalFrom
AND arrival.daysUntilArrivalTo
AND ' . $this->getDc()->getDb()?->quote($this->today->format('Y-m-d')) . " >= arrival.validFrom
)
AND (
(
? != arrival.dateFrom
AND ( arrival.minStay > ? OR arrival.maxStay < ?)
AND (
? BETWEEN arrival.dateFrom AND arrival.dateTo
OR arrival.minStayFromArrivalDay = 0
)
)
OR ( ? BETWEEN arrival.dateFrom AND arrival.dateTo
AND ( arrival.$arrivalDay != 1
OR IFNULL((? % arrival.dayInterval), 0) != 0))
OR ( ? BETWEEN arrival.dateFrom AND arrival.dateTo
AND arrival.$departureDay != 1))
)
AND EXISTS (
SELECT 1
FROM " . $this->getDc()->getVacanciesDb() . '.' . $this->getDc()->getArrivalTemplateTableName() . ' arrival
WHERE arrival.product_id = p.product_id
AND arrival.gap_id = p.gap_id
AND ( ( ? BETWEEN arrival.dateFrom AND arrival.dateTo
OR ? BETWEEN arrival.dateFrom AND arrival.dateTo
OR arrival.dateFrom BETWEEN ? AND ?
OR arrival.dateTo BETWEEN ? AND ?)
AND ? BETWEEN arrival.daysUntilArrivalFrom
AND arrival.daysUntilArrivalTo
AND ' . $this->getDc()->getDb()?->quote($this->today->format('Y-m-d')) . ' >= arrival.validFrom
)
)
AND (NOT EXISTS
(SELECT 1
FROM ' . $this->getDc()->getVacanciesDb() . '.' . $this->getDc()->getSalesRulesTemplateTableName() . ' salesrule
WHERE salesrule.product_id = p.product_id
AND ' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . ' >= salesrule.validFrom
AND ( (' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . ' >= salesrule.dateFrom
AND ' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . ' < salesrule.dateTo)
OR (' . $this->getDc()->getDb()?->quote($searchParams->getDateTo()->format('Y-m-d')) . ' > salesrule.dateFrom
AND ' . $this->getDc()->getDb()?->quote($searchParams->getDateTo()->format('Y-m-d')) . ' <= salesrule.dateTo)
OR (salesrule.dateFrom >= ' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . '
AND salesrule.dateFrom < ' . $this->getDc()->getDb()?->quote($searchParams->getDateTo()->format('Y-m-d')) . ')
OR (salesrule.dateTo >= ' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . '
AND salesrule.dateTo < ' . $this->getDc()->getDb()?->quote($searchParams->getDateTo()->format('Y-m-d')) . '))
AND salesrule.offerable = 0)
)
AND (
EXISTS
(SELECT 1
FROM ' . $this->getDc()->getVacanciesDb() . '.' . $this->getDc()->getSalesRulesTemplateTableName() . ' salesrule
WHERE salesrule.product_id = p.product_id
AND ' . $this->getDc()->getDb()?->quote($searchParams->getDateFrom()->format('Y-m-d')) . ' >= salesrule.validFrom
AND ? BETWEEN salesrule.dateFrom
AND salesrule.dateTo
AND DATEDIFF(?, ?) >= salesrule.daysOfferStops
)
)
AND (
(' . $roomrow->getAdults() . ' BETWEEN p.adultsMin AND p.adultsMax)
AND
(' . count($roomrow->getChildAges()) . ' BETWEEN p.childrenMin AND p.childrenMax)
AND
((' . (count($roomrow->getChildAges()) + $roomrow->getAdults()) . ' BETWEEN p.bedsMin AND p.bedsMax) OR (p.bedsMin = 0 AND p.bedsMax = 0))
)
';
$accoType = $roomrow->getAccommodationType();
if (!empty($accoType)) {
if ($accoType == AccommodationInterface::ACCOMMODATION_TYPE_APARTMENT
|| $accoType == AccommodationInterface::ACCOMMODATION_TYPE_CAMPING
|| $accoType == AccommodationInterface::ACCOMMODATION_TYPE_HOTELROOM
) {
$string .= " AND p.accommodationType = '" . $accoType . "' ";
}
}
$pType = $searchParams->getProductType();
if (!empty($pType) && $pType === 'Product') {
$string .= " AND p.productType = '" . AccommodationInterface::PRODUCT_TYPE_PACKAGE . "'";
} elseif (!empty($pType) && $pType == AccommodationInterface::PRODUCT_TYPE_ACCOMMODATION) {
$string .= " AND p.productType = '" . AccommodationInterface::PRODUCT_TYPE_ACCOMMODATION . "'";
}
if (empty($pType) || $pType === 'Package') {
$string .=
"
AND (
(
p.productType != 'Package' OR p.validDateType != 'Period'
)
OR
(
EXISTS (
SELECT 1 from " . $this->getDc()->getVacanciesDb() . '.' . $this->getDc()->getPackagePeriodTableName() . " pperiod
WHERE ('" . $searchParams->getDateFrom()->format('Y-m-d') . "' BETWEEN pperiod.dateFrom AND pperiod.dateTo)
AND
('" . $searchParams->getDateTo()->format('Y-m-d') . "' BETWEEN pperiod.dateFrom AND pperiod.dateTo)
AND
pperiod.product_id = p.product_id
)
)
)";
}
if ($searchParams->getLastAvailabilitiyChangeDays()) {
//$string .= " AND p.changeDate >= ? ";
$string .= ' AND (p.changeDate >= ?
OR ((dacco.noChangeDateCheck = 0 OR dacco.noChangeDateCheck IS NULL) AND UNIX_TIMESTAMP(p.changeDate) >= UNIX_TIMESTAMP(CURRENT_TIMESTAMP - (dacco.changeDateExceptionDays * 24 * 60 * 60)))
OR dacco.noChangeDateCheck = 1
) ';
}
$product = new AccommodationProduct();
$productRelationsTable = 'object_relations_' . $product->getClassId();
if ($searchParams->getSalesChannelId()) {
$string .= ' AND ' . $searchParams->getSalesChannelId() . ' IN
(SELECT dest_id FROM ' . $this->getDc()->getUsedPimcoreDb() . ".$productRelationsTable salesprodrel WHERE salesprodrel.src_id = p.product_id)";
}
$string .= $accoidadd;
$string .= $prodidadd;
return $string;
}
protected function getBindParams(RoomRow $firstRoomConfig): array
{
$searchParams = $this->getSearchParameter();
$period = $searchParams->getPeriod();
//todo include childs
$array = [
$firstRoomConfig->getBeds(),
$period,
$searchParams->getDateTo()?->format('Y-m-d'),
$period,
$searchParams->getDateFrom()?->format('Y-m-d'),
$period,
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
(int)$searchParams->getDaysUntilArrival(),
$searchParams->getDateTo()?->format('Y-m-d'),
$period, //minStay
$period, //maxStay
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$period, //interval
$searchParams->getDateTo()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateTo()?->format('Y-m-d'),
(int)$searchParams->getDaysUntilArrival(),
$searchParams->getDateFrom()?->format('Y-m-d'),
$searchParams->getDateFrom()?->format('Y-m-d'),
$this->today->format('Y-m-d'),
];
if ($searchParams->getLastAvailabilitiyChangeDays()) {
$todayWithTime = new DateTime();
$pastDayWithTime = clone $todayWithTime;
$pastDayWithTime->sub(new DateInterval('P' . ($searchParams->getLastAvailabilitiyChangeDays()) . 'D'));
$array = array_merge($array, [
$pastDayWithTime->format('Y-m-d H:i:s'),
]);
}
return $array;
}
/**
* @param ResultSet $resultSet
*
* @throws Exception
*/
protected function beforePhpSortingAndFiltering(ResultSet $resultSet): void
{
$time1 = (round(microtime(true) * 1000));
if (!$this->getSearchParameter()->isNoDate()) {
$this->handleSalesRules($resultSet);
if (isset($_GET['gimmetime']) && Pimcore::inDebugMode()) {
p_r('salesrules ' . ((round(microtime(true) * 1000)) - $time1));
}
}
$this->handlePrices($resultSet);
if (isset($_GET['gimmetime']) && Pimcore::inDebugMode()) {
p_r('prices ' . ((round(microtime(true) * 1000)) - $time1));
}
}
protected function handlePrices(ResultSet &$resultSet): void
{
$searchParams = $this->getSearchParameter();
if (!$resultSet->getPricesSet()) {
$resultBuilder = $this->getResultBuilder();
if ($searchParams->getDateFrom() && $searchParams->getDateTo()) {
$resultBuilder->preparePriceCalc($resultSet);
$resultSet->setPricesSet(true);
} else {
if ($searchParams->getCalculateStandardPrice()) {
if ($searchParams->getSearchType() == Parameter::SEARCH_TYPE_EACH_PRODUCT_SEPARATED ||
$searchParams->getSearchType() == Parameter::SEARCH_TYPE_ONE_PRODUCT_PER_PACKAGE) {
$resultBuilder->setProductStdPricesForResultSet($resultSet);
$resultBuilder->setStdPricesForResultSet($resultSet, true);
} else {
$resultBuilder->setStdPricesForResultSet($resultSet);
}
$resultSet->setPricesSet(true);
} else {
$resultSet->setPricesSet(false);
}
}
$resultSet->setPricesSet(true);
if (!$this->refilterNeededFromFilters()) {
$this->getDc()->saveToCache($resultSet, $this->getCacheId());
}
}
}
protected function handleSalesRules(ResultSet $resultSet): void
{
$resultbuilder = $this->getResultBuilder();
$resultbuilder->getBookableForResultSet($resultSet);
}
#[Pure]
public function getSortFactoryKeys(): array
{
$keys = [];
foreach ($this->getSortFactories() as $factory) {
$keys[] = $factory->getOrderKey();
}
return $keys;
}
#[Pure]
public function getSortFactoryByKey(string $key): ?Local\SortFactory
{
$obj = null;
foreach ($this->getSortFactories() as $factory) {
if ($factory->getOrderKey() == $key) {
$obj = $factory;
}
}
return $obj;
}
public function sortForPrice(&$accommodations, ?string $orderKey = null): void
{
$searchParams = $this->getSearchParameter();
$orderKeyes = (array) $searchParams->getOrderKey();
$sortFactoryKeys = $this->getSortFactoryKeys();
foreach ($orderKeyes as $orderKey) {
if (in_array($orderKey, $sortFactoryKeys)) {
$obj = $this->getSortFactoryByKey($orderKey);
if (!$obj instanceof SortFactory) {
continue;
}
$obj->sortAccos($accommodations);
} elseif ($orderKey === 'priceAsc') {
$method = 'cmpPrice';
if (!$searchParams->getDateFrom()) { //nodate
$method = 'cmpPriceBase';
}
uasort($accommodations, [$this, $method]);
} elseif ($orderKey === 'priceDesc') {
$method = 'cmpPriceDesc';
if (!$searchParams->getDateFrom()) { //nodate
$method = 'cmpPriceDescBase';
}
uasort($accommodations, [$this, $method]);
} elseif ($orderKey === 'bookable') {
//only for requests with date; noDate requests are handled already via SQL order
if ($searchParams->getDateFrom()) {
uasort($accommodations, [$this, 'cmpBookable']);
}
}
}
if (!$searchParams->isSkipCleaningSortAfterFactory() && $searchParams->getMinPrice() >= 0 && !in_array('nearbySearchAsc', $orderKeyes) && !in_array('nearbySearchDesc', $orderKeyes)) {
$method = 'getMinPrice';
if (!$searchParams->getDateFrom()) {
$method = 'getMinPriceBase';
}
$backArray = [];
$cleanArray = [];
foreach ($accommodations as $accoId => $acco) {
if ($acco->$method() <= $searchParams->getMinPrice()) {
$backArray[$accoId] = $acco;
} else {
$cleanArray[$accoId] = $acco;
}
}
$accommodations = $cleanArray + $backArray;
}
}
public function getAdapterCachePostFix(): string
{
return $this->adapterCachePostFix;
}
/**
* @param ResultSet $resultSet
*
* @return array
*
* @throws Exception
*/
public function splitResultSetIntoChunks($resultSet): array
{
$chunkSize = $this->getDc()->getGearmanChunkSize();
$offset = 0;
$resultSets = [];
while ($accos = $resultSet->getAccommodations($offset, $chunkSize, true)) {
$offset += $chunkSize;
$tmpResultSet = new ResultSet($this->getSearchParameter(), $accos);
$resultSets[] = $tmpResultSet;
}
return $resultSets;
}
/**
* @param ResultSet $resultSet
*
* @return ResultSet
*
* @throws Exception
*
* @deprecated
*
*/
public function launchGearmanPriceCalculation($resultSet): ResultSet
{
$resultSets = $this->splitResultSetIntoChunks($resultSet);
$gmclient = new GearmanClient();
$gmclient->addServer();
$this->workResultPerFunction = [
'localSearch' => 0,
];
$gmclient->setCompleteCallback(function (GearmanTask $task) {
//$functionIndex = $this->workResultPerFunction[$task->functionName()]++;
$data = $task->data();
$info = unserialize($data);
if ($info instanceof Exception) {
$this->failedTasks[$task->functionName() . '_' . $this->workResultPerFunction[$task->functionName()]] = $info->getMessage();
} else {
$this->workResult[$task->functionName() . '_' . $this->workResultPerFunction[$task->functionName()]] = $info;
}
});
$startWorkerCount = 0;
//tasks have to be added AFTER the callbacks for some reason, probably getting their callback only on add/create from the client
foreach ($resultSets as $workload) {
$gmclient->addTask('priceCalculate', serialize($workload));
$startWorkerCount++;
}
//$gmclient->setTimeout(20000);
$startTime = (round(microtime(true) * 1000));
//start a backupworker for each task to ensure enough workers are available
for ($i = 0; $i < $startWorkerCount; $i++) {
exec('nohup php ' . PIMCORE_PROJECT_ROOT . DIRECTORY_SEPARATOR . 'Demi/cli/gearman/priceWorker.php -t2000 > /dev/null 2>> dev/null &');
}
if (isset($_GET['gimmetime']) && Pimcore::inDebugMode()) {
p_r('Starting ' . $startWorkerCount . ' Workers. Time: ' . ((round(microtime(true) * 1000)) - $startTime));
p_r('nohup php ' . PIMCORE_PROJECT_ROOT . DIRECTORY_SEPARATOR . 'Demi/cli/gearman/priceWorker.php -t10000 >/dev/null 2>/dev/null &');
}
$taskTime = (round(microtime(true) * 1000));
$gmclient->runTasks();
if (Pimcore::inDebugMode() && isset($_GET['gimmetime'])) {
p_r('Running tasks took time: ' . ((round(microtime(true) * 1000)) - $taskTime));
}
//build complete result
$completeResultSet = new ResultSet($this->getSearchParameter());
$allAccos = [];
foreach ($this->workResult as $function => $info) {
if (Pimcore::inDebugMode() && isset($_GET['gimmetime'])) {
p_r('Running ' . $function . ' took time: ' . $info['time']);
p_r('RequestTime: ' . $info['requestTime']);
}
/** @var $resultSet ResultSet */
$resultSet = $info['resultSet'];
if (Pimcore::inDebugMode() && isset($_GET['gimmetime'])) {
p_r('count: ' . count($resultSet->getAccommodations()));
p_r('total: ' . $resultSet->getTotalCount());
}
if ($resultSet) {
foreach ($resultSet->getAccommodations() as $accoId => $accoSet) {
$allAccos[$accoId] = $accoSet;
}
}
}
$completeResultSet->setAccommodations($allAccos);
$completeResultSet->setTotalCount(count($allAccos));
$completeResultSet->setPricesSet(true);
return $completeResultSet;
}
/**
* @return SortFactory[]
*/
public function getSortFactories(): array
{
return $this->sortFactories;
}
/**
* @param SortFactory[] $sortFactories
*/
public function setSortFactories($sortFactories)
{
$this->sortFactories = $sortFactories;
}
}