feat: base travel info on product and provide date via url

This commit is contained in:
Björn Fromme
2024-11-12 15:15:53 +01:00
parent 71b23c5945
commit f46b9c31c4
9 changed files with 156 additions and 33 deletions
+11 -1
View File
@@ -173,16 +173,26 @@ routeEnhancers:
- 2712
routes:
-
routePath: '/{travelinfo}'
routePath: '/{travelinfo}/{traveldate}'
_controller: 'Travelinfo::index'
_arguments:
travelinfo: travelinfo
traveldate: travelDate
-
routePath: '/{busproid}'
_controller: 'Travelinfo::myep'
_arguments:
busproid: busProId
defaultController: 'Travelinfo::index'
aspects:
travelinfo:
type: PersistedAliasMapper
tableName: tx_epproducts_domain_model_travelinfo
routeFieldName: path_segment
traveldate:
type: PassthroughMapper
busproid:
type: PassthroughMapper
ContingentsIcalPlugin:
type: Extbase
extension: EpProducts
@@ -2,8 +2,10 @@
namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Date;
use EP\EpProducts\Domain\Model\Travelinfo;
use EP\EpProducts\Domain\Repository\DateRepository;
use EP\EpProducts\Domain\Repository\TravelInfoRepository;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -16,24 +18,49 @@ class TravelinfoController extends ActionController
*/
private $dateRepository;
public function __construct(DateRepository $dateRepository)
/**
* @var TravelInfoRepository
*/
private $travelInfoRepository;
public function __construct(DateRepository $dateRepository, TravelInfoRepository $travelInfoRepository)
{
$this->dateRepository = $dateRepository;
$this->travelInfoRepository = $travelInfoRepository;
}
public function indexAction(Travelinfo $travelinfo)
public function myepAction(int $busProId)
{
$today = new \DateTimeImmutable();
$date = $this->dateRepository->findOneByCode($travelinfo->getTravelCode());
/** @var Date $date */
$date = $this->dateRepository->findOneByBusProId($busProId);
$travelinfo = $this->travelInfoRepository->findOneByProduct($date->getProduct());
if (null === $date || $date->getDateEnd() < $today) {
if (null === $travelinfo) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'],
'Reiseinformationen nicht gefunden oder Termin abgelaufen'
'Reiseinformationen nicht gefunden'
);
throw new ImmediateResponseException($response, 1731071856);
}
$this->redirect(
'index',
null,
null,
[
'travelinfo' => $travelinfo,
'travelDate' => $date->getDateStart()->format('Y-m-d'),
]
);
}
public function indexAction(Travelinfo $travelinfo, string $travelDate)
{
$product = $travelinfo->getProduct();
/** @var Date $date */
$date = $this->dateRepository->findForProductAndDate($product, $travelDate)->getFirst();
$this->view->assign('travelinfo', $travelinfo);
$this->view->assign('date', $date);
}
@@ -79,6 +79,16 @@ class Travelinfo extends AbstractEntity
*/
protected $travelCode;
/**
* @var Product
*/
protected $product;
/**
* @var bool
*/
protected $hideAddress = false;
public function __construct()
{
$this->initStorageObjects();
@@ -259,5 +269,27 @@ class Travelinfo extends AbstractEntity
return $this;
}
public function getProduct()
{
return $this->product;
}
public function setProduct($product)
{
$this->product = $product;
return $this;
}
public function isHideAddress()
{
return $this->hideAddress;
}
public function setHideAddress($hideAddress)
{
$this->hideAddress = $hideAddress;
return $this;
}
}
@@ -2,5 +2,20 @@
namespace EP\EpProducts\Domain\Repository;
use EP\EpProducts\Domain\Model\Product;
class DateRepository extends AbstractRepository
{}
{
public function findForProductAndDate(Product $product, string $date)
{
$query = $this->createQuery();
return $query
->matching($query->logicalAnd([
$query->equals('product', $product),
$query->equals('dateStart', $date),
]))
->execute()
;
}
}
@@ -0,0 +1,6 @@
<?php
namespace EP\EpProducts\Domain\Repository;
class TravelInfoRepository extends AbstractRepository
{}
@@ -27,7 +27,8 @@ return [
],
],
'palettes' => [
'basics' => ['showitem' => 'title,path_segment,--linebreak--,travel_code,--linebreak--,links,--linebreak--,teasers'],
'basics' => ['showitem' => 'title,path_segment,--linebreak--,product,hide_address,--linebreak--,links,
--linebreak--,teasers'],
'text' => ['showitem' => 'subline,headline,--linebreak--,intro_text,--linebreak--,footer_text,--linebreak--,
self_arranged_text,--linebreak--,additional_info,--linebreak--,important_phone_numbers,--linebreak--,
important_links'],
@@ -190,13 +191,39 @@ return [
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
),
],
'travel_code' => [
'product' => [
'exclude' => false,
'label' => 'Reisecode des Termins',
'label' => 'Produkt',
'config' => [
'type' => 'input',
'size' => 30,
'eval' => 'trim,required'
'type' => 'group',
'internal_type' => 'db',
'allowed' => 'tx_epproducts_domain_model_product',
'minitems' => 1,
'maxitems' => 1,
'size' => 1,
'fieldControl' => [
'elementBrowser' => [
'disabled' => true,
],
],
'fieldWizard' => [
'recordsOverview' => [
'disabled' => true,
],
],
'suggestOptions' => [
'default' => [
'additionalSearchFields' => 'code,name,keywords',
'orderBy' => 'code',
],
],
],
],
'hide_address' => [
'exclude' => 0,
'label' => 'Adresse ausblenden',
'config' => [
'type' => 'check',
],
],
'intro_text' => [
@@ -252,7 +252,7 @@ $boot = function () {
'EP.ep_products',
'travelinfo',
[
'Travelinfo' => 'index',
'Travelinfo' => 'index,myep',
]
);
@@ -1370,6 +1370,8 @@ CREATE TABLE tx_epproducts_domain_model_travelinfo
footer_image int(11) DEFAULT '0' NOT NULL,
links int(11) DEFAULT '0' NOT NULL,
teasers int(11) DEFAULT '0' NOT NULL,
product int(11) unsigned DEFAULT '0',
hide_address tinyint(4) unsigned DEFAULT '0' NOT NULL,
travel_code varchar(255) DEFAULT '' NOT NULL,
path_segment varchar(255) DEFAULT '' NOT NULL,
@@ -12,8 +12,8 @@
alt="Reiseinformationen"
class="block w-full h-full object-cover object-center" />
</div>
<div class="absolute top-16 left-1/2 -translate-x-1/2 w-full max-w-screen-sm lg:left-0 lg:-translate-x-0 lg:max-w-none px-8 lg:px-24 h-64 lg:h-128 flex flex-col justify-between">
<div class="hidden lg:block text-3xl lg:text-6xl text-white uppercase font-bold">
<div class="absolute top-8 lg:top-16 left-1/2 -translate-x-1/2 w-full max-w-screen-sm lg:left-0 lg:-translate-x-0 lg:max-w-none px-8 lg:px-24 h-64 lg:h-128 flex flex-col justify-between">
<div class="text-2xl pb-4 lg:pb-0 md:text-3xl lg:text-6xl text-ep-secondary uppercase font-bold">
Reiseinformation
</div>
<div class="grid grid-cols-2 lg:grid-cols-4">
@@ -26,18 +26,18 @@
data-controller="scroll-to"
data-scroll-to-target-value="goodtoknow"
data-action="scroll-to#scroll"
class="flex flex-col space-y-4 items-center justify-center px-8 py-8 lg:py-16 text-white {bgColors.{linksCount}}">
<div class="w-12 h-12 lg:w-24 lg:h-24">
class="flex flex-col space-y-4 items-center justify-center px-4 py-4 lg:py-16 text-white {bgColors.{linksCount}}">
<div class="w-10 h-10 lg:w-24 lg:h-24">
<f:render section="Icon" arguments="{icon: 'chat'}" />
</div>
<div class="uppercase text-lg lg:text-xl text-center leading-tight">
<div class="uppercase lg:text-xl text-center leading-tight">
Wissens&shy;wertes
</div>
</a>
</div>
</div>
</div>
<div class="pb-32 pt-52 -mt-24 bg-ep-primary-bg">
<div class="pb-32 pt-32 lg:pt-52 -mt-24 bg-ep-primary-bg">
<div class="container px-8 lg:px-24" data-rte-content>
<h1 class="headline--underlined headline--has-subline">
<span class="subline">{travelinfo.subline}</span>
@@ -60,14 +60,18 @@
</div>
</div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
<h3 class="text-white">
Adresse
</h3>
<p>
{date.hotel.name}
<br>
{date.hotel.address -> f:format.nl2br()}
</p>
<f:if condition="{travelinfo.hideAddress}">
<f:else>
<h3 class="text-white">
Adresse
</h3>
<p>
{date.hotel.name}
<br>
{date.hotel.address -> f:format.nl2br()}
</p>
</f:else>
</f:if>
<f:if condition="{date.pickups -> f:count()} > 0">
<h3 class="text-white">
Bus-Zustiege
@@ -87,7 +91,7 @@
<f:for each="{date.pickups}" as="pickup">
<tr class="odd:bg-zinc-50 align-top">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
{pickup.city}<br><span class="text-xs">{pickup.street} {f:if(condition: pickup.price, then: ', Aufpreis {pickup.price -> f:format.currency(currencySign: \'€\')}')}</span>
{pickup.city}<br><span class="text-xs">{pickup.street}</span>
</td>
<td class="border-r border-zinc-200 px-2 py-1 md:p-2">
{pickup.date}
@@ -205,11 +209,11 @@
</f:section>
<f:section name="Link">
<f:link.typolink parameter="{item.link}" class="flex flex-col space-y-4 items-center justify-center px-8 py-8 lg:py-16 text-white {color}">
<div class="w-12 h-12 lg:w-24 lg:h-24">
<f:link.typolink parameter="{item.link}" class="flex flex-col space-y-4 items-center justify-center px-4 py-4 lg:py-16 text-white {color}">
<div class="w-10 h-10 lg:w-24 lg:h-24">
<f:render section="Icon" arguments="{icon: item.icon}" />
</div>
<div class="uppercase text-lg lg:text-xl text-center leading-tight">
<div class="uppercase lg:text-xl text-center leading-tight">
{item.title -> f:format.raw()}
</div>
</f:link.typolink>