From 2da2162ca9617cafa50962d91dba52fc176e2d71 Mon Sep 17 00:00:00 2001 From: Daniel Gohlke Date: Sat, 8 Mar 2025 22:11:53 +0100 Subject: [PATCH 1/4] [TASK] Make extension compatible to TYPO3 v13.4 Relates: #87 --- .github/workflows/ci.yaml | 20 +- .gitlab-ci.yml | 25 +- Classes/Controller/EventController.php | 43 +--- Classes/Controller/EventDateController.php | 3 +- Classes/Domain/Model/AbstractEventDate.php | 13 +- Classes/Domain/Model/Cart/ProductFactory.php | 171 +++++++++++++ .../Model/Cart/ProductFactoryInterface.php | 17 ++ Classes/Domain/Model/Event.php | 9 +- Classes/Domain/Model/EventDate.php | 12 + Classes/Domain/Model/PriceCategory.php | 5 + .../Domain/Repository/EventDateRepository.php | 46 +--- Classes/Domain/Repository/EventRepository.php | 4 +- .../CheckProductAvailability.php | 8 +- .../RetrieveProductsFromRequest.php | 228 ++---------------- Classes/Exception/NotBookableException.php | 5 + .../ExtcodeCartEventsCTypeMigration.php | 41 ++++ Configuration/FlexForms/EventDatesPlugin.xml | 4 +- Configuration/FlexForms/ListEventsPlugin.xml | 6 +- Configuration/FlexForms/SingleEventPlugin.xml | 1 - .../FlexForms/TeaserEventsPlugin.xml | 5 +- Configuration/TCA/Overrides/tt_content.php | 40 +-- .../tx_cartevents_domain_model_event.php | 3 +- .../TCA/tx_cartevents_domain_model_event.php | 94 +------- .../tx_cartevents_domain_model_eventdate.php | 94 +------- .../TSconfig/ContentElementWizard.tsconfig | 42 +--- Configuration/page.tsconfig | 3 + README.md | 26 +- Resources/Private/Language/locallang.xlf | 8 +- Resources/Public/JavaScripts/cart_events.js | 88 ++++--- Tests/Acceptance/AddEventDateToCartCest.php | 128 +++++++++- Tests/Acceptance/EventListCest.php | 104 +++++++- Tests/Acceptance/Support/Environment.php | 2 +- Tests/Fixtures/ContentDatabase.php | 6 +- Tests/Fixtures/EventsDatabase.php | 70 ++++++ .../Domain/Model/Cart/ProductFactoryTest.php | 214 ++++++++++++++++ .../Repository/EventDateRepositoryTest.php | 172 ++++++++++++- .../Domain/Repository/EventRepositoryTest.php | 2 +- composer.json | 20 +- ext_emconf.php | 4 +- ext_localconf.php | 22 +- rector.php | 8 +- shell.nix | 5 +- 42 files changed, 1144 insertions(+), 677 deletions(-) create mode 100644 Classes/Domain/Model/Cart/ProductFactory.php create mode 100644 Classes/Domain/Model/Cart/ProductFactoryInterface.php create mode 100644 Classes/Exception/NotBookableException.php create mode 100644 Classes/Updates/ExtcodeCartEventsCTypeMigration.php create mode 100644 Configuration/page.tsconfig create mode 100644 Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cc8346eb..8c3c986f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,7 +15,6 @@ jobs: strategy: matrix: php-version: - - 8.1 - 8.2 - 8.3 - 8.4 @@ -54,7 +53,7 @@ jobs: - name: Install PHP uses: shivammathur/setup-php@v2 with: - php-version: "8.1" + php-version: "8.2" tools: composer:v2 - name: Install dependencies @@ -70,14 +69,12 @@ jobs: strategy: matrix: include: - - php-version: '8.1' - typo3-version: '^12.4' - php-version: '8.2' - typo3-version: '^12.4' + typo3-version: '^13.4' - php-version: '8.3' - typo3-version: '^12.4' + typo3-version: '^13.4' - php-version: '8.4' - typo3-version: '^12.4' + typo3-version: '^13.4' steps: - uses: actions/checkout@v4 @@ -112,9 +109,6 @@ jobs: trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= fossar.cachix.org-1:Zv6FuqIboeHPWQS7ysLCJ7UT7xExb4OE8c4LyGb5AsE= substituters = https://cache.nixos.org/ https://fossar.cachix.org - - name: Run Unit Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-unit - - name: Run Unit Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-unit @@ -124,9 +118,6 @@ jobs: - name: Run Unit Tests PHP8.4 run: nix-shell --arg phpVersion \"php84\" --pure --run project-test-unit - - name: Run Functional Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-functional - - name: Run Functional Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-functional @@ -150,9 +141,6 @@ jobs: trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= fossar.cachix.org-1:Zv6FuqIboeHPWQS7ysLCJ7UT7xExb4OE8c4LyGb5AsE= substituters = https://cache.nixos.org/ https://fossar.cachix.org - - name: Run Acceptance Tests PHP8.1 - run: nix-shell --arg phpVersion \"php81\" --pure --run project-test-acceptance - - name: Run Acceptance Tests PHP8.2 run: nix-shell --arg phpVersion \"php82\" --pure --run project-test-acceptance diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29082ec5..e4864d52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ cache: - .php_cs.cache variables: - TYPO3_VERSION: ^12.4 + TYPO3_VERSION: ^13.4 before_script: - apk add git --update @@ -39,11 +39,6 @@ lint:yaml: script: - find *.php Classes Configuration Tests -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l -lint:php81: - <<: *lint_php - variables: - CONTAINER_IMAGE: php:8.1-alpine - lint:php82: <<: *lint_php variables: @@ -60,11 +55,11 @@ lint:php84: CONTAINER_IMAGE: php:8.4-alpine phpstan:analyse: - image: $CI_REGISTRY/containers/phpunit-with-php-8.1:main + image: $CI_REGISTRY/containers/phpunit-with-php-8.2:main stage: lint before_script: - sed -i -e "s#ssh://git@code.extco.de:22722#https://gitlab-ci-token:$CI_JOB_TOKEN@code.extco.de#g" composer.json - - composer config platform.php 8.1 + - composer config platform.php 8.2 - composer install --no-progress --no-ansi --no-interaction script: - vendor/bin/codecept build @@ -83,12 +78,7 @@ phpstan:analyse: - composer require typo3/cms-core="${TYPO3_VERSION}" script: - vendor/bin/phpunit -c Build/UnitTests.xml - - typo3DatabaseDriver=pdo_sqlite vendor/bin/phpunit -c Build/FunctionalTests.xml - -test:php81: - <<: *test_php - variables: - CONTAINER_IMAGE: $CI_REGISTRY/containers/phpunit-with-php-8.1:main + - typo3DatabaseDriver=pdo_sqlite vendor/bin/phpunit -d memory_limit=256M -c Build/FunctionalTests.xml test:php82: <<: *test_php @@ -129,15 +119,8 @@ test:php84: expire_in: 1 day when: always -codeception:php81: - <<: *test_codeception - variables: - CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.1:main - codeception:php82: <<: *test_codeception - needs: - - codeception:php81 variables: CONTAINER_IMAGE: $CI_REGISTRY/containers/codeception-with-php-8.2:main diff --git a/Classes/Controller/EventController.php b/Classes/Controller/EventController.php index 12ae8688..b0471215 100644 --- a/Classes/Controller/EventController.php +++ b/Classes/Controller/EventController.php @@ -28,9 +28,8 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Http\ForwardResponse; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface; -class EventController extends ActionController +final class EventController extends ActionController { private Cart $cart; @@ -118,6 +117,10 @@ public function showAction(?Event $event = null): ResponseInterface #[IgnoreValidation(['value' => 'priceCategory'])] public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCategory = null): ResponseInterface { + if (class_exists(\TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class) === false) { + throw new \BadFunctionCallException('This action requires the installation of typo3/cms-form.'); + } + if (!$eventDate) { $arguments = $this->request->getArguments(); foreach ($arguments as $argumentKey => $argumentValue) { @@ -133,7 +136,7 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa $formDefinition = $eventDate->getEvent()->getFormDefinition(); $formPersistenceManager = GeneralUtility::makeInstance( - FormPersistenceManagerInterface::class + \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class ); $form = $formPersistenceManager->load($formDefinition); @@ -195,28 +198,7 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa return $this->htmlResponse(); } - protected function getEvent(): ?Event - { - $eventUid = 0; - - if ((int)$GLOBALS['TSFE']->page['doktype'] == 186) { - $eventUid = (int)$GLOBALS['TSFE']->page['cart_events_event']; - } - - if ($eventUid > 0) { - $event = $this->eventRepository->findByUid($eventUid); - if ($event && $event instanceof Event) { - return $event; - } - } - - return null; - } - - /** - * Create the demand object which define which records will get shown - */ - protected function createDemandObjectFromSettings(string $type, array $settings): EventDemand + private function createDemandObjectFromSettings(string $type, array $settings): EventDemand { /** @var EventDemand $demand */ $demand = GeneralUtility::makeInstance( @@ -254,7 +236,7 @@ protected function createDemandObjectFromSettings(string $type, array $settings) return $demand; } - protected function addCategoriesToDemandObjectFromSettings(EventDemand &$demand): void + private function addCategoriesToDemandObjectFromSettings(EventDemand &$demand): void { if ($this->settings['categoriesList']) { $selectedCategories = GeneralUtility::intExplode( @@ -281,10 +263,7 @@ protected function addCategoriesToDemandObjectFromSettings(EventDemand &$demand) } } - /** - * assigns currency translation array to view - */ - protected function assignCurrencyTranslationData(): void + private function assignCurrencyTranslationData(): void { $this->restoreSession(); @@ -297,7 +276,7 @@ protected function assignCurrencyTranslationData(): void $this->view->assign('currencyTranslationData', $currencyTranslationData); } - protected function addCacheTags(iterable $events): void + private function addCacheTags(iterable $events): void { $cacheTags = []; @@ -333,7 +312,7 @@ private function forwardToShowActionWhenRequested(): ?ForwardResponse return $forwardResponse->withArguments(['event' => $this->request->getArgument('event')]); } - protected function restoreSession(): void + private function restoreSession(): void { $cart = $this->sessionHandler->restoreCart($this->cartConfiguration['settings']['cart']['pid']); diff --git a/Classes/Controller/EventDateController.php b/Classes/Controller/EventDateController.php index f307a562..0b43e027 100644 --- a/Classes/Controller/EventDateController.php +++ b/Classes/Controller/EventDateController.php @@ -10,12 +10,13 @@ * For the full copyright and license information, please read the * LICENSE file that was distributed with this source code. */ + use Extcode\CartEvents\Domain\Repository\EventDateRepository; use Psr\Http\Message\ResponseInterface; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; -class EventDateController extends ActionController +final class EventDateController extends ActionController { public function __construct( private readonly EventDateRepository $eventDateRepository, diff --git a/Classes/Domain/Model/AbstractEventDate.php b/Classes/Domain/Model/AbstractEventDate.php index 277852b3..68a338b3 100644 --- a/Classes/Domain/Model/AbstractEventDate.php +++ b/Classes/Domain/Model/AbstractEventDate.php @@ -11,32 +11,33 @@ * LICENSE file that was distributed with this source code. */ +use DateTime; use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; abstract class AbstractEventDate extends AbstractEntity { - protected ?\DateTime $begin = null; + protected ?DateTime $begin = null; - protected ?\DateTime $end = null; + protected ?DateTime $end = null; protected string $note = ''; - public function getBegin(): ?\DateTime + public function getBegin(): ?DateTime { return $this->begin; } - public function setBegin(\DateTime $begin): void + public function setBegin(DateTime $begin): void { $this->begin = $begin; } - public function getEnd(): ?\DateTime + public function getEnd(): ?DateTime { return $this->end; } - public function setEnd(\DateTime $end): void + public function setEnd(DateTime $end): void { $this->end = $end; } diff --git a/Classes/Domain/Model/Cart/ProductFactory.php b/Classes/Domain/Model/Cart/ProductFactory.php new file mode 100644 index 00000000..d2bf4158 --- /dev/null +++ b/Classes/Domain/Model/Cart/ProductFactory.php @@ -0,0 +1,171 @@ +getEventDateFromRequestArgument($requestArguments['eventDate']); + + $priceCategory = null; + if (isset($requestArguments['priceCategory'])) { + $priceCategory = $this->getPriceCategoryFromRequestArgument($requestArguments['priceCategory']); + } + + return $this->getProductFromEventDate( + $quantity, + $taxClasses, + $isNetPrice, + $eventDate, + $priceCategory, + ); + } + + private function getEventDateFromRequestArgument( + mixed $identifier, + ): EventDate { + if (is_numeric($identifier) === false) { + throw new InvalidArgumentException('Event date argument is invalid', 1741692831); + } + + $eventDate = $this->eventDateRepository->findByUid($identifier); + + if (($eventDate instanceof EventDate) === false) { + throw new InvalidArgumentException('Event date not found', 1741693220); + } + + if ($eventDate->isBookable() === false) { + throw new NotBookableException('Event date not bookable', 1741693273); + } + + return $eventDate; + } + + private function getPriceCategoryFromRequestArgument( + int $identifier, + ): PriceCategory { + if (is_numeric($identifier) === false) { + throw new InvalidArgumentException('Price category argument is invalid', 1741692831); + } + + $priceCategory = $this->priceCategoryRepository->findByUid($identifier); + + if (($priceCategory instanceof PriceCategory) === false) { + throw new InvalidArgumentException('Price category not found', 1741693444); + } + + if (!$priceCategory->isBookable()) { + throw new NotBookableException('Price category not bookable', 1741693482); + } + + return $priceCategory; + } + + private function getProductFromEventDate( + int $quantity, + array $taxClasses, + bool $isNetPrice, + EventDate $eventDate, + ?PriceCategory $priceCategory = null, + ): Product { + $event = $eventDate->getEvent(); + $title = implode(' - ', [$event->getTitle(), $eventDate->getTitle()]); + $sku = implode(' - ', [$event->getSku(), $eventDate->getSku()]); + + $price = $eventDate->getPrice(); + $bestPrice = $eventDate->getBestPrice(); + if ($priceCategory instanceof PriceCategory) { + $price = $priceCategory->getPrice(); + $bestPrice = $priceCategory->getBestPrice(); + } + + $product = new Product( + 'CartEvents', + $eventDate->getUid(), + $sku, + $title, + $price, + $taxClasses[$event->getTaxClassId()], + $quantity, + $isNetPrice, + null + ); + $product->setIsVirtualProduct($event->isVirtualProduct()); + + if ($bestPrice < $price) { + $product->setSpecialPrice($bestPrice); + } + + if ($priceCategory instanceof PriceCategory) { + $product->addBeVariant($this->getProductBackendVariant($product, $quantity, $priceCategory)); + } + + return $product; + } + + private function getProductBackendVariant( + Product $product, + int $quantity, + PriceCategory $priceCategory, + ): BeVariant { + $cartBackendVariant = GeneralUtility::makeInstance( + BeVariant::class, + PriceCategory::class . '-' . $priceCategory->getUid(), + $product, + $priceCategory->getTitle(), + $priceCategory->getSku(), + 1, + $priceCategory->getBestPrice(), + $quantity + ); + + /* + TODO + if ($bestSpecialPrice) { + $cartBackendVariant->setSpecialPrice($bestSpecialPrice->getPrice()); + } + */ + + return $cartBackendVariant; + } +} diff --git a/Classes/Domain/Model/Cart/ProductFactoryInterface.php b/Classes/Domain/Model/Cart/ProductFactoryInterface.php new file mode 100644 index 00000000..481074c3 --- /dev/null +++ b/Classes/Domain/Model/Cart/ProductFactoryInterface.php @@ -0,0 +1,17 @@ +images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->eventDates = new ObjectStorage(); + $this->relatedEvents = new ObjectStorage(); + $this->relatedEventsFrom = new ObjectStorage(); + } public function isVirtualProduct(): bool { diff --git a/Classes/Domain/Model/EventDate.php b/Classes/Domain/Model/EventDate.php index 6ec68e7e..2e5edf03 100644 --- a/Classes/Domain/Model/EventDate.php +++ b/Classes/Domain/Model/EventDate.php @@ -54,6 +54,9 @@ class EventDate extends AbstractEventDate protected bool $priceCategorized = false; + /** + * @var ObjectStorage + */ #[Cascade(['value' => 'remove'])] protected ObjectStorage $priceCategories; @@ -71,6 +74,15 @@ class EventDate extends AbstractEventDate #[Cascade(['value' => 'remove'])] protected ObjectStorage $calendarEntries; + public function __construct() + { + $this->images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->specialPrices = new ObjectStorage(); + $this->priceCategories = new ObjectStorage(); + $this->calendarEntries = new ObjectStorage(); + } + public function getSku(): string { return $this->sku; diff --git a/Classes/Domain/Model/PriceCategory.php b/Classes/Domain/Model/PriceCategory.php index 268e9af8..4ab96a34 100644 --- a/Classes/Domain/Model/PriceCategory.php +++ b/Classes/Domain/Model/PriceCategory.php @@ -40,6 +40,11 @@ class PriceCategory extends AbstractEntity protected int $seatsTaken = 0; + public function __construct() + { + $this->specialPrices = new ObjectStorage(); + } + public function getSku(): string { return $this->sku; diff --git a/Classes/Domain/Repository/EventDateRepository.php b/Classes/Domain/Repository/EventDateRepository.php index 098f7895..25bffc4b 100644 --- a/Classes/Domain/Repository/EventDateRepository.php +++ b/Classes/Domain/Repository/EventDateRepository.php @@ -10,17 +10,14 @@ * For the full copyright and license information, please read the * LICENSE file that was distributed with this source code. */ -use Doctrine\DBAL\Driver\Statement; + use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Repository; class EventDateRepository extends Repository { - /** - * @return Statement|int - */ - public function findNext(int $limit, string $pidList) + public function findNext(int $limit, string $pidList): array { $table = 'tx_cartevents_domain_model_eventdate'; $joinTableEvent = 'tx_cartevents_domain_model_event'; @@ -61,45 +58,10 @@ public function findNext(int $limit, string $pidList) $queryBuilder->setMaxResults($limit); } - return $queryBuilder->execute(); - } - - /** - * @param $queryBuilder - * @return mixed - */ - protected function joinCategory($queryBuilder) - { - return $queryBuilder - ->leftJoin( - 'event', - 'sys_category_record_mm', - 'categoryMM', - $queryBuilder->expr()->eq( - 'categoryMM.uid_foreign', - $queryBuilder->quoteIdentifier('event.uid') - ) - ) - ->where( - $queryBuilder->expr()->eq('categoryMM.tablenames', '"tx_cartevents_domain_model_event"'), - $queryBuilder->expr()->eq('categoryMM.fieldname', '"category"') - ) - ->leftJoin( - 'categoryMM', - 'sys_category', - 'category', - $queryBuilder->expr()->eq( - 'category.uid', - $queryBuilder->quoteIdentifier('categoryMM.uid_local') - ) - ); + return $queryBuilder->executeQuery()->fetchAllAssociative(); } - /** - * @param string $pidList - * @return array - */ - protected function getPids(string $pidList): array + private function getPids(string $pidList): array { return GeneralUtility::intExplode(',', $pidList, true); } diff --git a/Classes/Domain/Repository/EventRepository.php b/Classes/Domain/Repository/EventRepository.php index 53043504..9d47926a 100644 --- a/Classes/Domain/Repository/EventRepository.php +++ b/Classes/Domain/Repository/EventRepository.php @@ -78,7 +78,7 @@ public function findByUids(string $uids, ?int $limit = null): array return $this->orderByField($query->execute(), $uids); } - protected function createOrderingsFromDemand(EventDemand $demand): array + private function createOrderingsFromDemand(EventDemand $demand): array { $orderings = []; @@ -101,7 +101,7 @@ protected function createOrderingsFromDemand(EventDemand $demand): array return $orderings; } - protected function orderByField(QueryResultInterface $events, array $uids): array + private function orderByField(QueryResultInterface $events, array $uids): array { $indexedEvents = []; $orderedEvents = []; diff --git a/Classes/EventListener/CheckProductAvailability.php b/Classes/EventListener/CheckProductAvailability.php index 18492fe3..db527aa0 100644 --- a/Classes/EventListener/CheckProductAvailability.php +++ b/Classes/EventListener/CheckProductAvailability.php @@ -52,15 +52,17 @@ public function __invoke(CheckProductAvailabilityEvent $listenerEvent): void return; } - if (!$this->eventDate->isHandleSeatsInPriceCategory()) { + if ($this->eventDate->isHandleSeatsInPriceCategory() === false) { $this->hasEventDateEnoughSeats($cartProduct, $cart, $mode, (int)$quantity, $listenerEvent); return; } foreach ($this->eventDate->getPriceCategories() as $priceCategory) { $beVariantId = PriceCategory::class . '-' . $priceCategory->getUid(); - $quantity = (int)$quantity[$beVariantId]; - $this->hasPriceCategoryEnoughSeats($cartProduct, $cart, $mode, $beVariantId, $quantity, $priceCategory, $listenerEvent); + if (array_key_exists($beVariantId, $cartProduct->getBeVariants()) === false) { + continue; + } + $this->hasPriceCategoryEnoughSeats($cartProduct, $cart, $mode, $beVariantId, (int)$quantity, $priceCategory, $listenerEvent); } } diff --git a/Classes/EventListener/RetrieveProductsFromRequest.php b/Classes/EventListener/RetrieveProductsFromRequest.php index cb188a65..a8aaf66f 100644 --- a/Classes/EventListener/RetrieveProductsFromRequest.php +++ b/Classes/EventListener/RetrieveProductsFromRequest.php @@ -11,236 +11,50 @@ * LICENSE file that was distributed with this source code. */ -use Extcode\Cart\Domain\Model\Cart\BeVariant; -use Extcode\Cart\Domain\Model\Cart\Cart; +use Exception; use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\Cart\Event\RetrieveProductsFromRequestEvent; -use Extcode\CartEvents\Domain\Model\EventDate; -use Extcode\CartEvents\Domain\Model\PriceCategory; -use Extcode\CartEvents\Domain\Repository\EventDateRepository; -use Extcode\CartEvents\Domain\Repository\PriceCategoryRepository; +use Extcode\CartEvents\Domain\Model\Cart\ProductFactoryInterface; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; -use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Utility\LocalizationUtility; class RetrieveProductsFromRequest { - protected Cart $cart; - - protected EventDate $eventDate; - - protected ?PriceCategory $priceCategory = null; - public function __construct( - private readonly EventDateRepository $eventDateRepository, - private readonly PriceCategoryRepository $priceCategoryRepository + private readonly ExtensionConfiguration $extensionConfiguration, + private readonly ProductFactoryInterface $productFactory, ) {} public function __invoke(RetrieveProductsFromRequestEvent $event): void { - $request = $event->getRequest(); - $this->cart = $event->getCart(); - $requestArguments = $request->getArguments(); - $taxClasses = $this->cart->getTaxClasses(); + $requestArguments = $event->getRequest()->getArguments(); if ($requestArguments['productType'] !== 'CartEvents') { return; } - $errors = $this->checkRequestArguments($requestArguments); - - if (!empty($errors)) { - $event->setErrors($errors); - return; - } - - $quantity = (int)$requestArguments['quantity']; - - $this->eventDate = $this->eventDateRepository->findByUid((int)$requestArguments['eventDate']); - - if (!$this->eventDate) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.error.event_date_not_found', - 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) + try { + $product = $this->productFactory->createProductFromRequestArguments( + $requestArguments, + $event->getCart()->getTaxClasses(), + (bool)$this->extensionConfiguration->get('cart_events', 'inputIsNetPrice'), ); - return; - } - if (!$this->eventDate->isBookable()) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.error.event_is_not_bookable', + if ($product instanceof Product) { + $event->addProduct($product); + } + } catch (Exception $exception) { + $event->setErrors( + [ + 'messageBody' => LocalizationUtility::translate( + $exception->getCode(), 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) + ) ?? $exception->getMessage(), + 'severity' => ContextualFeedbackSeverity::ERROR, + ] ); return; } - - if (isset($requestArguments['priceCategory'])) { - if (!(int)$requestArguments['priceCategory']) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.invalid_event_date_category_price', - 'CartEvents' - ), - '', - ContextualFeedbackSeverity::ERROR - ) - ); - return; - } - - $this->priceCategory = $this->priceCategoryRepository->findByUid((int)$requestArguments['priceCategory']); - - if (!$this->priceCategory->isBookable()) { - $event->addError( - GeneralUtility::makeInstance( - FlashMessage::class, - LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.event_date.price_category.is_not_bookable', - 'CartEvents' - ), - '', - ContextualFeedbackSeverity::WARNING - ) - ); - return; - } - } - - $event->addProduct( - $this->getProductFromEventDate($quantity, $taxClasses) - ); - } - - /** - * @param int $quantity - * @param array $taxClasses - * - * @return Product - */ - protected function getProductFromEventDate( - int $quantity, - array $taxClasses - ) { - $event = $this->eventDate->getEvent(); - $title = implode(' - ', [$event->getTitle(), $this->eventDate->getTitle()]); - $sku = implode(' - ', [$event->getSku(), $this->eventDate->getSku()]); - - $price = $this->eventDate->getPrice(); - $bestPrice = $this->eventDate->getBestPrice(); - if ($this->priceCategory instanceof PriceCategory) { - $price = $this->priceCategory->getPrice(); - $bestPrice = $this->priceCategory->getBestPrice(); - } - - $inputIsNetPrice = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('cart_events', 'inputIsNetPrice'); - - $product = new Product( - 'CartEvents', - $this->eventDate->getUid(), - $sku, - $title, - $price, - $taxClasses[$event->getTaxClassId()], - $quantity, - $inputIsNetPrice, - null - ); - $product->setIsVirtualProduct($event->isVirtualProduct()); - - if ($bestPrice < $price) { - $product->setSpecialPrice($bestPrice); - } - - if ($this->priceCategory instanceof PriceCategory) { - $product->addBeVariant($this->getProductBackendVariant($product, $quantity)); - } - - if (isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate'])) { - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate'] ?? [] as $className) { - $params = [ - 'cart' => $this->cart, - 'eventDate' => $this->eventDate, - ]; - - $_procObj = GeneralUtility::makeInstance($className); - $_procObj->changeProductFromEventDate($product, $params); - } - } - - return $product; - } - - /** - * @param Product $product - * @param int $quantity - * - * @return BeVariant - */ - protected function getProductBackendVariant( - Product $product, - int $quantity - ): BeVariant { - $cartBackendVariant = GeneralUtility::makeInstance( - BeVariant::class, - PriceCategory::class . '-' . $this->priceCategory->getUid(), - $product, - $this->priceCategory->getTitle(), - $this->priceCategory->getSku(), - 1, - $this->priceCategory->getBestPrice(), - $quantity - ); - - /* - TODO - if ($bestSpecialPrice) { - $cartBackendVariant->setSpecialPrice($bestSpecialPrice->getPrice()); - } - */ - - return $cartBackendVariant; - } - - protected function checkRequestArguments(array $requestArguments): array - { - if (!(int)$requestArguments['eventDate']) { - return [ - 'messageBody' => LocalizationUtility::translate( - 'tx_cartevents.plugin.form.submit.error.invalid_event_date', - 'CartEvents' - ), - 'severity' => ContextualFeedbackSeverity::ERROR, - ]; - } - - if ((int)$requestArguments['quantity'] < 0) { - return [ - 'messageBody' => LocalizationUtility::translate( - 'tx_cart.error.invalid_quantity', - 'CartEvents' - ), - 'severity' => ContextualFeedbackSeverity::WARNING, - ]; - } - - return []; } } diff --git a/Classes/Exception/NotBookableException.php b/Classes/Exception/NotBookableException.php new file mode 100644 index 00000000..6ad69e46 --- /dev/null +++ b/Classes/Exception/NotBookableException.php @@ -0,0 +1,5 @@ + 'pi_plugin1', + * 'pi_plugin2' => 'new_content_element', + * ] + * + * @return array + */ + protected function getListTypeToCTypeMapping(): array + { + return [ + // TODO: Add this mapping yourself! + ]; + } +} diff --git a/Configuration/FlexForms/EventDatesPlugin.xml b/Configuration/FlexForms/EventDatesPlugin.xml index b2cf3b06..2667a740 100644 --- a/Configuration/FlexForms/EventDatesPlugin.xml +++ b/Configuration/FlexForms/EventDatesPlugin.xml @@ -21,7 +21,6 @@ group - db pages 1 1 @@ -39,10 +38,9 @@ 1 - input + number 3 3 - int 1 0 diff --git a/Configuration/FlexForms/ListEventsPlugin.xml b/Configuration/FlexForms/ListEventsPlugin.xml index 2a5f0660..83875ff0 100644 --- a/Configuration/FlexForms/ListEventsPlugin.xml +++ b/Configuration/FlexForms/ListEventsPlugin.xml @@ -12,7 +12,7 @@ selectSingle Extcode\Cart\Hooks\ItemsProcFunc->user_templateLayout cart_events - Events + ListEvents @@ -76,10 +76,9 @@ 1 - input + number 3 3 - int 1 0 @@ -94,7 +93,6 @@ group - db pages 1 1 diff --git a/Configuration/FlexForms/SingleEventPlugin.xml b/Configuration/FlexForms/SingleEventPlugin.xml index 9149748f..b5cc6d93 100644 --- a/Configuration/FlexForms/SingleEventPlugin.xml +++ b/Configuration/FlexForms/SingleEventPlugin.xml @@ -13,7 +13,6 @@ Extcode\Cart\Hooks\ItemsProcFunc->user_templateLayout cart_events SingleEvent - selectSingle diff --git a/Configuration/FlexForms/TeaserEventsPlugin.xml b/Configuration/FlexForms/TeaserEventsPlugin.xml index b71375c3..b50610fe 100644 --- a/Configuration/FlexForms/TeaserEventsPlugin.xml +++ b/Configuration/FlexForms/TeaserEventsPlugin.xml @@ -20,10 +20,9 @@ 1 - input + number 3 3 - int 1 0 @@ -38,7 +37,6 @@ group - db pages 1 1 @@ -58,7 +56,6 @@ select selectMultipleSideBySide - 1 tx_cartevents_domain_model_event 3 1 diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index bc417ba3..8abbb6f8 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -11,39 +11,51 @@ $pluginNames = [ 'ShowEvent' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.show_event', ], 'ListEvents' => [ - 'subtypes_excludelist' => 'select_key', + 'additionalNewFields' => 'pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-list', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.list_events', ], 'TeaserEvents' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-teaser', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.teaser_events', ], 'SingleEvent' => [ - 'subtypes_excludelist' => 'select_key, pages, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.single_event', ], 'EventDates' => [ - 'subtypes_excludelist' => 'select_key, recursive', + 'pluginIcon' => 'ext-cartevents-wizard-icon-show', + 'translationKeyPrefix' => $_LLL_be . 'tx_cartevents.plugin.event_dates', ], ]; foreach ($pluginNames as $pluginName => $pluginConf) { - $pluginSignature = 'cartevents_' . strtolower($pluginName); - $pluginNameSC = strtolower((string)preg_replace('/[A-Z]/', '_$0', lcfirst($pluginName))); - ExtensionUtility::registerPlugin( - 'CartEvents', + $pluginSignature = ExtensionUtility::registerPlugin( + 'cart_events', $pluginName, - $_LLL_be . 'tx_cartevents.plugin.' . $pluginNameSC . '.title' + $pluginConf['translationKeyPrefix'] . '.title', + $pluginConf['pluginIcon'], + 'cart', + $pluginConf['translationKeyPrefix'] . '.description', ); - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist'][$pluginSignature] = $pluginConf['subtypes_excludelist']; - $flexFormPath = 'EXT:cart_events/Configuration/FlexForms/' . $pluginName . 'Plugin.xml'; if (file_exists(GeneralUtility::getFileAbsFileName($flexFormPath))) { - $GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist'][$pluginSignature] = 'pi_flexform'; + ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + rtrim('--div--;Configuration,pi_flexform,' . ($pluginConf['additionalNewFields'] ?? ''), ','), + $pluginSignature, + 'after:subheader', + ); + ExtensionManagementUtility::addPiFlexFormValue( + '*', + 'FILE:' . $flexFormPath, $pluginSignature, - 'FILE:' . $flexFormPath ); } } diff --git a/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php b/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php index e2f9d3a0..514ab296 100644 --- a/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php +++ b/Configuration/TCA/Overrides/tx_cartevents_domain_model_event.php @@ -2,6 +2,7 @@ defined('TYPO3') or die(); +use Extcode\Cart\Hooks\FormDefinitions; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -21,7 +22,7 @@ 'items' => [ ['label' => 'LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.selectPersistenceIdentifier', 'value' => ''], ], - 'itemsProcFunc' => 'Extcode\\Cart\\Hooks\\ItemsProcFunc->user_formDefinition', + 'itemsProcFunc' => FormDefinitions::class . '->getItems', 'itemsProcFuncConfig' => [ 'prototypeName' => 'cart-events', ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_event.php b/Configuration/TCA/tx_cartevents_domain_model_event.php index 827fc226..f30bc695 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_event.php +++ b/Configuration/TCA/tx_cartevents_domain_model_event.php @@ -2,8 +2,6 @@ defined('TYPO3') or die(); -use TYPO3\CMS\Core\Resource\File; - $_LLL_general = 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf'; $_LLL_ttc = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf'; $_LLL_cart = 'LLL:EXT:cart/Resources/Private/Language/locallang_db.xlf'; @@ -50,7 +48,7 @@ tax_class_id, event_dates, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_event.div.relations, - related_events, + related_events, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_event.div.categorization, tags, category, categories, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.tabs.access, @@ -257,7 +255,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -265,48 +262,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'image', - 'tablenames' => 'tx_cartevents_domain_model_event', - ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-image-types', ], ], @@ -316,7 +275,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ', pdf', 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -324,48 +282,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'files', - 'tablenames' => 'tx_cartevents_domain_model_event', + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], - ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-media-types', ], ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php index c58bccb8..fd789bd4 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php +++ b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php @@ -2,8 +2,6 @@ defined('TYPO3') or die(); -use TYPO3\CMS\Core\Resource\File; - $_LLL_general = 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf'; $_LLL_ttc = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf'; $_LLL_db = 'LLL:EXT:cart_events/Resources/Private/Language/locallang_db.xlf'; @@ -40,7 +38,7 @@ 'types' => [ '1' => [ 'showitem' => ' - sku, title, + sku, title, --div--;' . $_LLL_tca . ':tx_cartevents_domain_model_eventdate.div.informations, --palette--;' . $_LLL_tca . ':tx_cartevents_domain_model_eventdate.palettes.begin_and_end;begin_and_end, calendar_entries, location, lecturer, @@ -195,7 +193,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -203,48 +200,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'image', - 'tablenames' => 'tx_cartevents_domain_model_eventdate', - ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-image-types', ], ], @@ -254,7 +213,6 @@ 'config' => [ //## !!! Watch out for fieldName different from columnName 'type' => 'file', - 'allowed' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] . ', pdf', 'appearance' => [ 'createNewRelationLinkTitle' => $_LLL_ttc . ':images.addFileReference', 'showPossibleLocalizationRecords' => true, @@ -262,48 +220,10 @@ 'showAllLocalizationLink' => true, 'showSynchronizationLink' => true, ], - 'foreign_match_fields' => [ - 'fieldname' => 'files', - 'tablenames' => 'tx_cartevents_domain_model_eventdate', + 'behaviour' => [ + 'allowLanguageSynchronization' => true, ], - // custom configuration for displaying fields in the overlay/reference table - // to use the imageoverlayPalette instead of the basicoverlayPalette - 'overrideChildTca' => [ - 'types' => [ - '0' => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_TEXT => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_IMAGE => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_AUDIO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_VIDEO => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - File::FILETYPE_APPLICATION => [ - 'showitem' => ' - --palette--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette;imageoverlayPalette, - --palette--;;filePalette', - ], - ], - ], - 'minitems' => 0, - 'maxitems' => 99, + 'allowed' => 'common-media-types', ], ], diff --git a/Configuration/TSconfig/ContentElementWizard.tsconfig b/Configuration/TSconfig/ContentElementWizard.tsconfig index 1b06b103..5e9df778 100644 --- a/Configuration/TSconfig/ContentElementWizard.tsconfig +++ b/Configuration/TSconfig/ContentElementWizard.tsconfig @@ -1,43 +1,5 @@ [traverse(page, "doktype") == 186] -TCEFORM.tt_content.list_type { - removeItems := addToList(cartevents_events, cartevents_eventdates) -} + TCEFORM.tt_content.CType.removeItems := addToList(cartevents_listevents, cartevents_eventdates) [ELSE] -TCEFORM.tt_content.list_type { - removeItems := addToList(cartevents_singleevent) -} - -mod.wizards.newContentElement.wizardItems.plugins { - elements { - list_events { - iconIdentifier = ext-cartevents-wizard-icon-list - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.list_events.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.list_events.description - tt_content_defValues { - CType = list - list_type = cartevents_listevents - } - } - - show_events { - iconIdentifier = ext-cartevents-wizard-icon-show - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.show_event.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.show_event.description - tt_content_defValues { - CType = list - list_type = cartevents_showevent - } - } - - teaser_events { - iconIdentifier = ext-cartevents-wizard-icon-teaser - title = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.teaser_events.title - description = LLL:EXT:cart_events/Resources/Private/Language/locallang_be.xlf:tx_cartevents.plugin.teaser_events.description - tt_content_defValues { - CType = list - list_type = cartevents_teaserevents - } - } - } -} + TCEFORM.tt_content.CType.removeItems := addToList(cartevents_singleevent) [END] diff --git a/Configuration/page.tsconfig b/Configuration/page.tsconfig new file mode 100644 index 00000000..97d6f2fa --- /dev/null +++ b/Configuration/page.tsconfig @@ -0,0 +1,3 @@ + + + diff --git a/README.md b/README.md index e71bf0c9..fe97bac1 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,20 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f7809fa0f2ab40118e263cb714212d13)](https://www.codacy.com/app/extcode/cart_events?utm_source=github.com&utm_medium=referral&utm_content=extcode/cart_events&utm_campaign=Badge_Grade) Cart is a small but powerful extension which "solely" adds a shopping cart to your TYPO3 installation. -Cart Events provides an own data storage for events. Events can be offered via a list and detail view and can be purchased via cart function of the Cart extension. +Cart Events provides an own data storage for events. ## 1. Features -- -- -- +* It provides events and their event dates which can be created in the TYPO3 backend. +* The data for those events are stored in own data tables. +* The data fields of the events fit many use cases for seminars, workshops, theatre + performances or generally date-related seat reservations. +* The events and their dates can be displayed on the website with a list view and a + detail view. +* It is possible to limit the number of bookable seats per event date or per price + category of an event date. +* As it extends EXT:cart are the products compatible with EXT:cart and can + therefore be be purchased with the cart functionality of EXT:cart. ## 2. Installation @@ -17,7 +24,7 @@ Cart Events provides an own data storage for events. Events can be offered via a #### Installation using Composer -The recommended way to install the extension is by using [Composer][2]. In your Composer based TYPO3 project root, just do `composer require extcode/cart-events`. +The recommended way to install the extension is by using [Composer][2]. In your Composer based TYPO3 project root, just do `composer require extcode/cart-events`. #### Installation as extension from TYPO3 Extension Repository (TER) @@ -35,9 +42,10 @@ Sometimes minor versions also result in minor adjustments to own templates or co | Cart Events | TYPO3 | PHP | Support/Development | |-------------|------------|-----------|--------------------------------------| -| 5.x.x | 12.4 | 8.1 - 8.4 | Features, Bugfixes, Security Updates | -| 4.x.x | 10.4, 11.5 | 7.2+ | Bugfixes, Security Updates | -| 3.x.x | 10.4 | 7.2 - 7.4 | Security Updates | +| 6.x.x | 13.4 | 8.2 - 8.4 | Features, Bugfixes, Security Updates | +| 5.x.x | 12.4 | 8.1 - 8.4 | Bugfixes, Security Updates | +| 4.x.x | 10.4, 11.5 | 7.2+ | Security Updates | +| 3.x.x | 10.4 | 7.2 - 7.4 | | | 2.x.x | 9.5 | 7.2 - 7.4 | | | 1.x.x | 8.7 | 7.0 - 7.4 | | @@ -62,4 +70,4 @@ News uses **semantic versioning** which basically means for you, that * [PayPal.Me](https://paypal.me/extcart) [1]: https://docs.typo3.org/typo3cms/extensions/cart_events/ -[2]: https://getcomposer.org/ \ No newline at end of file +[2]: https://getcomposer.org/ diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 03968d8f..91406791 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -16,18 +16,18 @@ The event could not be added to the shopping cart! - + Event is not bookable. - + Not enough Seats available. - + Invalid event date given. - + Invalid category price for event date given diff --git a/Resources/Public/JavaScripts/cart_events.js b/Resources/Public/JavaScripts/cart_events.js index 2bdfface..dace1d8c 100644 --- a/Resources/Public/JavaScripts/cart_events.js +++ b/Resources/Public/JavaScripts/cart_events.js @@ -1,54 +1,64 @@ -var cart_events = (function () { +(() => { + const eventDates = document.getElementsByClassName('event-event-date'); - var eventDates = document.getElementsByClassName('event-event-date'); - - for (var eventDateCount=0; eventDateCount < eventDates.length; eventDateCount++) { - var addToCartForms = eventDates[eventDateCount].querySelectorAll('form.add-to-cart-form'); - for (var addToCartFormsCount=0; addToCartFormsCount < addToCartForms.length; addToCartFormsCount++) { - var priceCategorySelects = addToCartForms[addToCartFormsCount].querySelectorAll('.price-category-select'); - for (var priceCategorySelectCount=0; priceCategorySelectCount < priceCategorySelects.length; priceCategorySelectCount++) { - - priceCategorySelects[priceCategorySelectCount].addEventListener('change', function(){ updatePriceCategory(this, eventDates[eventDateCount]) }, false); - } - } + for (let eventDateCount = 0; eventDateCount < eventDates.length; eventDateCount++) { + const addToCartForms = eventDates[eventDateCount].querySelectorAll('form.add-to-cart-form'); + for (let addToCartFormsCount = 0; addToCartFormsCount < addToCartForms.length; addToCartFormsCount++) { + const priceCategorySelects = addToCartForms[addToCartFormsCount].querySelectorAll('select.price-category-select'); + for (let priceCategorySelectCount = 0; priceCategorySelectCount < priceCategorySelects.length; priceCategorySelectCount++) { + priceCategorySelects[priceCategorySelectCount].addEventListener('change', function () { + updatePriceCategory(this, eventDates[eventDateCount]) + }, false); + } } + } - function updatePriceCategory(element, eventDate) { - var price; - - var style = element.selectedOptions[0].style.display; - eventDate.querySelectorAll('.event-date-price-category').forEach(el => { - el.style.display = 'none'; - }); - eventDate.querySelector('.event-date-price-category-' + element.selectedOptions[0].value).style.display = style; + function dispatchCustomEvent(name, dataObject) { + const customEvent = new CustomEvent( + name, + { + bubbles: true, + cancelable: true, + detail: dataObject + } + ); + document.dispatchEvent(customEvent); + } - var title = element.selectedOptions[0].getAttribute('data-title'); + function updatePriceCategory(element, eventDate) { + const title = element.selectedOptions[0].getAttribute('data-title'); - if (title) { - eventDate.querySelector('.event-date-price .regular-price').style.display = 'none'; - eventDate.querySelector('.event-date-price .special-price').style.display = 'block'; + if (title) { + eventDate.querySelector('.event-date-price .regular-price').style.display = 'none'; + eventDate.querySelector('.event-date-price .special-price').style.display = 'block'; - eventDate.querySelector('.event-date-price .special-price .title').innerHTML = title; + eventDate.querySelector('.event-date-price .special-price .title').innerHTML = title; - eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = ''; + eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = ''; - price = element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = price; + eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-regular-price'); - price = element.selectedOptions[0].getAttribute('data-special-price'); - eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = price; - } else { - eventDate.querySelector('.event-date-price .regular-price').style.display = 'block'; - eventDate.querySelector('.event-date-price .special-price').style.display = 'none'; + eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-special-price'); + } else { + eventDate.querySelector('.event-date-price .regular-price').style.display = 'block'; + eventDate.querySelector('.event-date-price .special-price').style.display = 'none'; - eventDate.querySelector('.event-date-price .special-price .title').innerHTML = ''; + eventDate.querySelector('.event-date-price .special-price .title').innerHTML = ''; - price = element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = price; + eventDate.querySelector('.event-date-price .regular-price .price').innerHTML = + element.selectedOptions[0].getAttribute('data-regular-price'); - eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = ''; - eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = ''; - } + eventDate.querySelector('.event-date-price .special-price .regular-price .price').innerHTML = ''; + eventDate.querySelector('.event-date-price .special-price .special-price .price').innerHTML = ''; } + dispatchCustomEvent( + 'extcode:update-price-category', + { + element, eventDate + } + ); + } })(); diff --git a/Tests/Acceptance/AddEventDateToCartCest.php b/Tests/Acceptance/AddEventDateToCartCest.php index b2c85ed9..8349d12d 100644 --- a/Tests/Acceptance/AddEventDateToCartCest.php +++ b/Tests/Acceptance/AddEventDateToCartCest.php @@ -26,7 +26,7 @@ public function addBookableEventToCart(Tester $I): void $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('19,99 €'); @@ -68,7 +68,7 @@ public function addBookableEventDateWithoutSeatHandlingToCart(Tester $I): void $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('19,99 €'); @@ -104,7 +104,7 @@ public function addBookableEventDateWithAvailableNumberOfSeatToCart(Tester $I): $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -149,7 +149,7 @@ public function addDifferentBookableEventDatesWithAvailableNumberOfSeatToCart(Te $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -203,7 +203,7 @@ public function addBookableEventDateWithAvailableNumberOfSeatButNotMoreToCart(Te $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -257,7 +257,7 @@ public function addDifferentBookableEventDateWithAvailableNumberOfSeatButNotMore $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', 'http://127.0.0.1:8080/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->dontSee('This event date can not be booked.'); @@ -333,4 +333,120 @@ public function addDifferentBookableEventDateWithAvailableNumberOfSeatButNotMore $I->seeInField("input[name='tx_cart_cart[quantities][CartEvents_4]']", '8'); $I->see('314,06', '.checkout-product-table tr:nth-child(2) td:nth-child(4)'); } + + #[Test] + public function addBookableEventDateWithPriceCategoryAvailableNumberOfSeatToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with price category wit available number of seats to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Item was added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '7'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('7 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } + + #[Test] + public function addBookableEventDateWithPriceCategoryAvailableNumberOfSeatButNotMoreToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with price category with available number of seats but not more to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '38'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '38'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '37'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('37 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } + + #[Test] + public function addDifferentBookableEventDateWithPriceCategoryAvailableNumberOfSeatButNotMoreToCart(Tester $I): void + { + $I->wantToTest('I can add a bookable event date with different price categories with available number of seats but not more to the cart.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->wantTo('Add the price group "Category B" with quantity of 37 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '37'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('37 Items were added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->wantTo('Add the first price group "Category B" with quantity of 1 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Desired number of this item not available.', '#event-date-7 .form-message .form-error'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + + $I->wantTo('Add the first price group "Category A" with quantity of 1 to cart.'); + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category A'); + $I->seeElement("#event-date-7 input[name='tx_cart_cart[quantity]']"); + $I->seeInField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->fillField("#event-date-7 input[name='tx_cart_cart[quantity]']", '1'); + $I->seeElement('#event-date-7 input[type="submit"]'); + $I->click('#event-date-7 input[type="submit"]'); + $I->see('Item was added to cart.', '#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-success'); + $I->waitForElementNotVisible('#event-date-7 .form-message .form-error'); + } } diff --git a/Tests/Acceptance/EventListCest.php b/Tests/Acceptance/EventListCest.php index 42445ae8..1caab50b 100644 --- a/Tests/Acceptance/EventListCest.php +++ b/Tests/Acceptance/EventListCest.php @@ -14,6 +14,8 @@ use Extcode\CartEvents\Tests\Acceptance\Support\Tester; use PHPUnit\Framework\Attributes\Test; +use function PHPUnit\Framework\assertSame; + class EventListCest { #[Test] @@ -21,11 +23,11 @@ public function listForEvents(Tester $I): void { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=1&cHash=4e06011cd740d94d7270b73b5e209c7b'); + $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=1&cHash=b94e793b120e29763527f801db80844c'); $I->see('Teaser 1'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->see('Teaser 2'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->see('Teaser 3'); $I->dontSee('Event 4'); @@ -36,7 +38,7 @@ public function detailViewForNonBookableEvent(Tester $I): void { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=1&cHash=4e06011cd740d94d7270b73b5e209c7b'); + $I->seeLink('Event 1', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=1&cHash=b94e793b120e29763527f801db80844c'); $I->click('Event 1'); $I->see('Event 1', 'h1'); @@ -49,7 +51,7 @@ public function detailViewForBookableEventWithOneEventdateWithoutPriceCategories { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=2&cHash=c02777b6ed45d4187afc3fd7f1a42b85'); + $I->seeLink('Event 2', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=2&cHash=18eb8b460c56ca88173743ab54524f53'); $I->click('Event 2'); $I->see('Event 2', 'h1'); @@ -69,7 +71,7 @@ public function detailViewForBookableEventWithTwoOrMoreEventdatesWithoutPriceCat { $I->amOnUrl('http://127.0.0.1:8080/events/'); - $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bevent%5D=3&cHash=d1b28fe9400d030f4a10cd177e517670'); + $I->seeLink('Event 3', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=3&cHash=e30dcda6967105cf51f3d0ed454f4ba1'); $I->click('Event 3'); $I->see('Event 3', 'h1'); @@ -91,4 +93,94 @@ public function detailViewForBookableEventWithTwoOrMoreEventdatesWithoutPriceCat $I->dontSee('This event date can not be booked.'); } + + #[Test] + public function detailViewForBookableEventWithPriceCategories(Tester $I): void + { + $I->wantToTest('I can see a bookable event with price categories and all options are rendered correctly.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->see('Event 5', 'h1'); + + $I->see('Eventdate 5', '.event-event-date:nth-child(1) > h2'); + $I->see('31.07.2024 10:00 - ', '.event-event-date:nth-child(1) > div.date'); + $I->see('Seats: 82 / 275', '.event-event-date:nth-child(1) > div'); + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->seeElement("select[name='tx_cart_cart[priceCategory]']"); + $I->dontSeeElement("select[name='tx_cart_cart[priceCategory]'] option[selected='selected']"); + + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(2)", + 'Category D', + '4', + '10,00 €' + ); + $I->see('Category D', "select[name='tx_cart_cart[priceCategory]'] option[disabled='']"); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(3)", + 'Category C', + '3', + '15,00 €' + ); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(4)", + 'Category B', + '2', + '17,00 €' + ); + $this->seeOption( + $I, + "select[name='tx_cart_cart[priceCategory]'] > option:nth-child(5)", + 'Category A', + '1', + '22,00 €' + ); + } + + #[Test] + public function selectOptionForBookableEventWithPriceCategoriesChangePrice(Tester $I): void + { + $I->wantToTest('I can see a bookable event with price categories and all options are rendered correctly.'); + + $I->amOnUrl('http://127.0.0.1:8080/events/'); + + $I->seeLink('Event 5', '/events?tx_cartevents_listevents%5Baction%5D=show&tx_cartevents_listevents%5Bcontroller%5D=Event&tx_cartevents_listevents%5Bevent%5D=5&cHash=18ff1dbf4acad08e4f2f23af33a5c222'); + $I->click('Event 5'); + + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category C'); + $I->see('15,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category B'); + $I->see('17,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category A'); + $I->see('22,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + + $I->selectOption("select[name='tx_cart_cart[priceCategory]']", 'Category D'); + $I->dontSeeElement("select[name='tx_cart_cart[priceCategory]'] option[selected='selected']"); + $I->see('22,00 €', '.event-event-date:nth-child(1) span.regular-price > span.price'); + } + + private function seeOption(Tester $I, string $selector, string $label, string $value, string $price): void + { + $I->see($label, $selector); + assertSame( + $value, + $I->grabValueFrom($selector) + ); + assertSame( + $price, + $I->grabAttributeFrom($selector, 'data-regular-price') + ); + } } diff --git a/Tests/Acceptance/Support/Environment.php b/Tests/Acceptance/Support/Environment.php index 3641f93a..336e8ca2 100644 --- a/Tests/Acceptance/Support/Environment.php +++ b/Tests/Acceptance/Support/Environment.php @@ -22,9 +22,9 @@ final class Environment extends BackendEnvironment 'typo3/cms-core', 'typo3/cms-backend', 'typo3/cms-extbase', - 'typo3/cms-frontend', 'typo3/cms-fluid', 'typo3/cms-fluid-styled-content', + 'typo3/cms-frontend', 'typo3/cms-install', ], 'testExtensionsToLoad' => [ diff --git a/Tests/Fixtures/ContentDatabase.php b/Tests/Fixtures/ContentDatabase.php index d8605615..3fe233ad 100644 --- a/Tests/Fixtures/ContentDatabase.php +++ b/Tests/Fixtures/ContentDatabase.php @@ -7,16 +7,14 @@ 0 => [ 'uid' => '1', 'pid' => '3', - 'CType' => 'list', - 'list_type' => 'cartevents_listevents', + 'CType' => 'cartevents_listevents', 'pages' => '7', 'pi_flexform' => ' Event->show;Event->list table 0 ', ], 1 => [ 'uid' => '2', 'pid' => '11', - 'CType' => 'list', - 'list_type' => 'cart_cart', + 'CType' => 'cart_cart', 'pages' => '', 'pi_flexform' => '', ], diff --git a/Tests/Fixtures/EventsDatabase.php b/Tests/Fixtures/EventsDatabase.php index 253f68bc..2f391782 100644 --- a/Tests/Fixtures/EventsDatabase.php +++ b/Tests/Fixtures/EventsDatabase.php @@ -48,6 +48,17 @@ 'audience' => '', 'path_segment' => 'event-4', ], + 4 => [ + 'uid' => '5', + 'pid' => '7', + 'sku' => 'event-5', + 'title' => 'Event 5', + 'teaser' => '', + 'description' => '', + 'meta_description' => '', + 'audience' => '', + 'path_segment' => 'event-5', + ], ], 'tx_cartevents_domain_model_eventdate' => [ 0 => [ @@ -141,5 +152,64 @@ 'price' => 9.99, 'bookable' => true, ], + 6 => [ + 'uid' => '7', + 'pid' => '7', + 'event' => '5', + 'sku' => 'eventdate-5', + 'title' => 'Eventdate 5', + 'begin' => '1722420000', + 'location' => '', + 'lecturer' => '', + 'note' => '', + 'price' => 9.99, + 'bookable' => true, + 'price_categorized' => true, + 'price_categories' => 4, + 'handle_seats' => true, + 'handle_seats_in_price_category' => true, + ], + ], + 'tx_cartevents_domain_model_pricecategory' => [ + 0 => [ + 'uid' => 1, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category A', + 'sku' => 'category-a', + 'price' => 22.0, + 'seats_number' => 80, + 'seats_taken' => 47, + ], + 1 => [ + 'uid' => 2, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category B', + 'sku' => 'category-b', + 'price' => 17.0, + 'seats_number' => 70, + 'seats_taken' => 33, + ], + 2 => [ + 'uid' => 3, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category C', + 'sku' => 'category-c', + 'price' => 15.0, + 'seats_number' => 110, + 'seats_taken' => 98, + ], + 3 => [ + 'uid' => 4, + 'pid' => 7, + 'event_date' => 7, + 'title' => 'Category D', + 'sku' => 'category-d', + 'price' => 10.0, + 'seats_number' => 15, + 'seats_taken' => 15, + ], ], ]; diff --git a/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php b/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php new file mode 100644 index 00000000..5761a397 --- /dev/null +++ b/Tests/Functional/Domain/Model/Cart/ProductFactoryTest.php @@ -0,0 +1,214 @@ +testExtensionsToLoad[] = 'extcode/cart'; + $this->testExtensionsToLoad[] = 'extcode/cart-events'; + + parent::setUp(); + + $this->importPHPDataSet(__DIR__ . '/../../../../Fixtures/PagesDatabase.php'); + $this->importPHPDataSet(__DIR__ . '/../../../../Fixtures/EventsDatabase.php'); + } + + #[Test] + public function throwExceptionWithoutQuantityArgument(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741700244); + $this->expectExceptionMessage('Quantity argument is missing'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [], + [], + false + ); + } + + #[Test] + public function throwExceptionWithQuantityArgumentLowerThanZero(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741692900); + $this->expectExceptionMessage('Quantity argument is invalid'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => -1, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithoutEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741700304); + $this->expectExceptionMessage('Event date argument is missing'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNonNumericEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741692831); + $this->expectExceptionMessage('Event date argument is invalid'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 'a', + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNotExistingEventDate(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionCode(1741693220); + $this->expectExceptionMessage('Event date not found'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 1000, + ], + [], + false + ); + } + + #[Test] + public function throwExceptionWithNotBookableExistingEventDate(): void + { + $this->expectException(NotBookableException::class); + $this->expectExceptionCode(1741693273); + $this->expectExceptionMessage('Event date not bookable'); + + $productFactory = $this->getProductFactory(); + $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 1, + ], + [], + false + ); + } + + #[Test] + public function getCartProductForValidQuantityAndBookableEventDate(): void + { + $productFactory = $this->getProductFactory(); + $product = $productFactory->createProductFromRequestArguments( + [ + 'quantity' => 1, + 'eventDate' => 3, + ], + [ + 1 => new TaxClass( + 1, + '19 %', + 0.19, + 'normal' + ), + ], + false + ); + + self::assertSame( + 1, + $product->getQuantity() + ); + self::assertSame( + 3, + $product->getProductId() + ); + self::assertSame( + 'event-3 - eventdate-3-1', + $product->getSku() + ); + self::assertSame( + 'Event 3 - Eventdate 3.1', + $product->getTitle() + ); + + self::assertSame( + 29.99, + $product->getPrice() + ); + self::assertSame( + 29.99, + $product->getGross() + ); + self::assertSame( + 25.201680672268907, + $product->getNet() + ); + self::assertSame( + 4.7883193277310925, + $product->getTax() + ); + + self::assertTrue( + $product->isVirtualProduct() + ); + + self::assertFalse( + $product->isHandleStock() + ); + } + + private function getProductFactory(): ProductFactory + { + return GeneralUtility::makeInstance( + ProductFactory::class, + GeneralUtility::makeInstance(EventDateRepository::class), + GeneralUtility::makeInstance(PriceCategoryRepository::class), + ); + } +} diff --git a/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php b/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php index 9423b950..5a47c8d6 100644 --- a/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php +++ b/Tests/Functional/Domain/Repository/EventDateRepositoryTest.php @@ -12,18 +12,18 @@ */ use Codappix\Typo3PhpDatasets\TestingFramework; -use Extcode\CartEvents\Domain\Repository\EventRepository; +use Extcode\CartEvents\Domain\Repository\EventDateRepository; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; -#[CoversClass(EventRepository::class)] +#[CoversClass(EventDateRepository::class)] class EventDateRepositoryTest extends FunctionalTestCase { use TestingFramework; - private EventRepository $eventRepository; + private EventDateRepository $eventDateRepository; public function setUp(): void { @@ -32,15 +32,175 @@ public function setUp(): void parent::setUp(); - $this->eventRepository = GeneralUtility::makeInstance(EventRepository::class); + $this->eventDateRepository = GeneralUtility::makeInstance(EventDateRepository::class); $this->importPHPDataSet(__DIR__ . '/../../../Fixtures/PagesDatabase.php'); $this->importPHPDataSet(__DIR__ . '/../../../Fixtures/EventsDatabase.php'); } #[Test] - public function findNextReturnsNext(): never + public function findNextReturnsNextForOnePid(): void { - self::markTestSkipped(); + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '7'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 4, + $eventDates[0]['uid'] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '9'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 6, + $eventDates[0]['uid'] + ); + } + + #[Test] + public function findNextReturnsNextForTwoPid(): void + { + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(1, '7, 9'); + + self::assertCount( + 1, + $eventDates + ); + + self::assertSame( + 6, + $eventDates[0]['uid'] + ); + } + + #[Test] + public function findNextsOnlyReturnsNextInFutureInCorrectOrder(): void + { + $connection = $this->getConnectionPool()->getConnectionForTable('tx_cartevents_domain_model_eventdate'); + + $eventDates = $this->eventDateRepository->findNext(10, '7'); + + self::assertCount( + 0, + $eventDates + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 3 * 86400], + ['uid' => 3] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 2 * 86400], + ['uid' => 4] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 4 * 86400], + ['uid' => 5] + ); + + $connection->update( + 'tx_cartevents_domain_model_eventdate', + ['begin' => time() + 1 * 86400], + ['uid' => 6] + ); + + $eventDates = $this->eventDateRepository->findNext(10, '7'); + + self::assertCount( + 3, + $eventDates + ); + self::assertSame( + 4, + $eventDates[0]['uid'] + ); + self::assertSame( + 3, + $eventDates[1]['uid'] + ); + self::assertSame( + 5, + $eventDates[2]['uid'] + ); } } diff --git a/Tests/Functional/Domain/Repository/EventRepositoryTest.php b/Tests/Functional/Domain/Repository/EventRepositoryTest.php index bc1ac4bd..138abdbe 100644 --- a/Tests/Functional/Domain/Repository/EventRepositoryTest.php +++ b/Tests/Functional/Domain/Repository/EventRepositoryTest.php @@ -75,7 +75,7 @@ public function findDemandedByNewEventDemand(): void $events = $this->eventRepository->findDemanded($eventDemand); self::assertCount( - 3, + 4, $events ); } diff --git a/composer.json b/composer.json index cd8679d1..ef055972 100644 --- a/composer.json +++ b/composer.json @@ -46,25 +46,24 @@ } }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", "ext-json": "*", "ext-pdo": "*", - "extcode/cart": "^10.0", - "typo3/cms-core": "^12.4", - "typo3/cms-extbase": "^12.4", - "typo3/cms-fluid": "^12.4" + "extcode/cart": "^11.3", + "typo3/cms-core": "^13.4", + "typo3/cms-extbase": "^13.4", + "typo3/cms-fluid": "^13.4" }, "require-dev": { - "codappix/typo3-php-datasets": "^1.5", + "codappix/typo3-php-datasets": "^2.1", "codeception/codeception": "^5.0", "codeception/module-db": "^3.1", "codeception/module-webdriver": "^4.0", "friendsofphp/php-cs-fixer": "^3.16", "helmich/typo3-typoscript-lint": "^3.1", "phpstan/phpstan": "^1.10", - "ssch/typo3-rector": "^2.6", - "typo3/cms-fluid-styled-content": "^12.4", - "typo3/cms-install": "^12.4", + "typo3/cms-fluid-styled-content": "^13.4", + "typo3/cms-install": "^13.4", "typo3/testing-framework": "^8.0" }, "scripts": { @@ -107,5 +106,8 @@ "@test:typoscript:lint", "@test:php" ] + }, + "suggest": { + "typo3/cms-form": "^13.4" } } diff --git a/ext_emconf.php b/ext_emconf.php index 47d4b501..cd1f32d1 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -11,8 +11,8 @@ 'author_company' => 'extco.de UG (haftungsbeschränkt)', 'constraints' => [ 'depends' => [ - 'typo3' => '12.4.0-12.4.99', - 'cart' => '10.0.0', + 'typo3' => '13.4.0-13.4.99', + 'cart' => '11.3.0', ], 'conflicts' => [], 'suggests' => [], diff --git a/ext_localconf.php b/ext_localconf.php index d036f989..3429c531 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -6,7 +6,6 @@ use Extcode\CartEvents\Hooks\DataHandler; use Extcode\CartEvents\Hooks\DatamapDataHandlerHook; use Extcode\CartEvents\Updates\SlugUpdater; -use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Extbase\Utility\ExtensionUtility; defined('TYPO3') or die(); @@ -23,7 +22,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -34,7 +34,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -45,7 +46,8 @@ ], [ EventController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -56,7 +58,8 @@ ], [ EventController::class => 'form', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); ExtensionUtility::configurePlugin( @@ -67,15 +70,10 @@ ], [ EventDateController::class => '', - ] + ], + ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); -// TSconfig - -ExtensionManagementUtility::addPageTSConfig(' - -'); - // Cart Hooks $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart']['CartEvents']['Form']['AddToCartFinisher'] diff --git a/rector.php b/rector.php index 0561a399..be817ae0 100644 --- a/rector.php +++ b/rector.php @@ -23,11 +23,11 @@ ]) // uncomment to reach your current PHP version ->withPhpSets(php81: true) - ->withPhpVersion(PhpVersion::PHP_81) + ->withPhpVersion(PhpVersion::PHP_82) ->withSets([ Typo3SetList::CODE_QUALITY, Typo3SetList::GENERAL, - Typo3LevelSetList::UP_TO_TYPO3_12, + Typo3LevelSetList::UP_TO_TYPO3_13, ]) // To have a better analysis from PHPStan, we teach it here some more things ->withPHPStanConfigs([ @@ -38,8 +38,8 @@ ConvertImplicitVariablesToExplicitGlobalsRector::class, ]) ->withConfiguredRule(ExtEmConfRector::class, [ - ExtEmConfRector::PHP_VERSION_CONSTRAINT => '8.1.0-8.4.99', - ExtEmConfRector::TYPO3_VERSION_CONSTRAINT => '12.4.0-12.4.99', + ExtEmConfRector::PHP_VERSION_CONSTRAINT => '8.2.0-8.4.99', + ExtEmConfRector::TYPO3_VERSION_CONSTRAINT => '13.4.0-13.4.99', ExtEmConfRector::ADDITIONAL_VALUES_TO_BE_REMOVED => [], ]) // If you use withImportNames(), you should consider excluding some TYPO3 files. diff --git a/shell.nix b/shell.nix index fe13e287..81275220 100644 --- a/shell.nix +++ b/shell.nix @@ -1,12 +1,13 @@ { pkgs ? import { } ,php81 ? import - ,phpVersion ? "php81" + ,php85 ? import (fetchTarball "https://github.com/piotrkwiecinski/nixpkgs/archive/1c614d75004b9eb1ecda6ddeb959c4f544403de5.tar.gz") {} + ,phpVersion ? "php82" }: let phpVersionPkgs = - if (phpVersion == "php81") then php81.packages.x86_64-linux.${phpVersion} + if (phpVersion == "php85") then php85.${phpVersion} else pkgs.${phpVersion}; php = phpVersionPkgs.buildEnv { extensions = { enabled, all }: enabled ++ (with all; [ From 10bc6053f83baedf0bb6145c5a3b4d845613f1bf Mon Sep 17 00:00:00 2001 From: Daniel Gohlke Date: Wed, 28 Jan 2026 13:45:41 +0100 Subject: [PATCH 2/4] [TASK] Remove unused setter from domain models The objects are to be converted to read model in the medium term. To this end, most setters have been removed and the tests adapted accordingly. The seats used for the order are then to be set using separate models. Relates: #87 --- Classes/Domain/Model/AbstractEventDate.php | 15 - Classes/Domain/Model/Event.php | 91 +---- Classes/Domain/Model/EventDate.php | 95 ----- Classes/Domain/Model/PriceCategory.php | 40 --- Classes/Domain/Model/SpecialPrice.php | 15 - .../Domain/Model/AbstractSpecialPrice.php | 57 ++- .../Functional/Domain/Model/EventDateTest.php | 11 +- .../Domain/Model/PriceCategoryTest.php | 11 +- Tests/ObjectAccess.php | 23 ++ .../Domain/Model/AbstractEventDateTest.php | 40 +-- Tests/Unit/Domain/Model/CalenderEntryTest.php | 4 + Tests/Unit/Domain/Model/CategoryTest.php | 27 +- .../Unit/Domain/Model/Dto/EventDemandTest.php | 24 +- Tests/Unit/Domain/Model/EventDateTest.php | 324 ++++++++++++------ Tests/Unit/Domain/Model/EventTest.php | 248 +++++++++++--- Tests/Unit/Domain/Model/PriceCategoryTest.php | 86 ++--- Tests/Unit/Domain/Model/SpecialPriceTest.php | 47 ++- 17 files changed, 623 insertions(+), 535 deletions(-) create mode 100644 Tests/ObjectAccess.php diff --git a/Classes/Domain/Model/AbstractEventDate.php b/Classes/Domain/Model/AbstractEventDate.php index 68a338b3..72d3f3c2 100644 --- a/Classes/Domain/Model/AbstractEventDate.php +++ b/Classes/Domain/Model/AbstractEventDate.php @@ -27,28 +27,13 @@ public function getBegin(): ?DateTime return $this->begin; } - public function setBegin(DateTime $begin): void - { - $this->begin = $begin; - } - public function getEnd(): ?DateTime { return $this->end; } - public function setEnd(DateTime $end): void - { - $this->end = $end; - } - public function getNote(): string { return $this->note; } - - public function setNote(string $note): void - { - $this->note = $note; - } } diff --git a/Classes/Domain/Model/Event.php b/Classes/Domain/Model/Event.php index 1382e5a2..53bc1e77 100644 --- a/Classes/Domain/Model/Event.php +++ b/Classes/Domain/Model/Event.php @@ -87,71 +87,36 @@ public function isVirtualProduct(): bool return $this->virtualProduct; } - public function setVirtualProduct(bool $virtualProduct): void - { - $this->virtualProduct = $virtualProduct; - } - public function getFormDefinition(): ?string { return $this->formDefinition; } - public function setFormDefinition(string $formDefinition): void - { - $this->formDefinition = $formDefinition; - } - public function getSku(): string { return $this->sku; } - public function setSku(string $sku): void - { - $this->sku = $sku; - } - public function getTitle(): string { return $this->title; } - public function setTitle(string $title): void - { - $this->title = $title; - } - public function getTeaser(): string { return $this->teaser; } - public function setTeaser(string $teaser): void - { - $this->teaser = $teaser; - } - public function getDescription(): string { return $this->description; } - public function setDescription(string $description): void - { - $this->description = $description; - } - public function getAudience(): string { return $this->audience; } - public function setAudience(string $audience): void - { - $this->audience = $audience; - } - public function getImages(): ?ObjectStorage { return $this->images; @@ -166,21 +131,11 @@ public function getFirstImage(): ?FileReference return array_shift($images); } - public function setImages(ObjectStorage $images): void - { - $this->images = $images; - } - public function getFiles(): ?ObjectStorage { return $this->files; } - public function setFiles(ObjectStorage $files): void - { - $this->files = $files; - } - /** * @return ObjectStorage */ @@ -194,22 +149,8 @@ public function getFirstEventDate(): ?EventDate if (!$this->getEventDates()) { return null; } - return $this->getEventDates()->current(); - } - - public function setEventDates(ObjectStorage $eventDates): void - { - $this->eventDates = $eventDates; - } - - public function addRelatedEvent(self $relatedEvent): void - { - $this->relatedEvents->attach($relatedEvent); - } - public function removeRelatedEvent(self $relatedEvent): void - { - $this->relatedEvents->detach($relatedEvent); + return $this->getEventDates()->current(); } /** @@ -220,21 +161,6 @@ public function getRelatedEvents(): ?ObjectStorage return $this->relatedEvents; } - public function setRelatedEvents(ObjectStorage $relatedEvents): void - { - $this->relatedEvents = $relatedEvents; - } - - public function addRelatedEventFrom(self $relatedEventFrom): void - { - $this->relatedEventsFrom->attach($relatedEventFrom); - } - - public function removeRelatedEventFrom(self $relatedEventFrom): void - { - $this->relatedEventsFrom->detach($relatedEventFrom); - } - /** * @return ObjectStorage */ @@ -243,28 +169,13 @@ public function getRelatedEventsFrom(): ?ObjectStorage return $this->relatedEventsFrom; } - public function setRelatedEventsFrom(ObjectStorage $relatedEventsFrom): void - { - $this->relatedEventsFrom = $relatedEventsFrom; - } - public function getTaxClassId(): int { return $this->taxClassId; } - public function setTaxClassId(int $taxClassId): void - { - $this->taxClassId = $taxClassId; - } - public function getMetaDescription(): string { return $this->metaDescription; } - - public function setMetaDescription(string $metaDescription): void - { - $this->metaDescription = $metaDescription; - } } diff --git a/Classes/Domain/Model/EventDate.php b/Classes/Domain/Model/EventDate.php index 2e5edf03..e4b9bc69 100644 --- a/Classes/Domain/Model/EventDate.php +++ b/Classes/Domain/Model/EventDate.php @@ -88,41 +88,21 @@ public function getSku(): string return $this->sku; } - public function setSku(string $sku): void - { - $this->sku = $sku; - } - public function getTitle(): string { return $this->title; } - public function setTitle(string $title): void - { - $this->title = $title; - } - public function getLocation(): string { return $this->location; } - public function setLocation(string $location): void - { - $this->location = $location; - } - public function getLecturer(): string { return $this->lecturer; } - public function setLecturer(string $lecturer): void - { - $this->lecturer = $lecturer; - } - /** * @return ObjectStorage */ @@ -140,11 +120,6 @@ public function getFirstImage(): ?FileReference return array_shift($images); } - public function setImages(ObjectStorage $images): void - { - $this->images = $images; - } - /** * @return ObjectStorage */ @@ -153,11 +128,6 @@ public function getFiles(): ?ObjectStorage return $this->files; } - public function setFiles(ObjectStorage $files): void - { - $this->files = $files; - } - public function isBookable(): bool { return $this->bookable; @@ -173,11 +143,6 @@ public function getPrice(): float return $this->price; } - public function setPrice(float $price): void - { - $this->price = $price; - } - /** * @return ObjectStorage */ @@ -186,31 +151,11 @@ public function getSpecialPrices(): ?ObjectStorage return $this->specialPrices; } - public function addSpecialPrice(SpecialPrice $specialPrice): void - { - $this->specialPrices->attach($specialPrice); - } - - public function removeSpecialPrice(SpecialPrice $specialPrice): void - { - $this->specialPrices->detach($specialPrice); - } - - public function setSpecialPrices(ObjectStorage $specialPrices): void - { - $this->specialPrices = $specialPrices; - } - public function isPriceCategorized(): bool { return $this->priceCategorized; } - public function setPriceCategorized(bool $priceCategorized): void - { - $this->priceCategorized = $priceCategorized; - } - public function getPriceCategories(): ?ObjectStorage { return $this->priceCategories; @@ -227,21 +172,6 @@ public function getFirstAvailablePriceCategory(): ?PriceCategory return null; } - public function addPriceCategory(PriceCategory $priceCategory): void - { - $this->priceCategories->attach($priceCategory); - } - - public function removePriceCategory(PriceCategory $priceCategory): void - { - $this->priceCategories->detach($priceCategory); - } - - public function setPriceCategories(ObjectStorage $priceCategories): void - { - $this->priceCategories = $priceCategories; - } - public function getBestSpecialPrice(?array $frontendUserGroupIds = null): ?SpecialPrice { if (is_null($frontendUserGroupIds)) { @@ -310,41 +240,21 @@ public function getFirstCalendarEntry(): ?CalendarEntry return $this->getCalendarEntries()->current(); } - public function setCalendarEntries(ObjectStorage $calendarEntries): void - { - $this->calendarEntries = $calendarEntries; - } - public function getEvent(): ?Event { return $this->event; } - public function setEvent(Event $event): void - { - $this->event = $event; - } - public function isHandleSeats(): bool { return $this->handleSeats; } - public function setHandleSeats(bool $handleSeats): void - { - $this->handleSeats = $handleSeats; - } - public function isHandleSeatsInPriceCategory(): bool { return $this->handleSeatsInPriceCategory; } - public function setHandleSeatsInPriceCategory(bool $handleSeatsInPriceCategory): void - { - $this->handleSeatsInPriceCategory = $handleSeatsInPriceCategory; - } - /** * Returns the number of seats in the event if handling the number of seats * is enabled, otherwise return 0. @@ -366,11 +276,6 @@ public function getSeatsNumber(): int return $this->seatsNumber; } - public function setSeatsNumber(int $seatsNumber): void - { - $this->seatsNumber = $seatsNumber; - } - /** * Returns the number of taken seats in the event if handling the number of * seats is enabled, otherwise return 0. diff --git a/Classes/Domain/Model/PriceCategory.php b/Classes/Domain/Model/PriceCategory.php index 4ab96a34..02f9eeb3 100644 --- a/Classes/Domain/Model/PriceCategory.php +++ b/Classes/Domain/Model/PriceCategory.php @@ -50,41 +50,21 @@ public function getSku(): string return $this->sku; } - public function setSku(string $sku): void - { - $this->sku = $sku; - } - public function getEventDate(): EventDate { return $this->eventDate; } - public function setEventDate(EventDate $eventDate): void - { - $this->eventDate = $eventDate; - } - public function getTitle(): string { return $this->title; } - public function setTitle(string $title): void - { - $this->title = $title; - } - public function getPrice(): float { return $this->price; } - public function setPrice(float $price): void - { - $this->price = $price; - } - /** * @return ObjectStorage */ @@ -93,31 +73,11 @@ public function getSpecialPrices(): ?ObjectStorage return $this->specialPrices; } - public function addSpecialPrice(SpecialPrice $specialPrice): void - { - $this->specialPrices->attach($specialPrice); - } - - public function removeSpecialPrice(SpecialPrice $specialPrice): void - { - $this->specialPrices->detach($specialPrice); - } - - public function setSpecialPrices(ObjectStorage $specialPrices): void - { - $this->specialPrices = $specialPrices; - } - public function getSeatsNumber(): int { return $this->seatsNumber; } - public function setSeatsNumber(int $seatsNumber): void - { - $this->seatsNumber = $seatsNumber; - } - public function getSeatsTaken(): int { return $this->seatsTaken; diff --git a/Classes/Domain/Model/SpecialPrice.php b/Classes/Domain/Model/SpecialPrice.php index 2a68fa82..f967b4a1 100644 --- a/Classes/Domain/Model/SpecialPrice.php +++ b/Classes/Domain/Model/SpecialPrice.php @@ -30,28 +30,13 @@ public function getTitle(): string return $this->title; } - public function setTitle(string $title): void - { - $this->title = $title; - } - public function getPrice(): float { return $this->price; } - public function setPrice(float $price): void - { - $this->price = $price; - } - public function getFrontendUserGroup(): ?FrontendUserGroup { return $this->frontendUserGroup; } - - public function setFrontendUserGroup(FrontendUserGroup $frontendUserGroup): void - { - $this->frontendUserGroup = $frontendUserGroup; - } } diff --git a/Tests/Functional/Domain/Model/AbstractSpecialPrice.php b/Tests/Functional/Domain/Model/AbstractSpecialPrice.php index af9836dd..dd21a618 100644 --- a/Tests/Functional/Domain/Model/AbstractSpecialPrice.php +++ b/Tests/Functional/Domain/Model/AbstractSpecialPrice.php @@ -15,6 +15,7 @@ use Extcode\CartEvents\Domain\Model\EventDate; use Extcode\CartEvents\Domain\Model\PriceCategory; use Extcode\CartEvents\Domain\Model\SpecialPrice; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\Test; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\UserAspect; @@ -28,17 +29,17 @@ abstract class AbstractSpecialPrice extends FunctionalTestCase protected EventDate|PriceCategory $subject; - protected function tearDown(): void + protected function setUp(): void { - unset($this->subject); + parent::setUp(); + + ObjectAccess::setProperty($this->subject, 'price', $this->price); + ObjectAccess::setProperty($this->subject, 'specialPrices', new ObjectStorage()); } #[Test] public function getBestSpecialPriceReturnsNullIfEventHasNoSpecialPrice(): void { - $objectStorage = new ObjectStorage(); - $this->subject->setSpecialPrices($objectStorage); - self::assertNull( $this->subject->getBestSpecialPrice() ); @@ -52,10 +53,9 @@ public function getBestSpecialPriceReturnsNullIfEventHasSpecialPriceButUserHasNo $frontendUserGroup1 = self::createStub(FrontendUserGroup::class); $frontendUserGroup1->method('getUid')->willReturn(42); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(10.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice1); self::assertNull( $this->subject->getBestSpecialPrice() @@ -75,10 +75,9 @@ public function getBestSpecialPriceReturnsSpecialPriceIfEventHasASpecialPriceWit $frontendUserGroup1 = self::createStub(FrontendUserGroup::class); $frontendUserGroup1->method('getUid')->willReturn(42); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(10.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice1); self::assertNull( $this->subject->getBestSpecialPrice() @@ -98,10 +97,9 @@ public function getBestSpecialPriceReturnsSpecialPriceIfEventHasASpecialPriceWit $frontendUserGroup1 = self::createStub(FrontendUserGroup::class); $frontendUserGroup1->method('getUid')->willReturn(42); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(10.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice1); self::assertSame( $specialPrice1, @@ -124,12 +122,11 @@ public function getBestSpecialPriceReturnsBestSpecialPriceIfEventHasSpecialPrice $frontendUserGroup2 = self::createStub(FrontendUserGroup::class); $frontendUserGroup2->method('getUid')->willReturn(43); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(10.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); + $specialPrices->attach($specialPrice1); $specialPrice2 = $this->createSpecialPriceForFrontendUserGroup(8.00, $frontendUserGroup2); - $objectStorage->attach($specialPrice2); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice2); self::assertSame( $specialPrice2, @@ -154,14 +151,13 @@ public function getBestSpecialPriceReturnsBestSpecialPriceForUserGroupIfEventHas $frontendUserGroup3 = self::createStub(FrontendUserGroup::class); $frontendUserGroup3->method('getUid')->willReturn(44); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(9.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); + $specialPrices->attach($specialPrice1); $specialPrice2 = $this->createSpecialPriceForFrontendUserGroup(11.00, $frontendUserGroup2); - $objectStorage->attach($specialPrice2); + $specialPrices->attach($specialPrice2); $specialPrice3 = $this->createSpecialPriceForFrontendUserGroup(7.00, $frontendUserGroup3); - $objectStorage->attach($specialPrice3); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice3); self::assertSame( $specialPrice1, @@ -182,10 +178,9 @@ public function getBestSpecialPriceReturnsSpecialPriceIfEventHasASpecialPriceWit $frontendUserGroup1 = self::createStub(FrontendUserGroup::class); $frontendUserGroup1->method('getUid')->willReturn(42); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(10.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice1); self::assertNull( $this->subject->getBestSpecialPrice() @@ -205,10 +200,9 @@ public function getBestPriceReturnsPriceIfSpecialPriceIsGreater(): void $frontendUserGroup1 = self::createStub(FrontendUserGroup::class); $frontendUserGroup1->method('getUid')->willReturn(42); - $objectStorage = new ObjectStorage(); + $specialPrices = $this->subject->getSpecialPrices(); $specialPrice1 = $this->createSpecialPriceForFrontendUserGroup(20.00, $frontendUserGroup1); - $objectStorage->attach($specialPrice1); - $this->subject->setSpecialPrices($objectStorage); + $specialPrices->attach($specialPrice1); self::assertSame( $specialPrice1, @@ -224,8 +218,9 @@ public function getBestPriceReturnsPriceIfSpecialPriceIsGreater(): void private function createSpecialPriceForFrontendUserGroup(float $price, FrontendUserGroup $frontendUserGroup): SpecialPrice { $specialPrice = new SpecialPrice(); - $specialPrice->setPrice($price); - $specialPrice->setFrontendUserGroup($frontendUserGroup); + + ObjectAccess::setProperty($specialPrice, 'price', $price); + ObjectAccess::setProperty($specialPrice, 'frontendUserGroup', $frontendUserGroup); return $specialPrice; } diff --git a/Tests/Functional/Domain/Model/EventDateTest.php b/Tests/Functional/Domain/Model/EventDateTest.php index 8d186cde..bb016e8e 100644 --- a/Tests/Functional/Domain/Model/EventDateTest.php +++ b/Tests/Functional/Domain/Model/EventDateTest.php @@ -20,8 +20,15 @@ class EventDateTest extends AbstractSpecialPrice protected function setUp(): void { $this->price = 17.49; - $this->subject = new EventDate(); - $this->subject->setPrice($this->price); + + parent::setUp(); + } + + protected function tearDown(): void + { + unset($this->subject); + + parent::tearDown(); } } diff --git a/Tests/Functional/Domain/Model/PriceCategoryTest.php b/Tests/Functional/Domain/Model/PriceCategoryTest.php index d2a5ab16..8bf3554d 100644 --- a/Tests/Functional/Domain/Model/PriceCategoryTest.php +++ b/Tests/Functional/Domain/Model/PriceCategoryTest.php @@ -20,8 +20,15 @@ class PriceCategoryTest extends AbstractSpecialPrice protected function setUp(): void { $this->price = 13.44; - $this->subject = new PriceCategory(); - $this->subject->setPrice($this->price); + + parent::setUp(); + } + + protected function tearDown(): void + { + unset($this->subject); + + parent::tearDown(); } } diff --git a/Tests/ObjectAccess.php b/Tests/ObjectAccess.php new file mode 100644 index 00000000..f8a43c0e --- /dev/null +++ b/Tests/ObjectAccess.php @@ -0,0 +1,23 @@ +setValue($instance, $value); + } +} diff --git a/Tests/Unit/Domain/Model/AbstractEventDateTest.php b/Tests/Unit/Domain/Model/AbstractEventDateTest.php index caa31622..fb3452ac 100644 --- a/Tests/Unit/Domain/Model/AbstractEventDateTest.php +++ b/Tests/Unit/Domain/Model/AbstractEventDateTest.php @@ -13,6 +13,7 @@ use DateTime; use Extcode\CartEvents\Domain\Model\AbstractEventDate; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -20,59 +21,58 @@ #[CoversClass(AbstractEventDate::class)] class AbstractEventDateTest extends UnitTestCase { + protected bool $resetSingletonInstances = true; + protected $eventDate; protected function setUp(): void { + parent::setUp(); + $this->eventDate = new class extends AbstractEventDate {}; } protected function tearDown(): void { unset($this->eventDate); + + parent::tearDown(); } #[Test] - public function getBeginReturnsInitialValueNull(): void + public function getBeginReturnsBegin(): void { self::assertNull( $this->eventDate->getBegin() ); - } - #[Test] - public function setBeginSetsBegin(): void - { $dateString = '2024-09-01 15:43:38'; $format = 'Y-m-d H:i:s'; - $dateTime = DateTime::createFromFormat($format, $dateString); + $begin = DateTime::createFromFormat($format, $dateString); - $this->eventDate->setBegin($dateTime); + ObjectAccess::setProperty($this->eventDate, 'begin', $begin); self::assertSame( - $dateTime, + $begin, $this->eventDate->getBegin() ); } #[Test] - public function getEndReturnsInitialValueNull(): void + public function getEndReturnsEnd(): void { self::assertNull( $this->eventDate->getEnd() ); - } - #[Test] - public function setEndSetsEnd(): void - { $dateString = '2024-09-01 20:01:01'; $format = 'Y-m-d H:i:s'; - $dateTime = DateTime::createFromFormat($format, $dateString); - $this->eventDate->setEnd($dateTime); + $end = DateTime::createFromFormat($format, $dateString); + + ObjectAccess::setProperty($this->eventDate, 'end', $end); self::assertSame( - $dateTime, + $end, $this->eventDate->getEnd() ); } @@ -84,15 +84,11 @@ public function getNoteReturnsInitialValueForNote(): void '', $this->eventDate->getNote() ); - } - #[Test] - public function setNoteSetsNote(): void - { - $this->eventDate->setNote('Note'); + ObjectAccess::setProperty($this->eventDate, 'note', 'note'); self::assertSame( - 'Note', + 'note', $this->eventDate->getNote() ); } diff --git a/Tests/Unit/Domain/Model/CalenderEntryTest.php b/Tests/Unit/Domain/Model/CalenderEntryTest.php index c13753e5..887adad5 100644 --- a/Tests/Unit/Domain/Model/CalenderEntryTest.php +++ b/Tests/Unit/Domain/Model/CalenderEntryTest.php @@ -24,12 +24,16 @@ class CalenderEntryTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->calendarEntry = new CalendarEntry(); } protected function tearDown(): void { unset($this->calendarEntry); + + parent::tearDown(); } #[Test] diff --git a/Tests/Unit/Domain/Model/CategoryTest.php b/Tests/Unit/Domain/Model/CategoryTest.php index c9f6b9c0..1a938a51 100644 --- a/Tests/Unit/Domain/Model/CategoryTest.php +++ b/Tests/Unit/Domain/Model/CategoryTest.php @@ -12,6 +12,7 @@ */ use Extcode\CartEvents\Domain\Model\Category; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -23,12 +24,16 @@ class CategoryTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->category = new Category(); } protected function tearDown(): void { unset($this->category); + + parent::tearDown(); } #[Test] @@ -40,16 +45,34 @@ public function categoryExtendsExtbaseCategoryModel(): void #[Test] public function getCartEventListPidReturnsInitialValueNull(): void { + $category = new Category(); + self::assertNull( - $this->category->getCartEventListPid() + $category->getCartEventListPid() + ); + + ObjectAccess::setProperty($category, 'cartEventListPid', 31); + + self::assertSame( + 31, + $category->getCartEventListPid() ); } #[Test] public function getCartEventShowPidReturnsInitialValueNull(): void { + $category = new Category(); + self::assertNull( - $this->category->getCartEventShowPid() + $category->getCartEventShowPid() + ); + + ObjectAccess::setProperty($category, 'cartEventShowPid', 31); + + self::assertSame( + 31, + $category->getCartEventShowPid() ); } } diff --git a/Tests/Unit/Domain/Model/Dto/EventDemandTest.php b/Tests/Unit/Domain/Model/Dto/EventDemandTest.php index 5ce6b337..295be465 100644 --- a/Tests/Unit/Domain/Model/Dto/EventDemandTest.php +++ b/Tests/Unit/Domain/Model/Dto/EventDemandTest.php @@ -23,12 +23,16 @@ class EventDemandTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->eventDemand = new EventDemand(); } protected function tearDown(): void { unset($this->eventDemand); + + parent::tearDown(); } #[Test] @@ -71,7 +75,25 @@ public function setTitleSetsTitle(): void ); } - // todo: categories + #[Test] + public function getCategoriesReturnsInitialValueForCategories(): void + { + self::assertSame( + [], + $this->eventDemand->getCategories() + ); + } + + #[Test] + public function setCategoriesSetsCategories(): void + { + $this->eventDemand->setCategories([2, 3, 5, 7]); + + self::assertSame( + [2, 3, 5, 7], + $this->eventDemand->getCategories() + ); + } #[Test] public function getOrderReturnsInitialValueForOrder(): void diff --git a/Tests/Unit/Domain/Model/EventDateTest.php b/Tests/Unit/Domain/Model/EventDateTest.php index 3a95d764..234c5ce5 100644 --- a/Tests/Unit/Domain/Model/EventDateTest.php +++ b/Tests/Unit/Domain/Model/EventDateTest.php @@ -13,8 +13,10 @@ use Extcode\CartEvents\Domain\Model\AbstractEventDate; use Extcode\CartEvents\Domain\Model\EventDate; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; #[CoversClass(EventDate::class)] @@ -24,12 +26,16 @@ class EventDateTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->eventDate = new EventDate(); } protected function tearDown(): void { unset($this->eventDate); + + parent::tearDown(); } #[Test] @@ -39,90 +45,172 @@ public function eventDateExtendsAbstractEventDate(): void } #[Test] - public function getSkuReturnsInitialValueForSku(): void + public function getSkuReturnsSku(): void { + $eventDate = new EventDate(); + self::assertSame( '', - $this->eventDate->getSku() + $eventDate->getSku() ); - } - #[Test] - public function setSkuSetsSku(): void - { - $this->eventDate->setSku('sku'); + ObjectAccess::setProperty($eventDate, 'sku', 'sku'); self::assertSame( 'sku', - $this->eventDate->getSku() + $eventDate->getSku() ); } #[Test] - public function getTitleReturnsInitialValueForTitle(): void + public function getTitleReturnsTitle(): void { + $eventDate = new EventDate(); + self::assertSame( '', - $this->eventDate->getTitle() + $eventDate->getTitle() + ); + + ObjectAccess::setProperty($eventDate, 'title', 'title'); + + self::assertSame( + 'title', + $eventDate->getTitle() ); } #[Test] - public function setTitleSetsTitle(): void + public function getLocationReturnsLocation(): void { - $this->eventDate->setTitle('Title'); + $eventDate = new EventDate(); + + self::assertSame( + '', + $eventDate->getLocation() + ); + + ObjectAccess::setProperty($eventDate, 'location', 'location'); self::assertSame( - 'Title', - $this->eventDate->getTitle() + 'location', + $eventDate->getLocation() ); } #[Test] - public function getLocationReturnsInitialValueForLocation(): void + public function getLecturerReturnsLecturer(): void { + $eventDate = new EventDate(); + self::assertSame( '', - $this->eventDate->getLocation() + $eventDate->getLecturer() + ); + + ObjectAccess::setProperty($eventDate, 'lecturer', 'lecturer'); + + self::assertSame( + 'lecturer', + $eventDate->getLecturer() ); } #[Test] - public function setLocationSetsLocation(): void + public function getImagesReturnsImages(): void { - $this->eventDate->setLocation('Location'); + $images = $this->eventDate->getImages(); self::assertSame( - 'Location', - $this->eventDate->getLocation() + $images, + $this->eventDate->getImages() + ); + + self::assertSame( + 0, + $this->eventDate->getImages()->count() + ); + + $image1 = self::createStub(FileReference::class); + $images->attach($image1); + $image2 = self::createStub(FileReference::class); + $images->attach($image2); + + self::assertSame( + $images, + $this->eventDate->getImages() + ); + + self::assertSame( + 2, + $this->eventDate->getImages()->count() ); } #[Test] - public function getLecturerReturnsInitialValueForLecturer(): void + public function getFirstImageReturnsFirstImage(): void { + $images = $this->eventDate->getImages(); + + self::assertNull( + $this->eventDate->getFirstImage() + ); + + $image1 = self::createStub(FileReference::class); + $images->attach($image1); + $image2 = self::createStub(FileReference::class); + $images->attach($image2); + self::assertSame( - '', - $this->eventDate->getLecturer() + $image1, + $this->eventDate->getFirstImage() ); } #[Test] - public function setLecturerSetsLecturer(): void + public function getFilesReturnsFiles(): void { - $this->eventDate->setLecturer('Lecturer'); + $files = $this->eventDate->getFiles(); + + self::assertSame( + $files, + $this->eventDate->getFiles() + ); + + self::assertSame( + 0, + $this->eventDate->getFiles()->count() + ); + + $file1 = self::createStub(FileReference::class); + $files->attach($file1); + $file2 = self::createStub(FileReference::class); + $files->attach($file2); + + self::assertSame( + $files, + $this->eventDate->getFiles() + ); self::assertSame( - 'Lecturer', - $this->eventDate->getLecturer() + 2, + $this->eventDate->getFiles()->count() ); } #[Test] - public function isBookableReturnsInitialValueForBookable(): void + public function isBookableReturnsBookable(): void { + $eventDate = new EventDate(); + self::assertFalse( - $this->eventDate->isBookable() + $eventDate->isBookable() + ); + + ObjectAccess::setProperty($eventDate, 'bookable', true); + + self::assertTrue( + $eventDate->isBookable() ); } @@ -137,68 +225,65 @@ public function setBookableSetsBookable(): void } #[Test] - public function isPriceCategorizedReturnsInitialValueForPriceCategorized(): void + public function isPriceCategorizedReturnsCategorized(): void { + $eventDate = new EventDate(); + self::assertFalse( - $this->eventDate->isPriceCategorized() + $eventDate->isPriceCategorized() ); - } - #[Test] - public function setPriceCategorizedSetsPriceCategorized(): void - { - $this->eventDate->setPriceCategorized(true); + ObjectAccess::setProperty($eventDate, 'priceCategorized', true); self::assertTrue( - $this->eventDate->isPriceCategorized() + $eventDate->isPriceCategorized() ); } #[Test] - public function isHandleSeatsReturnsInitialValueForHandleSeats(): void + public function isHandleSeatsReturnsHandleSeats(): void { + $eventDate = new EventDate(); + self::assertFalse( - $this->eventDate->isHandleSeats() + $eventDate->isHandleSeats() ); - } - #[Test] - public function setHandleSeatsSetsHandleSeats(): void - { - $this->eventDate->setHandleSeats(true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertTrue( - $this->eventDate->isHandleSeats() + $eventDate->isHandleSeats() ); } #[Test] - public function isHandleSeatsInPriceCategoryReturnsInitialValueForHandleSeatsInPriceCategory(): void + public function isHandleSeatsInPriceCategoryReturnsHandleSeatsInPriceCategory(): void { + + $eventDate = new EventDate(); + self::assertFalse( - $this->eventDate->isHandleSeatsInPriceCategory() + $eventDate->isHandleSeatsInPriceCategory() ); - } - #[Test] - public function setHandleSeatsInPriceCategorySetsHandleSeatsInPriceCategory(): void - { - $this->eventDate->setHandleSeatsInPriceCategory(true); + ObjectAccess::setProperty($eventDate, 'handleSeatsInPriceCategory', true); self::assertTrue( - $this->eventDate->isHandleSeatsInPriceCategory() + $eventDate->isHandleSeatsInPriceCategory() ); } #[Test] public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse() { + $eventDate = new EventDate(); + self::assertSame( 0, $this->eventDate->getSeatsNumber() ); - $this->eventDate->setSeatsNumber(15); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); self::assertSame( 0, @@ -209,190 +294,221 @@ public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse() #[Test] public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTrue() { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsNumber() + $eventDate->getSeatsNumber() ); } #[Test] public function setSeatsNumberSetsSeatsNumber() { - $this->eventDate->setSeatsNumber(15); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); self::assertSame( 0, - $this->eventDate->getSeatsNumber() + $eventDate->getSeatsNumber() ); - $this->eventDate->setHandleSeats(true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 15, - $this->eventDate->getSeatsNumber() + $eventDate->getSeatsNumber() ); } #[Test] public function getSeatsTakenReturnsZeroIfHandleSeatsIsFalse() { + $eventDate = new EventDate(); + self::assertSame( 0, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); - $this->eventDate->setSeatsTaken(15); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); self::assertSame( 0, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); } #[Test] public function getSeatsTakenReturnsInitialValueForSeatsTakenIfHandleSeatsIsTrue() { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); } #[Test] public function setSeatsTakenSetsSeatsTaken() { - $this->eventDate->setSeatsTaken(15); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'seatsTaken', 15); self::assertSame( 0, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); - $this->eventDate->setHandleSeats(true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 15, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); } #[Test] public function getSeatsAvailableReturnsZeroIfHandleSeatsIsFalse() { + $eventDate = new EventDate(); + self::assertSame( 0, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); - $this->eventDate->setSeatsNumber(15); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); self::assertSame( 0, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); } #[Test] public function getSeatsAvailableReturnsDifferenceOfInitialValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue() { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); } #[Test] public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue() { - $this->eventDate->setSeatsNumber(30); - $this->eventDate->setSeatsTaken(13); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'seatsNumber', 30); + ObjectAccess::setProperty($eventDate, 'seatsTaken', 13); self::assertSame( 0, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); - $this->eventDate->setHandleSeats(true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 17, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); } #[TEST] public function isAvailableReturnsFalseIfBookableIsFalse() { - $this->eventDate->setHandleSeats(true); - $this->eventDate->setSeatsNumber(20); - $this->eventDate->setBookable(false); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); + ObjectAccess::setProperty($eventDate, 'bookable', false); self::assertFalse( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); - $this->eventDate->setBookable(true); + ObjectAccess::setProperty($eventDate, 'bookable', true); self::assertTrue( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); } #[TEST] public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsFalse() { - $this->eventDate->setBookable(true); - $this->eventDate->setHandleSeats(false); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'bookable', true); + ObjectAccess::setProperty($eventDate, 'handleSeats', false); + self::assertTrue( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); - $this->eventDate->setHandleSeats(true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); + self::assertFalse( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); } #[TEST] public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsGreaterThanZero() { - $this->eventDate->setBookable(true); - $this->eventDate->setHandleSeats(true); - $this->eventDate->setSeatsNumber(2); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'bookable', true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 2); + self::assertTrue( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); - $this->eventDate->setSeatsTaken(1); + ObjectAccess::setProperty($eventDate, 'seatsTaken', 1); + self::assertTrue( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); - $this->eventDate->setSeatsTaken(2); + ObjectAccess::setProperty($eventDate, 'seatsTaken', 2); + self::assertFalse( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); } #[TEST] public function isAvailableReturnsFalseIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsLowerOrEqualToZero() { - $this->eventDate->setBookable(true); - $this->eventDate->setHandleSeats(true); - $this->eventDate->setSeatsNumber(0); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'bookable', true); + ObjectAccess::setProperty($eventDate, 'handleSeats', true); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 0); + self::assertFalse( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); - $this->eventDate->setSeatsTaken(1); + ObjectAccess::setProperty($eventDate, 'seatsTaken', 1); + self::assertFalse( - $this->eventDate->isAvailable() + $eventDate->isAvailable() ); } } diff --git a/Tests/Unit/Domain/Model/EventTest.php b/Tests/Unit/Domain/Model/EventTest.php index 35e23ab4..d4dba94a 100644 --- a/Tests/Unit/Domain/Model/EventTest.php +++ b/Tests/Unit/Domain/Model/EventTest.php @@ -12,8 +12,11 @@ */ use Extcode\CartEvents\Domain\Model\Event; +use Extcode\CartEvents\Domain\Model\EventDate; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; #[CoversClass(Event::class)] @@ -23,170 +26,309 @@ class EventTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->event = new Event(); } protected function tearDown(): void { unset($this->event); + + parent::tearDown(); } #[Test] public function isVirtualProductReturnsInitialValueForVirtualProduct(): void { + $event = new Event(); + self::assertTrue( - $this->event->isVirtualProduct() + $event->isVirtualProduct() ); - } - #[Test] - public function setVirtualProductSetsVirtualProduct(): void - { - $this->event->setVirtualProduct(false); + ObjectAccess::setProperty($event, 'virtualProduct', false); self::assertFalse( - $this->event->isVirtualProduct() + $event->isVirtualProduct() ); } #[Test] - public function getFormDefinitionReturnsInitialValueNull(): void + public function getFormDefinitionReturnsFormDefinition(): void { + $event = new Event(); + self::assertNull( - $this->event->getFormDefinition() + $event->getFormDefinition() ); - } - #[Test] - public function setFormDefinitionSetsFormDefinition(): void - { - $this->event->setFormDefinition('EXT:cart_events/Resources/Private/Forms/test-form.form.yaml'); + ObjectAccess::setProperty($event, 'formDefinition', 'EXT:cart_events/Resources/Private/Forms/test-form.form.yaml'); self::assertSame( 'EXT:cart_events/Resources/Private/Forms/test-form.form.yaml', - $this->event->getFormDefinition() + $event->getFormDefinition() ); } #[Test] - public function getSkuReturnsInitialValueForSku(): void + public function getSkuReturnsSku(): void { + $event = new Event(); + self::assertSame( '', $this->event->getSku() ); + + ObjectAccess::setProperty($event, 'sku', 'sku'); + + self::assertSame( + 'sku', + $event->getSku() + ); } #[Test] - public function setSkuSetsSku(): void + public function getTitleReturnsTitle(): void { - $this->event->setSku('sku'); + $event = new Event(); self::assertSame( - 'sku', - $this->event->getSku() + '', + $this->event->getTitle() + ); + + ObjectAccess::setProperty($event, 'title', 'title'); + + self::assertSame( + 'title', + $event->getTitle() ); } #[Test] - public function getTitleReturnsInitialValueForTitle(): void + public function getTeaserReturnsTeaser(): void { + $event = new Event(); + self::assertSame( '', - $this->event->getTitle() + $this->event->getTeaser() + ); + + ObjectAccess::setProperty($event, 'teaser', 'teaser'); + + self::assertSame( + 'teaser', + $event->getTeaser() ); } #[Test] - public function setTitleSetsTitle(): void + public function getDescriptionReturnsDescription(): void { - $this->event->setTitle('Title'); + $event = new Event(); self::assertSame( - 'Title', - $this->event->getTitle() + '', + $this->event->getDescription() + ); + + ObjectAccess::setProperty($event, 'description', 'description'); + + self::assertSame( + 'description', + $event->getDescription() ); } #[Test] - public function getTeaserReturnsInitialValueForTeaser(): void + public function getAudienceReturnsAudience(): void { + $event = new Event(); + self::assertSame( '', - $this->event->getTeaser() + $this->event->getAudience() + ); + + ObjectAccess::setProperty($event, 'audience', 'audience'); + + self::assertSame( + 'audience', + $event->getAudience() ); } #[Test] - public function setTeaserSetsTeaser(): void + public function getImagesReturnsImages(): void { - $this->event->setTeaser('Teaser'); + $images = $this->event->getImages(); self::assertSame( - 'Teaser', - $this->event->getTeaser() + $images, + $this->event->getImages() + ); + + self::assertSame( + 0, + $this->event->getImages()->count() + ); + + $image1 = self::createStub(FileReference::class); + $images->attach($image1); + $image2 = self::createStub(FileReference::class); + $images->attach($image2); + + self::assertSame( + $images, + $this->event->getImages() + ); + + self::assertSame( + 2, + $this->event->getImages()->count() ); } #[Test] - public function getDescriptionReturnsInitialValueForDescription(): void + public function getFirstImageReturnsFirstImage(): void { + $images = $this->event->getImages(); + + self::assertNull( + $this->event->getFirstImage() + ); + + $image1 = self::createStub(FileReference::class); + $images->attach($image1); + $image2 = self::createStub(FileReference::class); + $images->attach($image2); + self::assertSame( - '', - $this->event->getDescription() + $image1, + $this->event->getFirstImage() ); } #[Test] - public function setDescriptionSetsDescription(): void + public function getFilesReturnsFiles(): void { - $this->event->setDescription('Description'); + $files = $this->event->getFiles(); self::assertSame( - 'Description', - $this->event->getDescription() + $files, + $this->event->getFiles() + ); + + self::assertSame( + 0, + $this->event->getFiles()->count() + ); + + $file1 = self::createStub(FileReference::class); + $files->attach($file1); + $file2 = self::createStub(FileReference::class); + $files->attach($file2); + + self::assertSame( + $files, + $this->event->getFiles() + ); + + self::assertSame( + 2, + $this->event->getFiles()->count() ); } #[Test] - public function getAudienceReturnsInitialValueForAudience(): void + public function getEventDatesReturnsEventDates(): void { + $eventDates = $this->event->getEventDates(); + self::assertSame( - '', - $this->event->getAudience() + $eventDates, + $this->event->getEventDates() + ); + + self::assertSame( + 0, + $this->event->getEventDates()->count() + ); + + $eventDate1 = self::createStub(FileReference::class); + $eventDates->attach($eventDate1); + $eventDate2 = self::createStub(FileReference::class); + $eventDates->attach($eventDate2); + + self::assertSame( + $eventDates, + $this->event->getEventDates() + ); + + self::assertSame( + 2, + $this->event->getEventDates()->count() ); } #[Test] - public function setAudienceSetsAudience(): void + public function getFirstEventDateReturnsFirstEventDate(): void { - $this->event->setAudience('Audience'); + $eventDates = $this->event->getEventDates(); + + self::assertNull( + $this->event->getFirstEventDate() + ); + + $eventDate1 = self::createStub(EventDate::class); + $eventDates->attach($eventDate1); + $eventDate2 = self::createStub(EventDate::class); + $eventDates->attach($eventDate2); self::assertSame( - 'Audience', - $this->event->getAudience() + $eventDate1, + $this->event->getFirstEventDate() ); } - // todo: images, files, eventDates, relatedEvents, taxClassId, + // todo: relatedEvents #[Test] - public function getMetaDescriptionReturnsInitialValueForMetaDescription(): void + public function getTaxClassIdReturnsTaxClassId(): void { + $event = new Event(); + self::assertSame( - '', - $this->event->getMetaDescription() + 1, + $this->event->getTaxClassId() + ); + + ObjectAccess::setProperty($event, 'taxClassId', 7); + + self::assertSame( + 7, + $event->getTaxClassId() ); } #[Test] - public function setMetaDescriptionSetsMetaDescription(): void + public function getMetaDescriptionReturnsMetaDescription(): void { - $this->event->setMetaDescription('MetaDescription'); + $event = new Event(); self::assertSame( - 'MetaDescription', + '', $this->event->getMetaDescription() ); + + ObjectAccess::setProperty($event, 'metaDescription', 'meta description'); + + self::assertSame( + 'meta description', + $event->getMetaDescription() + ); } } diff --git a/Tests/Unit/Domain/Model/PriceCategoryTest.php b/Tests/Unit/Domain/Model/PriceCategoryTest.php index 0e131b4e..9064d611 100644 --- a/Tests/Unit/Domain/Model/PriceCategoryTest.php +++ b/Tests/Unit/Domain/Model/PriceCategoryTest.php @@ -12,6 +12,7 @@ */ use Extcode\CartEvents\Domain\Model\PriceCategory; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -23,113 +24,120 @@ class PriceCategoryTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->priceCategory = new PriceCategory(); } protected function tearDown(): void { unset($this->priceCategory); + + parent::tearDown(); } #[Test] - public function getSkuReturnsInitialValueForSku(): void + public function getSkuReturnsValueForSku(): void { + $priceCategory = new PriceCategory(); + self::assertSame( '', - $this->priceCategory->getSku() + $priceCategory->getSku() ); - } - #[Test] - public function setSkuSetsSku(): void - { - $this->priceCategory->setSku('sku'); + ObjectAccess::setProperty($priceCategory, 'sku', 'sku'); self::assertSame( 'sku', - $this->priceCategory->getSku() + $priceCategory->getSku() ); } #[Test] - public function getTitleReturnsInitialValueForTitle(): void + public function getTitleReturnsTitle(): void { + $priceCategory = new PriceCategory(); + self::assertSame( '', - $this->priceCategory->getTitle() + $priceCategory->getTitle() ); - } - #[Test] - public function setTitleSetsTitle(): void - { - $this->priceCategory->setTitle('Title'); + ObjectAccess::setProperty($priceCategory, 'title', 'title'); self::assertSame( - 'Title', - $this->priceCategory->getTitle() + 'title', + $priceCategory->getTitle() ); } #[Test] - public function getPriceReturnsInitialValueForPrice(): void + public function getPriceReturnsPrice(): void { + $priceCategory = new PriceCategory(); + self::assertSame( 0.0, - $this->priceCategory->getPrice() + $priceCategory->getPrice() ); - } - #[Test] - public function setPriceSetsPrice(): void - { - $this->priceCategory->setPrice(19.99); + ObjectAccess::setProperty($priceCategory, 'price', 12.88); self::assertSame( - 19.99, - $this->priceCategory->getPrice() + 12.88, + $priceCategory->getPrice() ); } // todo: specialPrice #[Test] - public function getSeatsNumberReturnsInitialValueForSeatsNumber(): void + public function getSeatsNumberReturnsSeatsNumber(): void { + $priceCategory = new PriceCategory(); + self::assertSame( 0, - $this->priceCategory->getSeatsNumber() + $priceCategory->getSeatsNumber() ); - } - #[Test] - public function setSeatsNumberSetsSeatsNumber(): void - { - $this->priceCategory->setSeatsNumber(42); + ObjectAccess::setProperty($priceCategory, 'seatsNumber', 42); self::assertSame( 42, - $this->priceCategory->getSeatsNumber() + $priceCategory->getSeatsNumber() ); } #[Test] public function getSeatsTakenReturnsInitialValueForSeatsTaken(): void { + $priceCategory = new PriceCategory(); + self::assertSame( 0, - $this->priceCategory->getSeatsTaken() + $priceCategory->getSeatsTaken() + ); + + ObjectAccess::setProperty($priceCategory, 'seatsTaken', 42); + + self::assertSame( + 42, + $priceCategory->getSeatsTaken() ); } #[Test] - public function setSeatsTakenSetsSeatsTaken(): void + public function setSeatsTakenSetsSeatsTaken() { - $this->priceCategory->setSeatsTaken(42); + $priceCategory = new PriceCategory(); + + ObjectAccess::setProperty($priceCategory, 'seatsTaken', 15); self::assertSame( - 42, - $this->priceCategory->getSeatsTaken() + 15, + $priceCategory->getSeatsTaken() ); } diff --git a/Tests/Unit/Domain/Model/SpecialPriceTest.php b/Tests/Unit/Domain/Model/SpecialPriceTest.php index 0e11f647..f2dce795 100644 --- a/Tests/Unit/Domain/Model/SpecialPriceTest.php +++ b/Tests/Unit/Domain/Model/SpecialPriceTest.php @@ -13,6 +13,7 @@ use Extcode\Cart\Domain\Model\FrontendUserGroup; use Extcode\CartEvents\Domain\Model\SpecialPrice; +use Extcode\CartEvents\Tests\ObjectAccess; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -24,73 +25,71 @@ class SpecialPriceTest extends UnitTestCase protected function setUp(): void { + parent::setUp(); + $this->specialPrice = new SpecialPrice(); } protected function tearDown(): void { unset($this->specialPrice); + + parent::tearDown(); } #[Test] - public function getTitleReturnsInitialValueForTitle(): void + public function getTitleReturnsTitle(): void { + $specialPrice = new SpecialPrice(); + self::assertSame( '', - $this->specialPrice->getTitle() + $specialPrice->getTitle() ); - } - #[Test] - public function setTitleSetsTitle(): void - { - $this->specialPrice->setTitle('Title'); + ObjectAccess::setProperty($specialPrice, 'title', 'title'); self::assertSame( - 'Title', - $this->specialPrice->getTitle() + 'title', + $specialPrice->getTitle() ); } #[Test] - public function getPriceReturnsInitialValueForPrice(): void + public function getPriceReturnsPrice(): void { + $specialPrice = new SpecialPrice(); + self::assertSame( 0.0, - $this->specialPrice->getPrice() + $specialPrice->getPrice() ); - } - #[Test] - public function setPriceSetsPrice(): void - { - $this->specialPrice->setPrice(19.99); + ObjectAccess::setProperty($specialPrice, 'price', 82.36); self::assertSame( - 19.99, - $this->specialPrice->getPrice() + 82.36, + $specialPrice->getPrice() ); } #[Test] public function getFrontendUserGroupReturnsInitialValueNull(): void { + $specialPrice = new SpecialPrice(); + self::assertNull( $this->specialPrice->getFrontendUserGroup() ); - } - #[Test] - public function setFrontendUserGroupSetsFrontendUserGroup(): void - { $frontendUserGroup = self::createStub( FrontendUserGroup::class ); - $this->specialPrice->setFrontendUserGroup($frontendUserGroup); + ObjectAccess::setProperty($specialPrice, 'frontendUserGroup', $frontendUserGroup); self::assertSame( $frontendUserGroup, - $this->specialPrice->getFrontendUserGroup() + $specialPrice->getFrontendUserGroup() ); } } From 27d748f9ab59cda9e1104fc0aa54ac93d85e33bc Mon Sep 17 00:00:00 2001 From: Daniel Gohlke Date: Wed, 28 Jan 2026 21:20:33 +0100 Subject: [PATCH 3/4] [TASK] Add some phpstan extensions and raise level Relates: #87 --- Build/phpstan-baseline.neon | 19 +++ Build/phpstan.neon | 32 ++++- Classes/Controller/EventController.php | 34 ++++-- Classes/Controller/EventDateController.php | 2 +- .../Finisher/Form/AddToCartFinisher.php | 114 +++--------------- Classes/Domain/Model/Cart/ProductFactory.php | 14 ++- Classes/Domain/Model/EventDate.php | 14 +-- Classes/Domain/Model/PriceCategory.php | 16 ++- .../Domain/Repository/CategoryRepository.php | 28 +---- Classes/Domain/Repository/EventRepository.php | 4 +- .../CheckProductAvailability.php | 7 +- .../EventListener/Order/Stock/FlushCache.php | 17 ++- .../EventListener/Order/Stock/HandleStock.php | 12 +- .../RetrieveProductsFromRequest.php | 5 +- Classes/Hooks/DatamapDataHandlerHook.php | 4 +- Classes/Updates/SlugUpdater.php | 3 +- .../Form/PriceCategorySelectViewHelper.php | 4 +- Classes/ViewHelpers/Link/EventViewHelper.php | 11 +- ..._cartevents_domain_model_calendarentry.php | 4 +- .../TCA/tx_cartevents_domain_model_event.php | 4 +- .../tx_cartevents_domain_model_eventdate.php | 4 +- Tests/Acceptance/AddEventDateToCartCest.php | 2 +- .../Functional/Domain/Model/EventDateTest.php | 7 -- .../Domain/Model/PriceCategoryTest.php | 7 -- .../Domain/Model/AbstractEventDateTest.php | 9 +- Tests/Unit/Domain/Model/CalenderEntryTest.php | 7 -- Tests/Unit/Domain/Model/CategoryTest.php | 7 -- .../Unit/Domain/Model/Dto/EventDemandTest.php | 7 -- Tests/Unit/Domain/Model/EventDateTest.php | 33 ++--- Tests/Unit/Domain/Model/EventTest.php | 11 +- Tests/Unit/Domain/Model/PriceCategoryTest.php | 9 +- Tests/Unit/Domain/Model/SpecialPriceTest.php | 7 -- composer.json | 11 +- 33 files changed, 194 insertions(+), 275 deletions(-) create mode 100644 Build/phpstan-baseline.neon diff --git a/Build/phpstan-baseline.neon b/Build/phpstan-baseline.neon new file mode 100644 index 00000000..d362c92e --- /dev/null +++ b/Build/phpstan-baseline.neon @@ -0,0 +1,19 @@ +parameters: + ignoreErrors: + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Extcode\\\\CartEvents\\\\Domain\\\\Model\\\\AbstractEventDate'' and Extcode\\CartEvents\\Domain\\Model\\CalendarEntry will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 1 + path: ../Tests/Unit/Domain/Model/CalenderEntryTest.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''TYPO3\\\\CMS\\\\Extbase\\\\Domain\\\\Model\\\\Category'' and Extcode\\CartEvents\\Domain\\Model\\Category will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 1 + path: ../Tests/Unit/Domain/Model/CategoryTest.php + + - + message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) with ''Extcode\\\\CartEvents\\\\Domain\\\\Model\\\\AbstractEventDate'' and Extcode\\CartEvents\\Domain\\Model\\EventDate will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 1 + path: ../Tests/Unit/Domain/Model/EventDateTest.php diff --git a/Build/phpstan.neon b/Build/phpstan.neon index fd0b0c9c..1bf02736 100644 --- a/Build/phpstan.neon +++ b/Build/phpstan.neon @@ -1,8 +1,38 @@ +includes: + - 'phpstan-baseline.neon' + parameters: - level: 0 + level: 5 + paths: - ../Classes - ../Configuration - ../Tests - ../ext_emconf.php - ../ext_localconf.php + excludePaths: + - '../Tests/Acceptance/Support/_generated/TesterActions.php' + + disallowedFunctionCalls: + - + function: + - 'var_dump()' + - 'xdebug_break()' + message: 'Do not add debugging' + - + function: 'header()' + message: 'Use API instead' + + disallowedStaticCalls: + - + method: 'TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump()' + message: 'Do not add debugging' + + disallowedSuperglobals: + - + superglobal: + - '$_GET' + - '$_POST' + - '$_FILES' + - '$_SERVER' + message: 'Use API instead' diff --git a/Classes/Controller/EventController.php b/Classes/Controller/EventController.php index b0471215..6ae6c536 100644 --- a/Classes/Controller/EventController.php +++ b/Classes/Controller/EventController.php @@ -11,9 +11,11 @@ * LICENSE file that was distributed with this source code. */ +use Exception; use Extcode\Cart\Domain\Model\Cart\Cart; use Extcode\Cart\Service\SessionHandler; use Extcode\Cart\Utility\CartUtility; +use Extcode\CartEvents\Domain\Model\Category; use Extcode\CartEvents\Domain\Model\Dto\EventDemand; use Extcode\CartEvents\Domain\Model\Event; use Extcode\CartEvents\Domain\Model\EventDate; @@ -39,6 +41,7 @@ public function __construct( private readonly SessionHandler $sessionHandler, private readonly CartUtility $cartUtility, private readonly EventRepository $eventRepository, + private readonly EventDateRepository $eventDateRepository, private readonly CategoryRepository $categoryRepository, ) {} @@ -129,12 +132,15 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa $priceCategoryId = (int)$argumentValue['priceCategoryId']; if ($eventDateId) { - $eventDateRepository = GeneralUtility::makeInstance( - EventDateRepository::class - ); - $eventDate = $eventDateRepository->findByUid($eventDateId); - - $formDefinition = $eventDate->getEvent()->getFormDefinition(); + $eventDate = $this->eventDateRepository->findByUid($eventDateId); + if (($eventDate instanceof EventDate) === false) { + throw new Exception('Can not find EventDate with uid ' . $eventDateId . '.', 1769617660); + } + $event = $eventDate->getEvent(); + if (($event instanceof Event) === false) { + throw new Exception('EventDate with uid ' . $eventDateId . ' has no event!', 1769617873); + } + $formDefinition = $event->getFormDefinition(); $formPersistenceManager = GeneralUtility::makeInstance( \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class ); @@ -149,14 +155,17 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa PriceCategoryRepository::class ); $priceCategory = $priceCategoryRepository->findByUid($priceCategoryId); + if (($priceCategory instanceof PriceCategory) === false) { + throw new Exception('Can not find PriceCategory with uid ' . $priceCategoryId . '.', 1769642011); + } } } } } } - if (!$eventDate) { - throw new \InvalidArgumentException(); + if (($eventDate instanceof EventDate) === false) { + throw new Exception('Can not find EventDate.', 1769641914); } $this->view->assign('eventDate', $eventDate); @@ -172,19 +181,19 @@ public function formAction(?EventDate $eventDate = null, ?PriceCategory $priceCa 'type' => 'Hidden', 'identifier' => 'productType', 'label' => 'productType', - 'defaultValue' => ($eventDate ? 'CartEvents' : ''), + 'defaultValue' => 'CartEvents', ], 9998 => [ 'type' => 'Hidden', 'identifier' => 'eventDateId', 'label' => 'eventDateId', - 'defaultValue' => ($eventDate ? $eventDate->getUid() : ''), + 'defaultValue' => $eventDate->getUid(), ], 9999 => [ 'type' => 'Hidden', 'identifier' => 'priceCategoryId', 'label' => 'priceCategoryId', - 'defaultValue' => ($priceCategory ? $priceCategory->getUid() : ''), + 'defaultValue' => (($priceCategory instanceof PriceCategory) ? $priceCategory->getUid() : ''), ], ], ], @@ -250,6 +259,9 @@ private function addCategoriesToDemandObjectFromSettings(EventDemand &$demand): if ($this->settings['listSubcategories']) { foreach ($selectedCategories as $selectedCategory) { $category = $this->categoryRepository->findByUid($selectedCategory); + if (($category instanceof Category) === false) { + continue; + } $categories = array_merge( $categories, $this->categoryRepository->findSubcategoriesRecursiveAsArray($category) diff --git a/Classes/Controller/EventDateController.php b/Classes/Controller/EventDateController.php index 0b43e027..65255055 100644 --- a/Classes/Controller/EventDateController.php +++ b/Classes/Controller/EventDateController.php @@ -49,7 +49,7 @@ public function listAction(): ResponseInterface ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK )['persistence']['storagePid']; - $eventDates = $this->eventDateRepository->findNext($limit, $pidList)->fetchAll(); + $eventDates = $this->eventDateRepository->findNext($limit, $pidList); $this->view->assign('eventDates', $eventDates); diff --git a/Classes/Domain/Finisher/Form/AddToCartFinisher.php b/Classes/Domain/Finisher/Form/AddToCartFinisher.php index 4625f98f..3360fc2d 100644 --- a/Classes/Domain/Finisher/Form/AddToCartFinisher.php +++ b/Classes/Domain/Finisher/Form/AddToCartFinisher.php @@ -12,14 +12,9 @@ */ use Extcode\Cart\Domain\Finisher\Form\AddToCartFinisherInterface; -use Extcode\Cart\Domain\Model\Cart\BeVariant; use Extcode\Cart\Domain\Model\Cart\Cart; use Extcode\Cart\Domain\Model\Cart\FeVariant; -use Extcode\Cart\Domain\Model\Cart\Product; -use Extcode\CartEvents\Domain\Model\EventDate; -use Extcode\CartEvents\Domain\Model\PriceCategory; -use Extcode\CartEvents\Domain\Repository\EventDateRepository; -use Extcode\CartEvents\Domain\Repository\PriceCategoryRepository; +use Extcode\CartEvents\Domain\Model\Cart\ProductFactoryInterface; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -27,13 +22,8 @@ class AddToCartFinisher implements AddToCartFinisherInterface { protected Cart $cart; - protected EventDate $eventDate; - - protected PriceCategory $priceCategory; - public function __construct( - private readonly EventDateRepository $eventDateRepository, - private readonly PriceCategoryRepository $priceCategoryRepository, + private readonly ProductFactoryInterface $productFactory, ) {} public function getProductFromForm( @@ -45,22 +35,25 @@ public function getProductFromForm( if ($formValues['productType'] !== 'CartEvents') { return [$errors, []]; } - - $eventDateId = $formValues['eventDateId']; - $priceCategoryId = (int)$formValues['priceCategoryId']; - unset($formValues['productType']); + + $requestArguments = [ + 'eventDate' => $formValues['eventDateId'], + 'priceCategory' => $formValues['priceCategoryId'], + 'quantity' => $formValues['quantity'] ?? 1, + ]; unset($formValues['eventDateId']); unset($formValues['priceCategoryId']); + unset($formValues['quantity']); - $this->eventDate = $this->eventDateRepository->findByUid((int)$eventDateId); - $quantity = 1; - - if ($priceCategoryId) { - $this->priceCategory = $this->priceCategoryRepository->findByUid((int)$priceCategoryId); + if (!empty($formValues)) { + $requestArguments['feVariant'] = $this->getFeVariant($formValues); } - - $newProduct = $this->getProductFromEventDate($quantity, $cart->getTaxClasses(), $formValues); + $newProduct = $this->productFactory->createProductFromRequestArguments( + $requestArguments, + $cart->getTaxClasses(), + (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('cart_events', 'inputIsNetPrice') + ); $newProduct->setMaxNumberInCart(1); $newProduct->setMinNumberInCart(1); @@ -68,84 +61,11 @@ public function getProductFromForm( return [$errors, [$newProduct]]; } - protected function getProductFromEventDate( - int $quantity, - array $taxClasses, - array $feVariants = [] - ): Product { - $event = $this->eventDate->getEvent(); - $title = implode(' - ', [$event->getTitle(), $this->eventDate->getTitle()]); - $sku = implode(' - ', [$event->getSku(), $this->eventDate->getSku()]); - - $price = $this->eventDate->getBestPrice(); - if ($this->priceCategory) { - $price = $this->priceCategory->getBestPrice(); - } - - $inputIsNetPrice = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('cart_events', 'inputIsNetPrice'); - - $product = new Product( - 'CartEvents', - $this->eventDate->getUid(), - $sku, - $title, - $price, - $taxClasses[$event->getTaxClassId()], - $quantity, - $inputIsNetPrice, - $this->getFeVariant($feVariants) - ); - $product->setIsVirtualProduct($event->isVirtualProduct()); - - if ($this->priceCategory) { - $product->addBeVariant($this->getProductBackendVariant($product, $quantity)); - } - - if ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate']) { - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cart_events']['getProductFromEventDate'] ?? [] as $className) { - $params = [ - 'cart' => $this->cart, - 'eventDate' => $this->eventDate, - ]; - - $_procObj = GeneralUtility::makeInstance($className); - $_procObj->changeProductFromEventDate($product, $params); - } - } - - return $product; - } - - protected function getProductBackendVariant( - Product $product, - int $quantity - ): BeVariant { - $cartBackendVariant = GeneralUtility::makeInstance( - BeVariant::class, - PriceCategory::class . '-' . $this->priceCategory->getUid(), - $product, - $this->priceCategory->getTitle(), - $this->priceCategory->getSku(), - 1, - $this->priceCategory->getBestPrice(), - $quantity - ); - - /* - TODO - if ($bestSpecialPrice) { - $cartBackendVariant->setSpecialPrice($bestSpecialPrice->getPrice()); - } - */ - - return $cartBackendVariant; - } - protected function getFeVariant(array $data): ?FeVariant { $feVariant = null; - if (!empty($data) && is_array($data)) { + if (!empty($data)) { $feVariants = []; foreach ($data as $dataKey => $dataValue) { if (!empty($dataKey) && !empty($dataValue)) { diff --git a/Classes/Domain/Model/Cart/ProductFactory.php b/Classes/Domain/Model/Cart/ProductFactory.php index d2bf4158..11adee7e 100644 --- a/Classes/Domain/Model/Cart/ProductFactory.php +++ b/Classes/Domain/Model/Cart/ProductFactory.php @@ -10,6 +10,7 @@ */ use Extcode\Cart\Domain\Model\Cart\BeVariant; +use Extcode\Cart\Domain\Model\Cart\FeVariant; use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\CartEvents\Domain\Model\EventDate; use Extcode\CartEvents\Domain\Model\PriceCategory; @@ -52,12 +53,18 @@ public function createProductFromRequestArguments( $priceCategory = $this->getPriceCategoryFromRequestArgument($requestArguments['priceCategory']); } + $feVariant = null; + if (isset($requestArguments['feVariant']) && ($requestArguments['feVariant'] instanceof FeVariant)) { + $feVariant = $requestArguments['feVariant']; + } + return $this->getProductFromEventDate( $quantity, $taxClasses, $isNetPrice, $eventDate, $priceCategory, + $feVariant ); } @@ -84,10 +91,6 @@ private function getEventDateFromRequestArgument( private function getPriceCategoryFromRequestArgument( int $identifier, ): PriceCategory { - if (is_numeric($identifier) === false) { - throw new InvalidArgumentException('Price category argument is invalid', 1741692831); - } - $priceCategory = $this->priceCategoryRepository->findByUid($identifier); if (($priceCategory instanceof PriceCategory) === false) { @@ -107,6 +110,7 @@ private function getProductFromEventDate( bool $isNetPrice, EventDate $eventDate, ?PriceCategory $priceCategory = null, + ?FeVariant $feVariant = null, ): Product { $event = $eventDate->getEvent(); $title = implode(' - ', [$event->getTitle(), $eventDate->getTitle()]); @@ -128,7 +132,7 @@ private function getProductFromEventDate( $taxClasses[$event->getTaxClassId()], $quantity, $isNetPrice, - null + $feVariant ); $product->setIsVirtualProduct($event->isVirtualProduct()); diff --git a/Classes/Domain/Model/EventDate.php b/Classes/Domain/Model/EventDate.php index e4b9bc69..5eef1843 100644 --- a/Classes/Domain/Model/EventDate.php +++ b/Classes/Domain/Model/EventDate.php @@ -181,14 +181,12 @@ public function getBestSpecialPrice(?array $frontendUserGroupIds = null): ?Speci $bestSpecialPrice = null; - if ($this->specialPrices) { - foreach ($this->specialPrices as $specialPrice) { - if (!isset($bestSpecialPrice) || $specialPrice->getPrice() < $bestSpecialPrice->getPrice()) { - if (!$specialPrice->getFrontendUserGroup() - || in_array($specialPrice->getFrontendUserGroup()->getUid(), $frontendUserGroupIds) - ) { - $bestSpecialPrice = $specialPrice; - } + foreach ($this->specialPrices as $specialPrice) { + if (!isset($bestSpecialPrice) || $specialPrice->getPrice() < $bestSpecialPrice->getPrice()) { + if (!$specialPrice->getFrontendUserGroup() + || in_array($specialPrice->getFrontendUserGroup()->getUid(), $frontendUserGroupIds) + ) { + $bestSpecialPrice = $specialPrice; } } } diff --git a/Classes/Domain/Model/PriceCategory.php b/Classes/Domain/Model/PriceCategory.php index 02f9eeb3..84080021 100644 --- a/Classes/Domain/Model/PriceCategory.php +++ b/Classes/Domain/Model/PriceCategory.php @@ -112,14 +112,12 @@ public function getBestSpecialPrice(?array $frontendUserGroupIds = null): ?Speci $bestSpecialPrice = null; - if ($this->specialPrices) { - foreach ($this->specialPrices as $specialPrice) { - if (!isset($bestSpecialPrice) || $specialPrice->getPrice() < $bestSpecialPrice->getPrice()) { - if (!$specialPrice->getFrontendUserGroup() - || in_array($specialPrice->getFrontendUserGroup()->getUid(), $frontendUserGroupIds) - ) { - $bestSpecialPrice = $specialPrice; - } + foreach ($this->specialPrices as $specialPrice) { + if (!isset($bestSpecialPrice) || $specialPrice->getPrice() < $bestSpecialPrice->getPrice()) { + if (!$specialPrice->getFrontendUserGroup() + || in_array($specialPrice->getFrontendUserGroup()->getUid(), $frontendUserGroupIds) + ) { + $bestSpecialPrice = $specialPrice; } } } @@ -177,7 +175,7 @@ public function isAvailable(): bool if (!$this->getEventDate()->isHandleSeatsInPriceCategory()) { return $this->getEventDate()->isAvailable(); } - if ($this->getEventDate()->isHandleSeatsInPriceCategory() && $this->getSeatsAvailable()) { + if ($this->getSeatsAvailable()) { return true; } diff --git a/Classes/Domain/Repository/CategoryRepository.php b/Classes/Domain/Repository/CategoryRepository.php index 58d485ea..fd0be280 100644 --- a/Classes/Domain/Repository/CategoryRepository.php +++ b/Classes/Domain/Repository/CategoryRepository.php @@ -16,18 +16,16 @@ class CategoryRepository extends Repository { - public function findAllAsRecursiveTreeArray(?Category $selectedCategory = null): array - { - $categoriesArray = $this->findAllAsArray($selectedCategory); - return $this->buildSubcategories($categoriesArray, null); - } - public function findAllAsArray(?Category $selectedCategory = null): array { $localCategories = $this->findAll(); $categories = []; // Transform categories to array foreach ($localCategories as $localCategory) { + if (($localCategory instanceof Category) === false) { + continue; + } + $newCategory = [ 'uid' => $localCategory->getUid(), 'title' => $localCategory->getTitle(), @@ -45,9 +43,7 @@ public function findSubcategoriesRecursiveAsArray(Category $parentCategory): arr $categories = []; $localCategories = $this->findAllAsArray(); foreach ($localCategories as $category) { - if (!$parentCategory - || ($parentCategory && $category['uid'] === $parentCategory->getUid()) - ) { + if ($category['uid'] === $parentCategory->getUid()) { $this->getSubcategoriesIds( $localCategories, $category, @@ -74,18 +70,4 @@ protected function getSubcategoriesIds( } } } - - protected function buildSubcategories(array $categoriesArray, array $parentCategory): array - { - $categories = null; - foreach ($categoriesArray as $category) { - if ($category['parent'] === $parentCategory['uid']) { - $newCategory = $category; - $newCategory['subcategories'] - = $this->buildSubcategories($categoriesArray, $category); - $categories[] = $newCategory; - } - } - return $categories; - } } diff --git a/Classes/Domain/Repository/EventRepository.php b/Classes/Domain/Repository/EventRepository.php index 9d47926a..2a159bee 100644 --- a/Classes/Domain/Repository/EventRepository.php +++ b/Classes/Domain/Repository/EventRepository.php @@ -38,12 +38,12 @@ public function findDemanded(EventDemand $demand): QueryResultInterface $categoryConstraints[] = $query->equals('category', $category); $categoryConstraints[] = $query->contains('categories', $category); } - $constraints[] = $query->logicalOr(...array_values($categoryConstraints)); + $constraints[] = $query->logicalOr(...$categoryConstraints); } if (!empty($constraints)) { $query->matching( - $query->logicalAnd(...array_values($constraints)) + $query->logicalAnd(...$constraints) ); } diff --git a/Classes/EventListener/CheckProductAvailability.php b/Classes/EventListener/CheckProductAvailability.php index db527aa0..00257765 100644 --- a/Classes/EventListener/CheckProductAvailability.php +++ b/Classes/EventListener/CheckProductAvailability.php @@ -11,6 +11,7 @@ * LICENSE file that was distributed with this source code. */ +use Exception; use Extcode\Cart\Domain\Model\Cart\Cart; use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\Cart\Event\CheckProductAvailabilityEvent; @@ -72,7 +73,11 @@ protected function retrieveEventDateFromDatabase(Product $cartProduct): void $querySettings->setRespectStoragePage(false); $this->eventDateRepository->setDefaultQuerySettings($querySettings); - $this->eventDate = $this->eventDateRepository->findByIdentifier($cartProduct->getProductId()); + $eventDate = $this->eventDateRepository->findByIdentifier($cartProduct->getProductId()); + if (($eventDate instanceof EventDate) === false) { + throw new Exception('Can not find EventDate with uid ' . $cartProduct->getProductId() . '.', 1769634921); + } + $this->eventDate = $eventDate; } protected function getQuantitiesFromRequest(Request $request, Product $cartProduct): mixed diff --git a/Classes/EventListener/Order/Stock/FlushCache.php b/Classes/EventListener/Order/Stock/FlushCache.php index 30caf3ce..46a5cb81 100644 --- a/Classes/EventListener/Order/Stock/FlushCache.php +++ b/Classes/EventListener/Order/Stock/FlushCache.php @@ -11,15 +11,18 @@ * LICENSE file that was distributed with this source code. */ +use Exception; use Extcode\Cart\Event\Order\EventInterface; +use Extcode\CartEvents\Domain\Model\Event; +use Extcode\CartEvents\Domain\Model\EventDate; use Extcode\CartEvents\Domain\Repository\EventDateRepository; use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Utility\GeneralUtility; -class FlushCache +readonly class FlushCache { public function __construct( - private readonly EventDateRepository $eventDateRepository + private EventDateRepository $eventDateRepository ) {} public function __invoke(EventInterface $event): void @@ -29,8 +32,14 @@ public function __invoke(EventInterface $event): void foreach ($cartProducts as $cartProduct) { if ($cartProduct->getProductType() === 'CartEvents') { $eventDate = $this->eventDateRepository->findByUid($cartProduct->getProductId()); - - $cacheTag = 'tx_cartevents_event_' . $eventDate->getEvent()->getUid(); + if (($eventDate instanceof EventDate) === false) { + throw new Exception('Can not find EventDate with uid ' . $cartProduct->getProductId() . ' has no event!', 1769617880); + } + $event = $eventDate->getEvent(); + if (($event instanceof Event) === false) { + throw new Exception('EventDate with uid ' . $cartProduct->getProductId() . ' has no event!', 1769617883); + } + $cacheTag = 'tx_cartevents_event_' . $event->getUid(); $cacheManager = GeneralUtility::makeInstance(CacheManager::class); $cacheManager->flushCachesInGroupByTag('pages', $cacheTag); } diff --git a/Classes/EventListener/Order/Stock/HandleStock.php b/Classes/EventListener/Order/Stock/HandleStock.php index 12348714..ac6532a9 100644 --- a/Classes/EventListener/Order/Stock/HandleStock.php +++ b/Classes/EventListener/Order/Stock/HandleStock.php @@ -11,8 +11,11 @@ * LICENSE file that was distributed with this source code. */ +use Exception; use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\Cart\Event\Order\EventInterface; +use Extcode\CartEvents\Domain\Model\EventDate; +use Extcode\CartEvents\Domain\Model\PriceCategory; use Extcode\CartEvents\Domain\Repository\EventDateRepository; use Extcode\CartEvents\Domain\Repository\PriceCategoryRepository; use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; @@ -40,12 +43,19 @@ protected function handleStockForEventDate(Product $cartProduct): void { $eventDate = $this->eventDateRepository->findByUid($cartProduct->getProductId()); - if ($eventDate && $eventDate->isHandleSeats()) { + if (($eventDate instanceof EventDate) === false) { + return; + } + + if ($eventDate->isHandleSeats()) { if ($eventDate->isHandleSeatsInPriceCategory()) { foreach ($cartProduct->getBeVariants() as $cartBeVariant) { $explodedId = explode('-', (string)$cartBeVariant->getId()); $id = (int)end($explodedId); $priceCategory = $this->priceCategoryRepository->findByUid($id); + if (($priceCategory instanceof PriceCategory) === false) { + throw new Exception('Can not find PriceCategory with $id=' . $id, 1769617418); + } $priceCategory->setSeatsTaken($priceCategory->getSeatsTaken() + $cartBeVariant->getQuantity()); $this->priceCategoryRepository->update($priceCategory); } diff --git a/Classes/EventListener/RetrieveProductsFromRequest.php b/Classes/EventListener/RetrieveProductsFromRequest.php index a8aaf66f..8ed03741 100644 --- a/Classes/EventListener/RetrieveProductsFromRequest.php +++ b/Classes/EventListener/RetrieveProductsFromRequest.php @@ -12,7 +12,6 @@ */ use Exception; -use Extcode\Cart\Domain\Model\Cart\Product; use Extcode\Cart\Event\RetrieveProductsFromRequestEvent; use Extcode\CartEvents\Domain\Model\Cart\ProductFactoryInterface; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; @@ -41,9 +40,7 @@ public function __invoke(RetrieveProductsFromRequestEvent $event): void (bool)$this->extensionConfiguration->get('cart_events', 'inputIsNetPrice'), ); - if ($product instanceof Product) { - $event->addProduct($product); - } + $event->addProduct($product); } catch (Exception $exception) { $event->setErrors( [ diff --git a/Classes/Hooks/DatamapDataHandlerHook.php b/Classes/Hooks/DatamapDataHandlerHook.php index 71c2a8f7..aa5a816d 100644 --- a/Classes/Hooks/DatamapDataHandlerHook.php +++ b/Classes/Hooks/DatamapDataHandlerHook.php @@ -53,10 +53,10 @@ public function processDatamap_beforeStart(DataHandler $dataHandler): void 'tt_content', $id, 1, - $pageId, + null, 1, 'The record "%s" couldn\'t be saved due to disallowed value(s).', - 23, + null, [ $incomingFieldArray[$GLOBALS['TCA']['tt_content']['ctrl']['label']], ] diff --git a/Classes/Updates/SlugUpdater.php b/Classes/Updates/SlugUpdater.php index 846e8daa..77b8d645 100644 --- a/Classes/Updates/SlugUpdater.php +++ b/Classes/Updates/SlugUpdater.php @@ -90,8 +90,7 @@ public function executeUpdate(): bool ) ) ->set('path_segment', $slugHelper->sanitize((string)$record['title'])); - $queryBuilder->getSQL(); - $queryBuilder->execute(); + $queryBuilder->executeQuery(); } return true; diff --git a/Classes/ViewHelpers/Form/PriceCategorySelectViewHelper.php b/Classes/ViewHelpers/Form/PriceCategorySelectViewHelper.php index d8e9900f..619880d5 100644 --- a/Classes/ViewHelpers/Form/PriceCategorySelectViewHelper.php +++ b/Classes/ViewHelpers/Form/PriceCategorySelectViewHelper.php @@ -113,7 +113,9 @@ protected function getOptions(): array } $disabled = ''; - if (!$priceCategory->isAvailable() && $priceCategory->getEventDate() && $priceCategory->getEventDate()->isHandleSeatsInPriceCategory()) { + if (!$priceCategory->isAvailable() + && $priceCategory->getEventDate()->isHandleSeatsInPriceCategory() + ) { $disabled = 'disabled'; } diff --git a/Classes/ViewHelpers/Link/EventViewHelper.php b/Classes/ViewHelpers/Link/EventViewHelper.php index 3633be43..aba79d2e 100644 --- a/Classes/ViewHelpers/Link/EventViewHelper.php +++ b/Classes/ViewHelpers/Link/EventViewHelper.php @@ -9,6 +9,7 @@ * LICENSE file that was distributed with this source code. */ use Extcode\CartEvents\Domain\Model\Event; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -27,11 +28,6 @@ class EventViewHelper extends AbstractTagBasedViewHelper public function initializeArguments(): void { parent::initializeArguments(); - $this->registerUniversalTagAttributes(); - $this->registerTagAttribute('name', 'string', 'Specifies the name of an anchor'); - $this->registerTagAttribute('rel', 'string', 'Specifies the relationship between the current document and the linked document'); - $this->registerTagAttribute('rev', 'string', 'Specifies the relationship between the linked document and the current document'); - $this->registerTagAttribute('target', 'string', 'Specifies where to open the linked document'); $this->registerArgument('action', 'string', 'Target action'); $this->registerArgument('controller', 'string', 'Target controller. If NULL current controllerName is used'); $this->registerArgument('extensionName', 'string', 'Target Extension Name (without `tx_` prefix and no underscores). If NULL the current extension name is used'); @@ -66,7 +62,8 @@ public function render(): string { /** @var RenderingContext $renderingContext */ $renderingContext = $this->renderingContext; - $request = $renderingContext->getRequest(); + $renderingContext->hasAttribute(ServerRequestInterface::class); + $request = $renderingContext->getAttribute(ServerRequestInterface::class); if (!$request instanceof RequestInterface) { throw new \RuntimeException( 'ViewHelper f:link.action can be used only in extbase context and needs a request implementing extbase RequestInterface.', @@ -109,7 +106,7 @@ public function render(): string // A missing $pageUid means the product does not have a defined detail view via category or flexform // In this case the $pluginName of the extbase context should be used. if (!$pageUid) { - $pluginName = $renderingContext->getRequest()->getAttributes()['extbase']->getPluginName(); + $pluginName = $request->getAttributes()['extbase']->getPluginName(); } $action = 'show'; diff --git a/Configuration/TCA/tx_cartevents_domain_model_calendarentry.php b/Configuration/TCA/tx_cartevents_domain_model_calendarentry.php index 629e6f70..9d24d3f9 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_calendarentry.php +++ b/Configuration/TCA/tx_cartevents_domain_model_calendarentry.php @@ -61,7 +61,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], @@ -74,7 +74,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_event.php b/Configuration/TCA/tx_cartevents_domain_model_event.php index f30bc695..8c2b5e6a 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_event.php +++ b/Configuration/TCA/tx_cartevents_domain_model_event.php @@ -124,7 +124,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], @@ -137,7 +137,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], diff --git a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php index fd789bd4..20e42d94 100644 --- a/Configuration/TCA/tx_cartevents_domain_model_eventdate.php +++ b/Configuration/TCA/tx_cartevents_domain_model_eventdate.php @@ -119,7 +119,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], @@ -132,7 +132,7 @@ 'checkbox' => 0, 'default' => 0, 'range' => [ - 'lower' => mktime(0, 0, 0, date('m'), date('d'), date('Y')), + 'lower' => mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y')), ], ], ], diff --git a/Tests/Acceptance/AddEventDateToCartCest.php b/Tests/Acceptance/AddEventDateToCartCest.php index 8349d12d..5989aa4b 100644 --- a/Tests/Acceptance/AddEventDateToCartCest.php +++ b/Tests/Acceptance/AddEventDateToCartCest.php @@ -337,7 +337,7 @@ public function addDifferentBookableEventDateWithAvailableNumberOfSeatButNotMore #[Test] public function addBookableEventDateWithPriceCategoryAvailableNumberOfSeatToCart(Tester $I): void { - $I->wantToTest('I can add a bookable event date with price category wit available number of seats to the cart.'); + $I->wantToTest('I can add a bookable event date with price category with available number of seats to the cart.'); $I->amOnUrl('http://127.0.0.1:8080/events/'); diff --git a/Tests/Functional/Domain/Model/EventDateTest.php b/Tests/Functional/Domain/Model/EventDateTest.php index bb016e8e..da7a271c 100644 --- a/Tests/Functional/Domain/Model/EventDateTest.php +++ b/Tests/Functional/Domain/Model/EventDateTest.php @@ -24,11 +24,4 @@ protected function setUp(): void parent::setUp(); } - - protected function tearDown(): void - { - unset($this->subject); - - parent::tearDown(); - } } diff --git a/Tests/Functional/Domain/Model/PriceCategoryTest.php b/Tests/Functional/Domain/Model/PriceCategoryTest.php index 8bf3554d..b9edb2cc 100644 --- a/Tests/Functional/Domain/Model/PriceCategoryTest.php +++ b/Tests/Functional/Domain/Model/PriceCategoryTest.php @@ -24,11 +24,4 @@ protected function setUp(): void parent::setUp(); } - - protected function tearDown(): void - { - unset($this->subject); - - parent::tearDown(); - } } diff --git a/Tests/Unit/Domain/Model/AbstractEventDateTest.php b/Tests/Unit/Domain/Model/AbstractEventDateTest.php index fb3452ac..06d9cd34 100644 --- a/Tests/Unit/Domain/Model/AbstractEventDateTest.php +++ b/Tests/Unit/Domain/Model/AbstractEventDateTest.php @@ -23,7 +23,7 @@ class AbstractEventDateTest extends UnitTestCase { protected bool $resetSingletonInstances = true; - protected $eventDate; + protected AbstractEventDate $eventDate; protected function setUp(): void { @@ -32,13 +32,6 @@ protected function setUp(): void $this->eventDate = new class extends AbstractEventDate {}; } - protected function tearDown(): void - { - unset($this->eventDate); - - parent::tearDown(); - } - #[Test] public function getBeginReturnsBegin(): void { diff --git a/Tests/Unit/Domain/Model/CalenderEntryTest.php b/Tests/Unit/Domain/Model/CalenderEntryTest.php index 887adad5..b9e426f0 100644 --- a/Tests/Unit/Domain/Model/CalenderEntryTest.php +++ b/Tests/Unit/Domain/Model/CalenderEntryTest.php @@ -29,13 +29,6 @@ protected function setUp(): void $this->calendarEntry = new CalendarEntry(); } - protected function tearDown(): void - { - unset($this->calendarEntry); - - parent::tearDown(); - } - #[Test] public function calendarEntryExtendsAbstractEventDate(): void { diff --git a/Tests/Unit/Domain/Model/CategoryTest.php b/Tests/Unit/Domain/Model/CategoryTest.php index 1a938a51..1d43adb7 100644 --- a/Tests/Unit/Domain/Model/CategoryTest.php +++ b/Tests/Unit/Domain/Model/CategoryTest.php @@ -29,13 +29,6 @@ protected function setUp(): void $this->category = new Category(); } - protected function tearDown(): void - { - unset($this->category); - - parent::tearDown(); - } - #[Test] public function categoryExtendsExtbaseCategoryModel(): void { diff --git a/Tests/Unit/Domain/Model/Dto/EventDemandTest.php b/Tests/Unit/Domain/Model/Dto/EventDemandTest.php index 295be465..37973e2e 100644 --- a/Tests/Unit/Domain/Model/Dto/EventDemandTest.php +++ b/Tests/Unit/Domain/Model/Dto/EventDemandTest.php @@ -28,13 +28,6 @@ protected function setUp(): void $this->eventDemand = new EventDemand(); } - protected function tearDown(): void - { - unset($this->eventDemand); - - parent::tearDown(); - } - #[Test] public function getSkuReturnsInitialValueForSku(): void { diff --git a/Tests/Unit/Domain/Model/EventDateTest.php b/Tests/Unit/Domain/Model/EventDateTest.php index 234c5ce5..6d649cf1 100644 --- a/Tests/Unit/Domain/Model/EventDateTest.php +++ b/Tests/Unit/Domain/Model/EventDateTest.php @@ -31,13 +31,6 @@ protected function setUp(): void $this->eventDate = new EventDate(); } - protected function tearDown(): void - { - unset($this->eventDate); - - parent::tearDown(); - } - #[Test] public function eventDateExtendsAbstractEventDate(): void { @@ -274,7 +267,7 @@ public function isHandleSeatsInPriceCategoryReturnsHandleSeatsInPriceCategory(): } #[Test] - public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse() + public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse(): void { $eventDate = new EventDate(); @@ -292,7 +285,7 @@ public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse() } #[Test] - public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTrue() + public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTrue(): void { $eventDate = new EventDate(); @@ -305,7 +298,7 @@ public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTr } #[Test] - public function setSeatsNumberSetsSeatsNumber() + public function setSeatsNumberSetsSeatsNumber(): void { $eventDate = new EventDate(); @@ -325,7 +318,7 @@ public function setSeatsNumberSetsSeatsNumber() } #[Test] - public function getSeatsTakenReturnsZeroIfHandleSeatsIsFalse() + public function getSeatsTakenReturnsZeroIfHandleSeatsIsFalse(): void { $eventDate = new EventDate(); @@ -343,7 +336,7 @@ public function getSeatsTakenReturnsZeroIfHandleSeatsIsFalse() } #[Test] - public function getSeatsTakenReturnsInitialValueForSeatsTakenIfHandleSeatsIsTrue() + public function getSeatsTakenReturnsInitialValueForSeatsTakenIfHandleSeatsIsTrue(): void { $eventDate = new EventDate(); @@ -356,7 +349,7 @@ public function getSeatsTakenReturnsInitialValueForSeatsTakenIfHandleSeatsIsTrue } #[Test] - public function setSeatsTakenSetsSeatsTaken() + public function setSeatsTakenSetsSeatsTaken(): void { $eventDate = new EventDate(); @@ -376,7 +369,7 @@ public function setSeatsTakenSetsSeatsTaken() } #[Test] - public function getSeatsAvailableReturnsZeroIfHandleSeatsIsFalse() + public function getSeatsAvailableReturnsZeroIfHandleSeatsIsFalse(): void { $eventDate = new EventDate(); @@ -394,7 +387,7 @@ public function getSeatsAvailableReturnsZeroIfHandleSeatsIsFalse() } #[Test] - public function getSeatsAvailableReturnsDifferenceOfInitialValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue() + public function getSeatsAvailableReturnsDifferenceOfInitialValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue(): void { $eventDate = new EventDate(); @@ -407,7 +400,7 @@ public function getSeatsAvailableReturnsDifferenceOfInitialValueForSeatsNumberAn } #[Test] - public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue() + public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue(): void { $eventDate = new EventDate(); @@ -428,7 +421,7 @@ public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfH } #[TEST] - public function isAvailableReturnsFalseIfBookableIsFalse() + public function isAvailableReturnsFalseIfBookableIsFalse(): void { $eventDate = new EventDate(); @@ -448,7 +441,7 @@ public function isAvailableReturnsFalseIfBookableIsFalse() } #[TEST] - public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsFalse() + public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsFalse(): void { $eventDate = new EventDate(); @@ -467,7 +460,7 @@ public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsFalse() } #[TEST] - public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsGreaterThanZero() + public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsGreaterThanZero(): void { $eventDate = new EventDate(); @@ -493,7 +486,7 @@ public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsTrueAndNumberO } #[TEST] - public function isAvailableReturnsFalseIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsLowerOrEqualToZero() + public function isAvailableReturnsFalseIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsLowerOrEqualToZero(): void { $eventDate = new EventDate(); diff --git a/Tests/Unit/Domain/Model/EventTest.php b/Tests/Unit/Domain/Model/EventTest.php index d4dba94a..16f8e106 100644 --- a/Tests/Unit/Domain/Model/EventTest.php +++ b/Tests/Unit/Domain/Model/EventTest.php @@ -31,13 +31,6 @@ protected function setUp(): void $this->event = new Event(); } - protected function tearDown(): void - { - unset($this->event); - - parent::tearDown(); - } - #[Test] public function isVirtualProductReturnsInitialValueForVirtualProduct(): void { @@ -258,9 +251,9 @@ public function getEventDatesReturnsEventDates(): void $this->event->getEventDates()->count() ); - $eventDate1 = self::createStub(FileReference::class); + $eventDate1 = self::createStub(EventDate::class); $eventDates->attach($eventDate1); - $eventDate2 = self::createStub(FileReference::class); + $eventDate2 = self::createStub(EventDate::class); $eventDates->attach($eventDate2); self::assertSame( diff --git a/Tests/Unit/Domain/Model/PriceCategoryTest.php b/Tests/Unit/Domain/Model/PriceCategoryTest.php index 9064d611..66213bf1 100644 --- a/Tests/Unit/Domain/Model/PriceCategoryTest.php +++ b/Tests/Unit/Domain/Model/PriceCategoryTest.php @@ -29,13 +29,6 @@ protected function setUp(): void $this->priceCategory = new PriceCategory(); } - protected function tearDown(): void - { - unset($this->priceCategory); - - parent::tearDown(); - } - #[Test] public function getSkuReturnsValueForSku(): void { @@ -129,7 +122,7 @@ public function getSeatsTakenReturnsInitialValueForSeatsTaken(): void } #[Test] - public function setSeatsTakenSetsSeatsTaken() + public function setSeatsTakenSetsSeatsTaken(): void { $priceCategory = new PriceCategory(); diff --git a/Tests/Unit/Domain/Model/SpecialPriceTest.php b/Tests/Unit/Domain/Model/SpecialPriceTest.php index f2dce795..f37539fc 100644 --- a/Tests/Unit/Domain/Model/SpecialPriceTest.php +++ b/Tests/Unit/Domain/Model/SpecialPriceTest.php @@ -30,13 +30,6 @@ protected function setUp(): void $this->specialPrice = new SpecialPrice(); } - protected function tearDown(): void - { - unset($this->specialPrice); - - parent::tearDown(); - } - #[Test] public function getTitleReturnsTitle(): void { diff --git a/composer.json b/composer.json index ef055972..c30fe088 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "allow-plugins": { "typo3/cms-composer-installers": true, "typo3/class-alias-loader": true, - "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": true + "sbuerk/typo3-cmscomposerinstallers-testingframework-bridge": true, + "phpstan/extension-installer": true } }, "extra": { @@ -61,7 +62,13 @@ "codeception/module-webdriver": "^4.0", "friendsofphp/php-cs-fixer": "^3.16", "helmich/typo3-typoscript-lint": "^3.1", - "phpstan/phpstan": "^1.10", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "saschaegerer/phpstan-typo3": "^2.1", + "spaze/phpstan-disallowed-calls": "^4.7", + "staabm/phpstan-todo-by": "^0.3", "typo3/cms-fluid-styled-content": "^13.4", "typo3/cms-install": "^13.4", "typo3/testing-framework": "^8.0" From 3691282fb8e4985327ec5474e25849df5c09a7a0 Mon Sep 17 00:00:00 2001 From: Daniel Gohlke Date: Thu, 29 Jan 2026 13:51:25 +0100 Subject: [PATCH 4/4] [BUGFIX] Prepare Release 6.0.0 --- Documentation/Changelog/6.0/Index.rst | 11 +++++++++++ Documentation/Changelog/Index.rst | 1 + Documentation/guides.xml | 4 ++-- ext_emconf.php | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 Documentation/Changelog/6.0/Index.rst diff --git a/Documentation/Changelog/6.0/Index.rst b/Documentation/Changelog/6.0/Index.rst new file mode 100644 index 00000000..0dbef5de --- /dev/null +++ b/Documentation/Changelog/6.0/Index.rst @@ -0,0 +1,11 @@ +.. include:: ../../Includes.rst.txt + +=========== +6.0 Changes +=========== + +**Table of contents** + +.. contents:: + :local: + :depth: 1 diff --git a/Documentation/Changelog/Index.rst b/Documentation/Changelog/Index.rst index f16f5894..5856e100 100644 --- a/Documentation/Changelog/Index.rst +++ b/Documentation/Changelog/Index.rst @@ -10,6 +10,7 @@ ChangeLog :maxdepth: 5 :titlesonly: + 6.0/Index 5.0/Index 4.0/Index 3.0/Index diff --git a/Documentation/guides.xml b/Documentation/guides.xml index 891b0c68..36ccd264 100644 --- a/Documentation/guides.xml +++ b/Documentation/guides.xml @@ -11,8 +11,8 @@ interlink-shortcode="extcode/cart_events" /> diff --git a/ext_emconf.php b/ext_emconf.php index cd1f32d1..0dc2f820 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -4,7 +4,7 @@ 'title' => 'Cart - Events', 'description' => 'Shopping Cart(s) for TYPO3 - Event Extension', 'category' => 'plugin', - 'version' => '5.1.0', + 'version' => '6.0.0', 'state' => 'stable', 'author' => 'Daniel Gohlke', 'author_email' => 'ext@extco.de',