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 - 2712
routes: routes:
- -
routePath: '/{travelinfo}' routePath: '/{travelinfo}/{traveldate}'
_controller: 'Travelinfo::index' _controller: 'Travelinfo::index'
_arguments: _arguments:
travelinfo: travelinfo travelinfo: travelinfo
traveldate: travelDate
-
routePath: '/{busproid}'
_controller: 'Travelinfo::myep'
_arguments:
busproid: busProId
defaultController: 'Travelinfo::index' defaultController: 'Travelinfo::index'
aspects: aspects:
travelinfo: travelinfo:
type: PersistedAliasMapper type: PersistedAliasMapper
tableName: tx_epproducts_domain_model_travelinfo tableName: tx_epproducts_domain_model_travelinfo
routeFieldName: path_segment routeFieldName: path_segment
traveldate:
type: PassthroughMapper
busproid:
type: PassthroughMapper
ContingentsIcalPlugin: ContingentsIcalPlugin:
type: Extbase type: Extbase
extension: EpProducts extension: EpProducts
@@ -2,8 +2,10 @@
namespace EP\EpProducts\Controller; namespace EP\EpProducts\Controller;
use EP\EpProducts\Domain\Model\Date;
use EP\EpProducts\Domain\Model\Travelinfo; use EP\EpProducts\Domain\Model\Travelinfo;
use EP\EpProducts\Domain\Repository\DateRepository; use EP\EpProducts\Domain\Repository\DateRepository;
use EP\EpProducts\Domain\Repository\TravelInfoRepository;
use TYPO3\CMS\Core\Http\ImmediateResponseException; use TYPO3\CMS\Core\Http\ImmediateResponseException;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
@@ -16,24 +18,49 @@ class TravelinfoController extends ActionController
*/ */
private $dateRepository; private $dateRepository;
public function __construct(DateRepository $dateRepository) /**
* @var TravelInfoRepository
*/
private $travelInfoRepository;
public function __construct(DateRepository $dateRepository, TravelInfoRepository $travelInfoRepository)
{ {
$this->dateRepository = $dateRepository; $this->dateRepository = $dateRepository;
$this->travelInfoRepository = $travelInfoRepository;
} }
public function indexAction(Travelinfo $travelinfo) public function myepAction(int $busProId)
{ {
$today = new \DateTimeImmutable(); /** @var Date $date */
$date = $this->dateRepository->findOneByCode($travelinfo->getTravelCode()); $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( $response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$GLOBALS['TYPO3_REQUEST'], $GLOBALS['TYPO3_REQUEST'],
'Reiseinformationen nicht gefunden oder Termin abgelaufen' 'Reiseinformationen nicht gefunden'
); );
throw new ImmediateResponseException($response, 1731071856); 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('travelinfo', $travelinfo);
$this->view->assign('date', $date); $this->view->assign('date', $date);
} }
@@ -79,6 +79,16 @@ class Travelinfo extends AbstractEntity
*/ */
protected $travelCode; protected $travelCode;
/**
* @var Product
*/
protected $product;
/**
* @var bool
*/
protected $hideAddress = false;
public function __construct() public function __construct()
{ {
$this->initStorageObjects(); $this->initStorageObjects();
@@ -259,5 +269,27 @@ class Travelinfo extends AbstractEntity
return $this; 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; namespace EP\EpProducts\Domain\Repository;
use EP\EpProducts\Domain\Model\Product;
class DateRepository extends AbstractRepository 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' => [ '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--, 'text' => ['showitem' => 'subline,headline,--linebreak--,intro_text,--linebreak--,footer_text,--linebreak--,
self_arranged_text,--linebreak--,additional_info,--linebreak--,important_phone_numbers,--linebreak--, self_arranged_text,--linebreak--,additional_info,--linebreak--,important_phone_numbers,--linebreak--,
important_links'], important_links'],
@@ -190,13 +191,39 @@ return [
$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
), ),
], ],
'travel_code' => [ 'product' => [
'exclude' => false, 'exclude' => false,
'label' => 'Reisecode des Termins', 'label' => 'Produkt',
'config' => [ 'config' => [
'type' => 'input', 'type' => 'group',
'size' => 30, 'internal_type' => 'db',
'eval' => 'trim,required' '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' => [ 'intro_text' => [
@@ -252,7 +252,7 @@ $boot = function () {
'EP.ep_products', 'EP.ep_products',
'travelinfo', '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, footer_image int(11) DEFAULT '0' NOT NULL,
links int(11) DEFAULT '0' NOT NULL, links int(11) DEFAULT '0' NOT NULL,
teasers 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, travel_code varchar(255) DEFAULT '' NOT NULL,
path_segment varchar(255) DEFAULT '' NOT NULL, path_segment varchar(255) DEFAULT '' NOT NULL,
@@ -12,8 +12,8 @@
alt="Reiseinformationen" alt="Reiseinformationen"
class="block w-full h-full object-cover object-center" /> class="block w-full h-full object-cover object-center" />
</div> </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="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="hidden lg:block text-3xl lg:text-6xl text-white uppercase font-bold"> <div class="text-2xl pb-4 lg:pb-0 md:text-3xl lg:text-6xl text-ep-secondary uppercase font-bold">
Reiseinformation Reiseinformation
</div> </div>
<div class="grid grid-cols-2 lg:grid-cols-4"> <div class="grid grid-cols-2 lg:grid-cols-4">
@@ -26,18 +26,18 @@
data-controller="scroll-to" data-controller="scroll-to"
data-scroll-to-target-value="goodtoknow" data-scroll-to-target-value="goodtoknow"
data-action="scroll-to#scroll" 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}}"> 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-12 h-12 lg:w-24 lg:h-24"> <div class="w-10 h-10 lg:w-24 lg:h-24">
<f:render section="Icon" arguments="{icon: 'chat'}" /> <f:render section="Icon" arguments="{icon: 'chat'}" />
</div> </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 Wissens&shy;wertes
</div> </div>
</a> </a>
</div> </div>
</div> </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> <div class="container px-8 lg:px-24" data-rte-content>
<h1 class="headline--underlined headline--has-subline"> <h1 class="headline--underlined headline--has-subline">
<span class="subline">{travelinfo.subline}</span> <span class="subline">{travelinfo.subline}</span>
@@ -60,6 +60,8 @@
</div> </div>
</div> </div>
<div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content> <div class="lg:w-3/4 pt-8 lg:pt-0 lg:px-8 text-white" data-rte-content>
<f:if condition="{travelinfo.hideAddress}">
<f:else>
<h3 class="text-white"> <h3 class="text-white">
Adresse Adresse
</h3> </h3>
@@ -68,6 +70,8 @@
<br> <br>
{date.hotel.address -> f:format.nl2br()} {date.hotel.address -> f:format.nl2br()}
</p> </p>
</f:else>
</f:if>
<f:if condition="{date.pickups -> f:count()} > 0"> <f:if condition="{date.pickups -> f:count()} > 0">
<h3 class="text-white"> <h3 class="text-white">
Bus-Zustiege Bus-Zustiege
@@ -87,7 +91,7 @@
<f:for each="{date.pickups}" as="pickup"> <f:for each="{date.pickups}" as="pickup">
<tr class="odd:bg-zinc-50 align-top"> <tr class="odd:bg-zinc-50 align-top">
<td class="border-r border-zinc-200 px-2 py-1 md:p-2"> <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>
<td class="border-r border-zinc-200 px-2 py-1 md:p-2"> <td class="border-r border-zinc-200 px-2 py-1 md:p-2">
{pickup.date} {pickup.date}
@@ -205,11 +209,11 @@
</f:section> </f:section>
<f:section name="Link"> <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}"> <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-12 h-12 lg:w-24 lg:h-24"> <div class="w-10 h-10 lg:w-24 lg:h-24">
<f:render section="Icon" arguments="{icon: item.icon}" /> <f:render section="Icon" arguments="{icon: item.icon}" />
</div> </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()} {item.title -> f:format.raw()}
</div> </div>
</f:link.typolink> </f:link.typolink>