<?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();
}
}