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/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 12ae8688..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; @@ -28,9 +30,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; @@ -40,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, ) {} @@ -118,6 +120,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) { @@ -126,14 +132,17 @@ 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( - FormPersistenceManagerInterface::class + \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface::class ); $form = $formPersistenceManager->load($formDefinition); @@ -146,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); @@ -169,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() : ''), ], ], ], @@ -195,28 +207,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 +245,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( @@ -268,6 +259,9 @@ protected 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) @@ -281,10 +275,7 @@ protected function addCategoriesToDemandObjectFromSettings(EventDemand &$demand) } } - /** - * assigns currency translation array to view - */ - protected function assignCurrencyTranslationData(): void + private function assignCurrencyTranslationData(): void { $this->restoreSession(); @@ -297,7 +288,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 +324,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..65255055 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, @@ -48,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/AbstractEventDate.php b/Classes/Domain/Model/AbstractEventDate.php index 277852b3..72d3f3c2 100644 --- a/Classes/Domain/Model/AbstractEventDate.php +++ b/Classes/Domain/Model/AbstractEventDate.php @@ -11,43 +11,29 @@ * 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 - { - $this->begin = $begin; - } - - public function getEnd(): ?\DateTime + 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/Cart/ProductFactory.php b/Classes/Domain/Model/Cart/ProductFactory.php new file mode 100644 index 00000000..11adee7e --- /dev/null +++ b/Classes/Domain/Model/Cart/ProductFactory.php @@ -0,0 +1,175 @@ +getEventDateFromRequestArgument($requestArguments['eventDate']); + + $priceCategory = null; + if (isset($requestArguments['priceCategory'])) { + $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 + ); + } + + 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 { + $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, + ?FeVariant $feVariant = 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, + $feVariant + ); + $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 @@ +virtualProduct; + $this->images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->eventDates = new ObjectStorage(); + $this->relatedEvents = new ObjectStorage(); + $this->relatedEventsFrom = new ObjectStorage(); } - public function setVirtualProduct(bool $virtualProduct): void + public function isVirtualProduct(): bool { - $this->virtualProduct = $virtualProduct; + return $this->virtualProduct; } public function getFormDefinition(): ?string @@ -90,61 +92,31 @@ 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; @@ -159,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 */ @@ -187,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(); } /** @@ -213,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 */ @@ -236,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 6ec68e7e..5eef1843 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,14 +74,18 @@ class EventDate extends AbstractEventDate #[Cascade(['value' => 'remove'])] protected ObjectStorage $calendarEntries; - public function getSku(): string + public function __construct() { - return $this->sku; + $this->images = new ObjectStorage(); + $this->files = new ObjectStorage(); + $this->specialPrices = new ObjectStorage(); + $this->priceCategories = new ObjectStorage(); + $this->calendarEntries = new ObjectStorage(); } - public function setSku(string $sku): void + public function getSku(): string { - $this->sku = $sku; + return $this->sku; } public function getTitle(): string @@ -86,31 +93,16 @@ 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 */ @@ -128,11 +120,6 @@ public function getFirstImage(): ?FileReference return array_shift($images); } - public function setImages(ObjectStorage $images): void - { - $this->images = $images; - } - /** * @return ObjectStorage */ @@ -141,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; @@ -161,11 +143,6 @@ public function getPrice(): float return $this->price; } - public function setPrice(float $price): void - { - $this->price = $price; - } - /** * @return ObjectStorage */ @@ -174,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; @@ -215,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)) { @@ -239,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; } } } @@ -298,41 +238,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. @@ -354,11 +274,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 268e9af8..84080021 100644 --- a/Classes/Domain/Model/PriceCategory.php +++ b/Classes/Domain/Model/PriceCategory.php @@ -40,14 +40,14 @@ class PriceCategory extends AbstractEntity protected int $seatsTaken = 0; - public function getSku(): string + public function __construct() { - return $this->sku; + $this->specialPrices = new ObjectStorage(); } - public function setSku(string $sku): void + public function getSku(): string { - $this->sku = $sku; + return $this->sku; } public function getEventDate(): EventDate @@ -55,31 +55,16 @@ 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 */ @@ -88,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; @@ -147,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; } } } @@ -212,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/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/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/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..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) ); } @@ -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..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; @@ -52,15 +53,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); } } @@ -70,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 cb188a65..8ed03741 100644 --- a/Classes/EventListener/RetrieveProductsFromRequest.php +++ b/Classes/EventListener/RetrieveProductsFromRequest.php @@ -11,236 +11,47 @@ * LICENSE file that was distributed with this source code. */ -use Extcode\Cart\Domain\Model\Cart\BeVariant; -use Extcode\Cart\Domain\Model\Cart\Cart; -use Extcode\Cart\Domain\Model\Cart\Product; +use Exception; 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', + $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/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/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_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 827fc226..8c2b5e6a 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, @@ -126,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')), ], ], ], @@ -139,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')), ], ], ], @@ -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..20e42d94 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, @@ -121,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')), ], ], ], @@ -134,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')), ], ], ], @@ -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/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/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..5989aa4b 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 with 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/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/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/Model/EventDateTest.php b/Tests/Functional/Domain/Model/EventDateTest.php index 8d186cde..da7a271c 100644 --- a/Tests/Functional/Domain/Model/EventDateTest.php +++ b/Tests/Functional/Domain/Model/EventDateTest.php @@ -20,8 +20,8 @@ class EventDateTest extends AbstractSpecialPrice protected function setUp(): void { $this->price = 17.49; - $this->subject = new EventDate(); - $this->subject->setPrice($this->price); + + parent::setUp(); } } diff --git a/Tests/Functional/Domain/Model/PriceCategoryTest.php b/Tests/Functional/Domain/Model/PriceCategoryTest.php index d2a5ab16..b9edb2cc 100644 --- a/Tests/Functional/Domain/Model/PriceCategoryTest.php +++ b/Tests/Functional/Domain/Model/PriceCategoryTest.php @@ -20,8 +20,8 @@ class PriceCategoryTest extends AbstractSpecialPrice protected function setUp(): void { $this->price = 13.44; - $this->subject = new PriceCategory(); - $this->subject->setPrice($this->price); + + parent::setUp(); } } 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/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..06d9cd34 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,51 @@ #[CoversClass(AbstractEventDate::class)] class AbstractEventDateTest extends UnitTestCase { - protected $eventDate; + protected bool $resetSingletonInstances = true; + + protected AbstractEventDate $eventDate; protected function setUp(): void { - $this->eventDate = new class extends AbstractEventDate {}; - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->eventDate); + $this->eventDate = new class extends AbstractEventDate {}; } #[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 +77,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..b9e426f0 100644 --- a/Tests/Unit/Domain/Model/CalenderEntryTest.php +++ b/Tests/Unit/Domain/Model/CalenderEntryTest.php @@ -24,12 +24,9 @@ class CalenderEntryTest extends UnitTestCase protected function setUp(): void { - $this->calendarEntry = new CalendarEntry(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->calendarEntry); + $this->calendarEntry = new CalendarEntry(); } #[Test] diff --git a/Tests/Unit/Domain/Model/CategoryTest.php b/Tests/Unit/Domain/Model/CategoryTest.php index c9f6b9c0..1d43adb7 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,9 @@ class CategoryTest extends UnitTestCase protected function setUp(): void { - $this->category = new Category(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->category); + $this->category = new Category(); } #[Test] @@ -40,16 +38,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..37973e2e 100644 --- a/Tests/Unit/Domain/Model/Dto/EventDemandTest.php +++ b/Tests/Unit/Domain/Model/Dto/EventDemandTest.php @@ -23,12 +23,9 @@ class EventDemandTest extends UnitTestCase protected function setUp(): void { - $this->eventDemand = new EventDemand(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->eventDemand); + $this->eventDemand = new EventDemand(); } #[Test] @@ -71,7 +68,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..6d649cf1 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,9 @@ class EventDateTest extends UnitTestCase protected function setUp(): void { - $this->eventDate = new EventDate(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->eventDate); + $this->eventDate = new EventDate(); } #[Test] @@ -39,90 +38,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( + $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( - 'Location', - $this->eventDate->getLocation() + 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 +218,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() + public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse(): void { + $eventDate = new EventDate(); + self::assertSame( 0, $this->eventDate->getSeatsNumber() ); - $this->eventDate->setSeatsNumber(15); + ObjectAccess::setProperty($eventDate, 'seatsNumber', 15); self::assertSame( 0, @@ -207,192 +285,223 @@ public function getSeatsNumberReturnsZeroIfHandleSeatsIsFalse() } #[Test] - public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTrue() + public function getSeatsNumberReturnsInitialValueForSeatsNumberIfHandleSeatsIsTrue(): void { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsNumber() + $eventDate->getSeatsNumber() ); } #[Test] - public function setSeatsNumberSetsSeatsNumber() + public function setSeatsNumberSetsSeatsNumber(): void { - $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() + public function getSeatsTakenReturnsZeroIfHandleSeatsIsFalse(): void { + $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() + public function getSeatsTakenReturnsInitialValueForSeatsTakenIfHandleSeatsIsTrue(): void { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsTaken() + $eventDate->getSeatsTaken() ); } #[Test] - public function setSeatsTakenSetsSeatsTaken() + public function setSeatsTakenSetsSeatsTaken(): void { - $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() + public function getSeatsAvailableReturnsZeroIfHandleSeatsIsFalse(): void { + $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() + public function getSeatsAvailableReturnsDifferenceOfInitialValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue(): void { - $this->eventDate->setHandleSeats(true); + $eventDate = new EventDate(); + + ObjectAccess::setProperty($eventDate, 'handleSeats', true); self::assertSame( 0, - $this->eventDate->getSeatsAvailable() + $eventDate->getSeatsAvailable() ); } #[Test] - public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue() + public function getSeatsAvailableDifferenceOfValueForSeatsNumberAndSeatsTakenIfHandleSeatsIsTrue(): void { - $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() + public function isAvailableReturnsFalseIfBookableIsFalse(): void { - $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() + public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsFalse(): void { - $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() + public function isAvailableReturnsTrueIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsGreaterThanZero(): void { - $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() + public function isAvailableReturnsFalseIfIsBookableAndHandleSeatsIsTrueAndNumberOfSeatsIsLowerOrEqualToZero(): void { - $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..16f8e106 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,302 @@ class EventTest extends UnitTestCase protected function setUp(): void { - $this->event = new Event(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->event); + $this->event = new Event(); } #[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(EventDate::class); + $eventDates->attach($eventDate1); + $eventDate2 = self::createStub(EventDate::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..66213bf1 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,113 @@ class PriceCategoryTest extends UnitTestCase protected function setUp(): void { - $this->priceCategory = new PriceCategory(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->priceCategory); + $this->priceCategory = new PriceCategory(); } #[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 { - $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..f37539fc 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,64 @@ class SpecialPriceTest extends UnitTestCase protected function setUp(): void { - $this->specialPrice = new SpecialPrice(); - } + parent::setUp(); - protected function tearDown(): void - { - unset($this->specialPrice); + $this->specialPrice = new SpecialPrice(); } #[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() ); } } diff --git a/composer.json b/composer.json index cd8679d1..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": { @@ -46,25 +47,30 @@ } }, "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", + "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" }, "scripts": { @@ -107,5 +113,8 @@ "@test:typoscript:lint", "@test:php" ] + }, + "suggest": { + "typo3/cms-form": "^13.4" } } diff --git a/ext_emconf.php b/ext_emconf.php index 47d4b501..0dc2f820 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -4,15 +4,15 @@ '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', '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; [