<?php declare(strict_types=1); /* * This file is part of contao-weinanlieferung-bundle. * * (c) vonRotenberg * * @license commercial */ namespace vonRotenberg\WeinanlieferungBundle\Controller\Frontend\Module; use Contao\Controller; use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController; use Contao\CoreBundle\Exception\ResponseException; use Contao\CoreBundle\InsertTag\InsertTagParser; use Contao\CoreBundle\ServiceAnnotation\FrontendModule; use Contao\Date; use Contao\FrontendUser; use Contao\ModuleModel; use Contao\PageModel; use Contao\StringUtil; use Contao\System; use Contao\Template; use Doctrine\DBAL\Connection; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungLeseartModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungRebsorteModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungReservationModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungSlotsModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungStandortModel; use vonRotenberg\WeinanlieferungBundle\Model\WeinanlieferungUnitsModel; /** * @FrontendModule(WeinanlieferungSlotsListModuleController::TYPE, category="miscellaneous") */ class WeinanlieferungSlotsListModuleController extends AbstractFrontendModuleController { public const TYPE = 'wa_slots_list'; private $insertTagParser; private $db; public function __construct(InsertTagParser $insertTagParser, Connection $connection) { $this->insertTagParser = $insertTagParser; $this->db = $connection; } protected function getResponse(Template $template, ModuleModel $model, Request $request): ?Response { global $objPage; Controller::loadDataContainer('tl_vr_wa_slot'); $GLOBALS['TL_CSS']['vr_wa'] = 'bundles/vonrotenbergweinanlieferung/css/frontend.scss|static'; $standortIds = StringUtil::deserialize($model->vr_wa_standortId); $arrData = $template->getData(); $arrOptions = []; // Add filter sql if (!empty($_GET['filter_kapazitaet'])) { $filterRaw = (string) $_GET['filter_kapazitaet']; $arrData['filter']['kapazitaet']['selected'] = $filterRaw; // Support unit-prefixed values like "u1-16" (unit-specific) and legacy "u-16". Both resolve to numeric capacity 16 for SQL. $filterNumeric = null; if (preg_match('/^u\d+-(\d+)$/', $filterRaw, $m)) { // New format: u<unitId>-<capacity> $filterNumeric = (int) $m[1]; } elseif (preg_match('/^u-(\d+)$/', $filterRaw, $m)) { // Legacy format kept for backward compatibility: u-<capacity> $filterNumeric = (int) $m[1]; } else { $filterNumeric = (int) $filterRaw; } $arrOptions['column'][] = '(SELECT tl_vr_wa_slot.behaelter - IFNULL(SUM(tl_vr_wa_reservation.behaelter),0) FROM tl_vr_wa_reservation WHERE tl_vr_wa_reservation.pid = tl_vr_wa_slot.id) >= ?'; $arrOptions['value'][] = $filterNumeric; } if (!empty($_GET['filter_standort'])) { $arrData['filter']['standort']['selected'] = $_GET['filter_standort']; $arrOptions['column'][] = 'pid = ?'; $arrOptions['value'][] = $_GET['filter_standort']; } if (!empty($_GET['filter_tag']) && ($Tag = new Date($_GET['filter_tag']))) { $arrData['filter']['tag']['selected'] = $_GET['filter_tag']; $arrOptions['column'][] = 'tl_vr_wa_slot.time BETWEEN ? and ?'; $arrOptions['value'][] = $Tag->dayBegin; $arrOptions['value'][] = $Tag->dayEnd; } if (!empty($_GET['filter_ernteart'])) { $arrData['filter']['ernteart']['selected'] = $_GET['filter_ernteart']; $arrOptions['column'][] = 'FIND_IN_SET (?,ernteart)'; $arrOptions['value'][] = $_GET['filter_ernteart']; } if (!empty($_GET['filter_sorte']) && !empty($_GET['filter_leseart'])) { $arrData['filter']['sorte']['selected'] = $_GET['filter_sorte']; $arrData['filter']['leseart']['selected'] = $_GET['filter_leseart']; $arrOptions['column'][] = "sorten REGEXP('\"sorte\";s:[0-9]+:\"" . intval($_GET['filter_sorte']) . "\"[^\}]+\"leseart\";s:[0-9]+:\"" . intval($_GET['filter_leseart']) . "\"')"; } else { if (!empty($_GET['filter_sorte'])) { $arrData['filter']['sorte']['selected'] = $_GET['filter_sorte']; $arrOptions['column'][] = "sorten REGEXP('\"sorte\";s:[0-9]+:\"" . intval($_GET['filter_sorte']) . "\"')"; } if (!empty($_GET['filter_leseart'])) { $arrData['filter']['leseart']['selected'] = $_GET['filter_leseart']; $arrOptions['column'][] = "sorten REGEXP('\"leseart\";s:[0-9]+:\"" . intval($_GET['filter_leseart']) . "\"')"; } } // Get available slots if (($slots = WeinanlieferungSlotsModel::findMultiplePublishedByPids($standortIds,$arrOptions)) !== null) { $slotIds = $slots->fetchEach('id'); /** @var WeinanlieferungSlotsModel $slot */ foreach ($slots as $slot) { $day = new Date($slot->date); $arrSorten = []; $arrErnteart = []; $intAvailableBehaelter = max(0, $slot->getAvailableBehaelter()); $Sorten = StringUtil::deserialize($slot->sorten, true); foreach ($Sorten as $sorte) { $objSorte = WeinanlieferungRebsorteModel::findByPk($sorte['sorte']); $objLeseart = WeinanlieferungLeseartModel::findByPk($sorte['leseart']); $arrSorten[] = ($objSorte !== null ? $objSorte->title : '') . ' ' . ($objLeseart !== null ? $objLeseart->title : ''); } if ($slot->ernteart !== null) { foreach (explode(',', $slot->ernteart) as $ernteart) { $arrErnteart[] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart; } } $strStandort = ''; if (($Standort = $slot->getRelated('pid')) !== null) { $strStandort = $Standort->title; } $arrLage = []; if (($Lage = $slot->getRelated('lage')) !== null) { $arrLage = $Lage->fetchEach('title'); } $arrData['days'][$day->dayBegin][] = array_merge($slot->row(), [ 'anmerkungen' => $slot->anmerkungen ? StringUtil::substr(strip_tags($slot->anmerkungen), 110) : '', 'standort' => $strStandort, 'lage' => $arrLage, 'sorte' => $arrSorten, 'ernteart' => $arrErnteart, 'behaelterAvailable' => $intAvailableBehaelter, 'buchbar' => (boolean)$intAvailableBehaelter, 'gebucht' => (boolean)WeinanlieferungReservationModel::countBy(["uid = ?", "pid = ?"], [FrontendUser::getInstance()->id, $slot->id]) ]); } // Get filter values $result = $this->db->executeQuery("SELECT MAX(behaelter) as 'kapazitaet' FROM tl_vr_wa_slot WHERE id IN (" . implode(',', $slotIds) . ")"); $intMaxKapazitaet = (int)$result->fetchOne(); // Build capacity filter options as an ordered list where each item has a numeric value and a label. // Do not combine unit labels with numeric labels; append unit options at the bottom. $options = []; $max = max(1, $intMaxKapazitaet); // Plain numeric options first for ($i = 1; $i <= $max; $i++) { $options[] = [ 'value' => (string) $i, 'label' => (string) $i, ]; } // Determine which units are enabled on any of the shown standorts (locations) $allowedUnitIds = []; $shownStandortIds = []; if (!empty($slotIds)) { // Get unique Standort IDs from the shown slots $shownStandortIds = array_unique($slots->fetchEach('pid')); if (!empty($shownStandortIds)) { if (($ShownStandorte = WeinanlieferungStandortModel::findBy(["id IN (" . implode(',', $shownStandortIds) . ")"],null)) !== null) { foreach ($ShownStandorte as $ShownStandort) { $ids = \Contao\StringUtil::deserialize($ShownStandort->units, true); if (!empty($ids)) { foreach ($ids as $uid) { $allowedUnitIds[(int)$uid] = true; } } } } } } // Append unit options at the bottom, without exceeding max capacity and only for allowed units if (!empty($allowedUnitIds) && ($Units = WeinanlieferungUnitsModel::findAll()) !== null) { foreach ($Units as $unit) { if (empty($allowedUnitIds[(int)$unit->id])) { continue; // this unit is not enabled on any shown standort } $unitContainers = (int) $unit->containers; if ($unitContainers <= 0) { continue; } $unitName = $unit->description ?: $unit->title; // visible unit name $multiplierMax = intdiv($max, $unitContainers); for ($m = 1; $m <= $multiplierMax; $m++) { $value = (string) ($m * $unitContainers); $label = $m . ' ' . $unitName; $options[] = [ 'value' => 'u' . $unit->id . '-' . $value, 'label' => $label, ]; } } } $arrData['filter']['kapazitaet']['options'] = $options; if (($Standorte = WeinanlieferungStandortModel::findBy(["id IN (" . implode(',', $standortIds) . ")", "id IN (SELECT tl_vr_wa_slot.pid FROM tl_vr_wa_slot WHERE tl_vr_wa_slot.id IN (" . implode(',', $slotIds) . "))"], null, ['order' => 'title ASC'])) !== null) { $arrData['filter']['standort']['options'] = array_combine($Standorte->fetchEach('id'), $Standorte->fetchEach('title')); } // $slots->reset(); if (($slotsUnfiltered = WeinanlieferungSlotsModel::findMultiplePublishedByPids($standortIds)) !== null) { foreach ($slotsUnfiltered as $slot) { $Tag = new Date($slot->time); $arrData['filter']['tag']['options'][$Tag->dayBegin] = Date::parse(Date::getNumericDateFormat(), $Tag->dayBegin); } } /*if (isset($GLOBALS['TL_DCA']['tl_vr_wa_slot']['fields']['ernteart']['options'])) { foreach ($GLOBALS['TL_DCA']['tl_vr_wa_slot']['fields']['ernteart']['options'] as $ernteart) { $arrData['filter']['ernteart']['options'][$ernteart] = $GLOBALS['TL_LANG']['REF']['wa_ernteart'][$ernteart] ?? $ernteart; } } if (($Sorten = WeinanlieferungRebsorteModel::findAll(['order'=>'title ASC'])) !== null) { $arrData['filter']['sorte']['options'] = array_combine($Sorten->fetchEach('id'),$Sorten->fetchEach('title')); } if (($Leseart = WeinanlieferungLeseartModel::findAll(['order'=>'title ASC'])) !== null) { $arrData['filter']['leseart']['options'] = array_combine($Leseart->fetchEach('id'),$Leseart->fetchEach('title')); }*/ } // Add page URL if ($objPage instanceof PageModel) { $arrData['pageUrl'] = $objPage->getFrontendUrl(); } $template->setData($arrData); // Handle ajax if ($request->headers->get('VR-Ajax') == 'WaSlotsModule') { throw new ResponseException(new Response($this->insertTagParser->replace($template->parse()))); } return $template->getResponse(); } }