From 442a6842ec8c0d53670a77f0d54d2f333ff9fe8e Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 18 Feb 2021 14:54:00 +0100 Subject: [PATCH] Add prices to tourist attraction Import prices, show them in records and make them accessible in frontend. --- .../Import/Converter/TouristAttraction.php | 5 + Classes/Domain/Import/JsonLD/Parser.php | 42 +-- .../Import/JsonLD/Parser/GenericFields.php | 43 +++ .../Import/JsonLD/Parser/LanguageValues.php | 60 +++ .../Domain/Import/JsonLD/Parser/Offers.php | 103 ++++++ Classes/Domain/Model/Frontend/Offer.php | 70 ++++ Classes/Domain/Model/Frontend/Offers.php | 75 ++++ Classes/Domain/Model/Frontend/Price.php | 83 +++++ .../Model/Frontend/TouristAttraction.php | 6 + .../tt_content_tourist_attraction.php | 3 +- .../TCA/tx_thuecat_tourist_attraction.php | 10 +- Resources/Private/Language/locallang.xlf | 10 + Resources/Private/Language/locallang_tca.xlf | 3 + .../ContentElement/TouristAttraction.html | 17 + ...sTouristAttractionsWithRelationsResult.csv | 18 +- Tests/Functional/ImportTest.php | 3 + .../Converter/TouristAttractionTest.php | 15 + .../JsonLD/Parser/GenericFieldsTest.php | 95 +++++ .../JsonLD/Parser/LanguageValuesTest.php | 147 ++++++++ .../Import/JsonLD/Parser/OffersTest.php | 349 ++++++++++++++++++ .../Unit/Domain/Import/JsonLD/ParserTest.php | 347 +++++------------ ecs.php | 2 + ext_tables.sql | 1 + 23 files changed, 1205 insertions(+), 302 deletions(-) create mode 100644 Classes/Domain/Import/JsonLD/Parser/GenericFields.php create mode 100644 Classes/Domain/Import/JsonLD/Parser/LanguageValues.php create mode 100644 Classes/Domain/Import/JsonLD/Parser/Offers.php create mode 100644 Classes/Domain/Model/Frontend/Offer.php create mode 100644 Classes/Domain/Model/Frontend/Offers.php create mode 100644 Classes/Domain/Model/Frontend/Price.php create mode 100644 Tests/Unit/Domain/Import/JsonLD/Parser/GenericFieldsTest.php create mode 100644 Tests/Unit/Domain/Import/JsonLD/Parser/LanguageValuesTest.php create mode 100644 Tests/Unit/Domain/Import/JsonLD/Parser/OffersTest.php diff --git a/Classes/Domain/Import/Converter/TouristAttraction.php b/Classes/Domain/Import/Converter/TouristAttraction.php index 38dec3a..1176186 100644 --- a/Classes/Domain/Import/Converter/TouristAttraction.php +++ b/Classes/Domain/Import/Converter/TouristAttraction.php @@ -26,6 +26,7 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Converter; use TYPO3\CMS\Core\Utility\GeneralUtility; use WerkraumMedia\ThueCat\Domain\Import\Importer\LanguageHandling; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Offers; use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; @@ -35,17 +36,20 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; class TouristAttraction implements Converter { private Parser $parser; + private Offers $parserForOffers; private LanguageHandling $language; private OrganisationRepository $organisationRepository; private TownRepository $townRepository; public function __construct( Parser $parser, + Offers $parserForOffers, LanguageHandling $language, OrganisationRepository $organisationRepository, TownRepository $townRepository ) { $this->parser = $parser; + $this->parserForOffers = $parserForOffers; $this->language = $language; $this->organisationRepository = $organisationRepository; $this->townRepository = $townRepository; @@ -77,6 +81,7 @@ class TouristAttraction implements Converter 'opening_hours' => json_encode($this->parser->getOpeningHours($jsonLD)), 'address' => json_encode($this->parser->getAddress($jsonLD)), 'media' => json_encode($this->parser->getMedia($jsonLD)), + 'offers' => json_encode($this->parserForOffers->get($jsonLD, $language)), ] ); $entities->add($entity); diff --git a/Classes/Domain/Import/JsonLD/Parser.php b/Classes/Domain/Import/JsonLD/Parser.php index c3cb46b..fe04de1 100644 --- a/Classes/Domain/Import/JsonLD/Parser.php +++ b/Classes/Domain/Import/JsonLD/Parser.php @@ -24,20 +24,24 @@ namespace WerkraumMedia\ThueCat\Domain\Import\JsonLD; */ use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Address; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Media; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours; class Parser { + private GenericFields $genericFields; private OpeningHours $openingHours; private Address $address; private Media $media; public function __construct( + GenericFields $genericFields, OpeningHours $openingHours, Address $address, Media $media ) { + $this->genericFields = $genericFields; $this->openingHours = $openingHours; $this->address = $address; $this->media = $media; @@ -50,12 +54,12 @@ class Parser public function getTitle(array $jsonLD, string $language = ''): string { - return $this->getValueForLanguage($jsonLD['schema:name'], $language); + return $this->genericFields->getTitle($jsonLD, $language); } public function getDescription(array $jsonLD, string $language = ''): string { - return $this->getValueForLanguage($jsonLD['schema:description'], $language); + return $this->genericFields->getDescription($jsonLD, $language); } public function getManagerId(array $jsonLD): string @@ -108,6 +112,7 @@ class Parser $languages = array_map(function (array $language) { $language = $language['@value']; + // TODO: Make configurable / easier to extend if ($language === 'thuecat:German') { return 'de'; } @@ -123,37 +128,4 @@ class Parser return $languages; } - - private function getValueForLanguage( - array $property, - string $language - ): string { - if ( - $this->doesLanguageMatch($property, $language) - && isset($property['@value']) - ) { - return $property['@value']; - } - - foreach ($property as $languageEntry) { - if ( - is_array($languageEntry) - && $this->doesLanguageMatch($languageEntry, $language) - ) { - return $languageEntry['@value']; - } - } - - return ''; - } - - private function doesLanguageMatch(array $property, string $language): bool - { - return isset($property['@language']) - && ( - $property['@language'] === $language - || $language === '' - ) - ; - } } diff --git a/Classes/Domain/Import/JsonLD/Parser/GenericFields.php b/Classes/Domain/Import/JsonLD/Parser/GenericFields.php new file mode 100644 index 0000000..840ecaf --- /dev/null +++ b/Classes/Domain/Import/JsonLD/Parser/GenericFields.php @@ -0,0 +1,43 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +class GenericFields +{ + private LanguageValues $languageValues; + + public function __construct( + LanguageValues $languageValues + ) { + $this->languageValues = $languageValues; + } + + public function getTitle(array $jsonLD, string $language = ''): string + { + return $this->languageValues->getValueForLanguage($jsonLD['schema:name'] ?? [], $language); + } + + public function getDescription(array $jsonLD, string $language = ''): string + { + return $this->languageValues->getValueForLanguage($jsonLD['schema:description'] ?? [], $language); + } +} diff --git a/Classes/Domain/Import/JsonLD/Parser/LanguageValues.php b/Classes/Domain/Import/JsonLD/Parser/LanguageValues.php new file mode 100644 index 0000000..8283aac --- /dev/null +++ b/Classes/Domain/Import/JsonLD/Parser/LanguageValues.php @@ -0,0 +1,60 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +class LanguageValues +{ + public function getValueForLanguage( + array $property, + string $language + ): string { + if ( + $this->doesLanguageMatch($property, $language) + && isset($property['@value']) + ) { + return $property['@value']; + } + + foreach ($property as $languageEntry) { + if ( + is_array($languageEntry) + && $this->doesLanguageMatch($languageEntry, $language) + ) { + return $languageEntry['@value']; + } + } + + return ''; + } + + private function doesLanguageMatch( + array $property, + string $language + ): bool { + return isset($property['@language']) + && ( + $property['@language'] === $language + || $language === '' + ) + ; + } +} diff --git a/Classes/Domain/Import/JsonLD/Parser/Offers.php b/Classes/Domain/Import/JsonLD/Parser/Offers.php new file mode 100644 index 0000000..7c4d48f --- /dev/null +++ b/Classes/Domain/Import/JsonLD/Parser/Offers.php @@ -0,0 +1,103 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +class Offers +{ + private GenericFields $genericFields; + + public function __construct( + GenericFields $genericFields + ) { + $this->genericFields = $genericFields; + } + + public function get(array $jsonLD, string $language): array + { + $offers = []; + $jsonLDOffers = $jsonLD['schema:makesOffer'] ?? []; + foreach ($jsonLDOffers as $jsonLDOffer) { + $offer = $this->getOffer($jsonLDOffer, $language); + if ($offer !== []) { + $offers[] = $offer; + } + } + + return $offers; + } + + private function getOffer(array $jsonLD, string $language): array + { + return [ + 'title' => $this->genericFields->getTitle($jsonLD, $language), + 'description' => $this->genericFields->getDescription($jsonLD, $language), + 'prices' => $this->getPrices($jsonLD, $language), + ]; + } + + private function getPrices(array $jsonLD, string $language): array + { + $prices = []; + $jsonLDPrices = $jsonLD['schema:priceSpecification'] ?? []; + + foreach ($jsonLDPrices as $jsonLDPrice) { + $price = $this->getPrice($jsonLDPrice, $language); + if ($price !== []) { + $prices[] = $price; + } + } + + return $prices; + } + + private function getPrice(array $jsonLD, string $language): array + { + return [ + 'title' => $this->genericFields->getTitle($jsonLD, $language), + 'description' => $this->genericFields->getDescription($jsonLD, $language), + 'price' => $this->getPriceValue($jsonLD), + 'currency' => $this->getCurrencyValue($jsonLD), + 'rule' => $this->getRuleValue($jsonLD), + ]; + } + + private function getPriceValue(array $jsonLD): float + { + $price = $jsonLD['schema:price']['@value'] ?? 0.0; + $price = floatval($price); + return $price; + } + + private function getCurrencyValue(array $jsonLD): string + { + $currency = $jsonLD['schema:priceCurrency']['@value'] ?? ''; + $currency = str_replace('thuecat:', '', $currency); + return $currency; + } + + private function getRuleValue(array $jsonLD): string + { + $rule = $jsonLD['thuecat:calculationRule']['@value'] ?? ''; + $rule = str_replace('thuecat:', '', $rule); + return $rule; + } +} diff --git a/Classes/Domain/Model/Frontend/Offer.php b/Classes/Domain/Model/Frontend/Offer.php new file mode 100644 index 0000000..d32599b --- /dev/null +++ b/Classes/Domain/Model/Frontend/Offer.php @@ -0,0 +1,70 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +class Offer +{ + private string $title; + private string $description; + private array $prices; + + private function __construct( + string $title, + string $description, + array $prices + ) { + $this->title = $title; + $this->description = $description; + $this->prices = $prices; + } + + public static function createFromArray(array $rawData): self + { + $prices = []; + foreach ($rawData['prices'] as $price) { + $prices[] = Price::createFromArray($price); + } + + return new self( + $rawData['title'], + $rawData['description'], + $prices + ); + } + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getPrices(): array + { + return $this->prices; + } +} diff --git a/Classes/Domain/Model/Frontend/Offers.php b/Classes/Domain/Model/Frontend/Offers.php new file mode 100644 index 0000000..fb6249c --- /dev/null +++ b/Classes/Domain/Model/Frontend/Offers.php @@ -0,0 +1,75 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use TYPO3\CMS\Core\Type\TypeInterface; + +/** + * @implements \Iterator + */ +class Offers implements TypeInterface, \Iterator +{ + private string $serialized = ''; + private array $array = []; + private int $position = 0; + + public function __construct(string $serialized) + { + $this->serialized = $serialized; + $this->array = array_map( + [Offer::class, 'createFromArray'], + json_decode($serialized, true) + ); + } + + public function __toString(): string + { + return $this->serialized; + } + + public function current(): Offer + { + return $this->array[$this->position]; + } + + public function next(): void + { + ++$this->position; + } + + public function key(): int + { + return $this->position; + } + + public function valid(): bool + { + return isset($this->array[$this->position]); + } + + public function rewind(): void + { + $this->position = 0; + } +} diff --git a/Classes/Domain/Model/Frontend/Price.php b/Classes/Domain/Model/Frontend/Price.php new file mode 100644 index 0000000..77276ea --- /dev/null +++ b/Classes/Domain/Model/Frontend/Price.php @@ -0,0 +1,83 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +class Price +{ + private string $title; + private string $description; + private float $price; + private string $currency; + private string $rule; + + private function __construct( + string $title, + string $description, + float $price, + string $currency, + string $rule + ) { + $this->title = $title; + $this->description = $description; + $this->price = $price; + $this->currency = $currency; + $this->rule = $rule; + } + + public static function createFromArray(array $rawData): self + { + return new self( + $rawData['title'], + $rawData['description'], + $rawData['price'], + $rawData['currency'], + $rawData['rule'] + ); + } + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getPrice(): float + { + return $this->price; + } + + public function getCurrency(): string + { + return $this->currency; + } + + public function getRule(): string + { + return $this->rule; + } +} diff --git a/Classes/Domain/Model/Frontend/TouristAttraction.php b/Classes/Domain/Model/Frontend/TouristAttraction.php index 9207cb9..f07e0d8 100644 --- a/Classes/Domain/Model/Frontend/TouristAttraction.php +++ b/Classes/Domain/Model/Frontend/TouristAttraction.php @@ -30,6 +30,7 @@ class TouristAttraction extends AbstractEntity protected string $title = ''; protected string $description = ''; protected ?OpeningHours $openingHours = null; + protected ?Offers $offers = null; protected ?Address $address = null; protected ?Town $town = null; protected ?Media $media = null; @@ -49,6 +50,11 @@ class TouristAttraction extends AbstractEntity return $this->openingHours; } + public function getOffers(): ?Offers + { + return $this->offers; + } + public function getAddress(): ?Address { return $this->address; diff --git a/Configuration/TCA/Overrides/tt_content_tourist_attraction.php b/Configuration/TCA/Overrides/tt_content_tourist_attraction.php index e8c2213..f6a5b8e 100644 --- a/Configuration/TCA/Overrides/tt_content_tourist_attraction.php +++ b/Configuration/TCA/Overrides/tt_content_tourist_attraction.php @@ -14,8 +14,7 @@ defined('TYPO3') or die(); ], 'types' => [ $cType => [ - 'showitem' => - '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,' + 'showitem' => '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,' . '--palette--;;general,' . '--palette--;;headers,' . 'records,' diff --git a/Configuration/TCA/tx_thuecat_tourist_attraction.php b/Configuration/TCA/tx_thuecat_tourist_attraction.php index 6c04bd0..1264258 100644 --- a/Configuration/TCA/tx_thuecat_tourist_attraction.php +++ b/Configuration/TCA/tx_thuecat_tourist_attraction.php @@ -101,6 +101,14 @@ return (static function (string $extensionKey, string $tableName) { 'readOnly' => true, ], ], + 'offers' => [ + 'label' => $languagePath . '.offers', + 'l10n_mode' => 'exclude', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], 'remote_id' => [ 'label' => $languagePath . '.remote_id', 'l10n_mode' => 'exclude', @@ -150,7 +158,7 @@ return (static function (string $extensionKey, string $tableName) { ], 'types' => [ '0' => [ - 'showitem' => '--palette--;;language, title, description, opening_hours, address, media, remote_id, --div--;' . $languagePath . '.tab.relations, town, managed_by', + 'showitem' => '--palette--;;language, title, description, opening_hours, offers, address, media, remote_id, --div--;' . $languagePath . '.tab.relations, town, managed_by', ], ], ]; diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 7882d95..9eacac1 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -106,6 +106,16 @@ Imported configuration "%1$s". But had at least one error. + + + pro Paket + + + pro Gruppe + + + pro Person + diff --git a/Resources/Private/Language/locallang_tca.xlf b/Resources/Private/Language/locallang_tca.xlf index 58e1364..b1a7bc0 100644 --- a/Resources/Private/Language/locallang_tca.xlf +++ b/Resources/Private/Language/locallang_tca.xlf @@ -102,6 +102,9 @@ Media + + Offers + Town diff --git a/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html b/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html index 87ce4f1..bd6242c 100644 --- a/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html +++ b/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html @@ -27,5 +27,22 @@ {entity.address.fax}

+ + + +

{offer.title}

+ +

{offer.description}

+
+ +

{price.title}

+ +

{price.description}

+
+ {price.price -> f:format.currency(decimalSeparator: ',', thousandsSeparator: '.', decimals: 2, currencySign: price.currency)} + {f:translate(id: 'content.price.rule.{price.rule}', default: price.rule, extensionName: 'Thuecat')} +
+
+
diff --git a/Tests/Functional/Fixtures/Import/ImportsTouristAttractionsWithRelationsResult.csv b/Tests/Functional/Fixtures/Import/ImportsTouristAttractionsWithRelationsResult.csv index 284cdcc..aae1312 100644 --- a/Tests/Functional/Fixtures/Import/ImportsTouristAttractionsWithRelationsResult.csv +++ b/Tests/Functional/Fixtures/Import/ImportsTouristAttractionsWithRelationsResult.csv @@ -1,10 +1,10 @@ tx_thuecat_tourist_attraction -,"uid","pid","sys_language_uid","l18n_parent","l10n_source","l10n_state","remote_id","title","managed_by","town","address" -,1,10,0,0,0,\NULL,"https://thuecat.org/resources/835224016581-dara","Dom St. Marien",1,1,"{""street"":""Domstufen 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""dominformation@domberg-erfurt.de"",""phone"":""+49 361 6461265"",""fax"":"""",""geo"":{""latitude"":50.975955358589545,""longitude"":11.023667024961856}}" -,2,10,1,1,1,\NULL,"https://thuecat.org/resources/835224016581-dara","Cathedral of St. Mary",1,1,"{""street"":""Domstufen 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""dominformation@domberg-erfurt.de"",""phone"":""+49 361 6461265"",""fax"":"""",""geo"":{""latitude"":50.975955358589545,""longitude"":11.023667024961856}}" -,3,10,0,0,0,\NULL,"https://thuecat.org/resources/165868194223-zmqf","Alte Synagoge",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}" -,4,10,1,3,3,\NULL,"https://thuecat.org/resources/165868194223-zmqf","Old Synagogue",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}" -,5,10,2,3,3,\NULL,"https://thuecat.org/resources/165868194223-zmqf","La vieille synagogue",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}" -,6,10,0,0,0,\NULL,"https://thuecat.org/resources/215230952334-yyno","Krämerbrücke",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}" -,7,10,1,6,6,\NULL,"https://thuecat.org/resources/215230952334-yyno","Merchants' Bridge",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}" -,8,10,2,6,6,\NULL,"https://thuecat.org/resources/215230952334-yyno","Pont de l'épicier",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}" +,"uid","pid","sys_language_uid","l18n_parent","l10n_source","l10n_state","remote_id","title","managed_by","town","address","offers" +,1,10,0,0,0,\NULL,"https://thuecat.org/resources/835224016581-dara","Dom St. Marien",1,1,"{""street"":""Domstufen 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""dominformation@domberg-erfurt.de"",""phone"":""+49 361 6461265"",""fax"":"""",""geo"":{""latitude"":50.975955358589545,""longitude"":11.023667024961856}}","[]" +,2,10,1,1,1,\NULL,"https://thuecat.org/resources/835224016581-dara","Cathedral of St. Mary",1,1,"{""street"":""Domstufen 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""dominformation@domberg-erfurt.de"",""phone"":""+49 361 6461265"",""fax"":"""",""geo"":{""latitude"":50.975955358589545,""longitude"":11.023667024961856}}","[]" +,3,10,0,0,0,\NULL,"https://thuecat.org/resources/165868194223-zmqf","Alte Synagoge",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}","[{""title"":""F\u00fchrungen"",""description"":""Immer samstags, um 11:15 Uhr findet eine \u00f6ffentliche F\u00fchrung durch das Museum statt. Dauer etwa 90 Minuten"",""prices"":[{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""}]},{""title"":""Eintritt"",""description"":""Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei"",""prices"":[{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Familienkarte"",""description"":"""",""price"":17,""currency"":""EUR"",""rule"":""PerGroup""},{""title"":""ErfurtCard"",""description"":"""",""price"":14.9,""currency"":""EUR"",""rule"":""PerPackage""},{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""}]}]" +,4,10,1,3,3,\NULL,"https://thuecat.org/resources/165868194223-zmqf","Old Synagogue",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}","[{""title"":""F\u00fchrungen"",""description"":""Immer samstags, um 11:15 Uhr findet eine \u00f6ffentliche F\u00fchrung durch das Museum statt. Dauer etwa 90 Minuten"",""prices"":[{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""}]},{""title"":""Eintritt"",""description"":""Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei"",""prices"":[{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Familienkarte"",""description"":"""",""price"":17,""currency"":""EUR"",""rule"":""PerGroup""},{""title"":""ErfurtCard"",""description"":"""",""price"":14.9,""currency"":""EUR"",""rule"":""PerPackage""},{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""}]}]" +,5,10,2,3,3,\NULL,"https://thuecat.org/resources/165868194223-zmqf","La vieille synagogue",1,1,"{""street"":""Waagegasse 8"",""zip"":""99084"",""city"":""Erfurt"",""email"":""altesynagoge@erfurt.de"",""phone"":""+49 361 6551520"",""fax"":""+49 361 6551669"",""geo"":{""latitude"":50.978765,""longitude"":11.029133}}","[{""title"":""F\u00fchrungen"",""description"":""Immer samstags, um 11:15 Uhr findet eine \u00f6ffentliche F\u00fchrung durch das Museum statt. Dauer etwa 90 Minuten"",""prices"":[{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""}]},{""title"":""Eintritt"",""description"":""Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei"",""prices"":[{""title"":""Erm\u00e4\u00dfigt"",""description"":""als erm\u00e4\u00dfigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner\/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt"",""price"":5,""currency"":""EUR"",""rule"":""PerPerson""},{""title"":""Familienkarte"",""description"":"""",""price"":17,""currency"":""EUR"",""rule"":""PerGroup""},{""title"":""ErfurtCard"",""description"":"""",""price"":14.9,""currency"":""EUR"",""rule"":""PerPackage""},{""title"":""Erwachsene"",""description"":"""",""price"":8,""currency"":""EUR"",""rule"":""PerPerson""}]}]" +,6,10,0,0,0,\NULL,"https://thuecat.org/resources/215230952334-yyno","Krämerbrücke",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}","[]" +,7,10,1,6,6,\NULL,"https://thuecat.org/resources/215230952334-yyno","Merchants' Bridge",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}","[]" +,8,10,2,6,6,\NULL,"https://thuecat.org/resources/215230952334-yyno","Pont de l'épicier",1,1,"{""street"":""Benediktsplatz 1"",""zip"":""99084"",""city"":""Erfurt"",""email"":""service@erfurt-tourismus.de"",""phone"":""+49 361 66 400"",""fax"":"""",""geo"":{""latitude"":50.978772,""longitude"":11.031622}}","[]" diff --git a/Tests/Functional/ImportTest.php b/Tests/Functional/ImportTest.php index cef8824..a7842a2 100644 --- a/Tests/Functional/ImportTest.php +++ b/Tests/Functional/ImportTest.php @@ -55,7 +55,10 @@ use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase; * @uses WerkraumMedia\ThueCat\Domain\Import\Importer\LanguageHandling * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Address + * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields + * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\LanguageValues * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Media + * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Offers * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours * @uses WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours * @uses WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection diff --git a/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php b/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php index a5cd9f4..25ffc62 100644 --- a/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php +++ b/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php @@ -28,6 +28,7 @@ use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\ThueCat\Domain\Import\Converter\TouristAttraction; use WerkraumMedia\ThueCat\Domain\Import\Importer\LanguageHandling; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Offers; use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; @@ -50,12 +51,14 @@ class TouristAttractionTest extends TestCase public function canBeCreated(): void { $parser = $this->prophesize(Parser::class); + $parserForOffers = $this->prophesize(Offers::class); $language = $this->prophesize(LanguageHandling::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); $townRepository = $this->prophesize(TownRepository::class); $subject = new TouristAttraction( $parser->reveal(), + $parserForOffers->reveal(), $language->reveal(), $organisationRepository->reveal(), $townRepository->reveal() @@ -70,12 +73,14 @@ class TouristAttractionTest extends TestCase public function canConvert(): void { $parser = $this->prophesize(Parser::class); + $parserForOffers = $this->prophesize(Offers::class); $language = $this->prophesize(LanguageHandling::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); $townRepository = $this->prophesize(TownRepository::class); $subject = new TouristAttraction( $parser->reveal(), + $parserForOffers->reveal(), $language->reveal(), $organisationRepository->reveal(), $townRepository->reveal() @@ -126,6 +131,9 @@ class TouristAttractionTest extends TestCase $parser->getAddress($jsonLD)->willReturn([]); $parser->getMedia($jsonLD)->willReturn([]); + $parserForOffers = $this->prophesize(Offers::class); + $parserForOffers->get($jsonLD, 'de')->willReturn([]); + $language = $this->prophesize(LanguageHandling::class); $language->isUnknown('de', 10)->willReturn(false); $language->getSystemUid('de', 10)->willReturn(0); @@ -138,6 +146,7 @@ class TouristAttractionTest extends TestCase $subject = new TouristAttraction( $parser->reveal(), + $parserForOffers->reveal(), $language->reveal(), $organisationRepository->reveal(), $townRepository->reveal() @@ -160,6 +169,7 @@ class TouristAttractionTest extends TestCase 'opening_hours' => '[]', 'address' => '[]', 'media' => '[]', + 'offers' => '[]', ], $entity->getData()); } @@ -205,6 +215,9 @@ class TouristAttractionTest extends TestCase $parser->getAddress($jsonLD)->willReturn([]); $parser->getMedia($jsonLD)->willReturn([]); + $parserForOffers = $this->prophesize(Offers::class); + $parserForOffers->get($jsonLD, 'de')->willReturn([]); + $language = $this->prophesize(LanguageHandling::class); $language->isUnknown('de', 10)->willReturn(false); $language->getSystemUid('de', 10)->willReturn(0); @@ -227,6 +240,7 @@ class TouristAttractionTest extends TestCase $subject = new TouristAttraction( $parser->reveal(), + $parserForOffers->reveal(), $language->reveal(), $organisationRepository->reveal(), $townRepository->reveal() @@ -249,6 +263,7 @@ class TouristAttractionTest extends TestCase 'opening_hours' => '[]', 'address' => '[]', 'media' => '[]', + 'offers' => '[]', ], $entity->getData()); } } diff --git a/Tests/Unit/Domain/Import/JsonLD/Parser/GenericFieldsTest.php b/Tests/Unit/Domain/Import/JsonLD/Parser/GenericFieldsTest.php new file mode 100644 index 0000000..a889392 --- /dev/null +++ b/Tests/Unit/Domain/Import/JsonLD/Parser/GenericFieldsTest.php @@ -0,0 +1,95 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\LanguageValues; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields + */ +class GenericFieldsTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $languageValues = $this->prophesize(LanguageValues::class); + + $subject = new GenericFields( + $languageValues->reveal() + ); + + self::assertInstanceOf(GenericFields::class, $subject); + } + + /** + * @test + */ + public function returnsTitle(): void + { + $languageValues = $this->prophesize(LanguageValues::class); + $languageValues->getValueForLanguage([ + '@value' => 'DE Title', + ], 'de')->willReturn('DE Title'); + + $subject = new GenericFields( + $languageValues->reveal() + ); + + $result = $subject->getTitle([ + 'schema:name' => [ + '@value' => 'DE Title', + ], + ], 'de'); + + self::assertSame('DE Title', $result); + } + + /** + * @test + */ + public function returnsDescription(): void + { + $languageValues = $this->prophesize(LanguageValues::class); + $languageValues->getValueForLanguage([ + '@value' => 'DE Description', + ], 'de')->willReturn('DE Description'); + + $subject = new GenericFields( + $languageValues->reveal() + ); + + $result = $subject->getDescription([ + 'schema:description' => [ + '@value' => 'DE Description', + ], + ], 'de'); + + self::assertSame('DE Description', $result); + } +} diff --git a/Tests/Unit/Domain/Import/JsonLD/Parser/LanguageValuesTest.php b/Tests/Unit/Domain/Import/JsonLD/Parser/LanguageValuesTest.php new file mode 100644 index 0000000..96df41c --- /dev/null +++ b/Tests/Unit/Domain/Import/JsonLD/Parser/LanguageValuesTest.php @@ -0,0 +1,147 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use PHPUnit\Framework\TestCase; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\LanguageValues; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\LanguageValues + */ +class LanguageValuesTest extends TestCase +{ + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new LanguageValues( + ); + + self::assertInstanceOf(LanguageValues::class, $subject); + } + + /** + * @test + * @dataProvider setups + */ + public function returnsValue(array $jsonLD, string $language, string $expected): void + { + $subject = new LanguageValues( + ); + + $result = $subject->getValueForLanguage($jsonLD, $language); + + self::assertSame($expected, $result); + } + + public function setups(): array + { + return [ + 'has multiple lanugages, one matches' => [ + 'jsonLD' => [ + [ + '@language' => 'de', + '@value' => 'DE value', + ], + [ + '@language' => 'fr', + '@value' => 'FR value', + ], + ], + 'language' => 'de', + 'expected' => 'DE value', + ], + 'has multiple lanugages, no language specified' => [ + 'jsonLD' => [ + [ + '@language' => 'de', + '@value' => 'DE value', + ], + [ + '@language' => 'fr', + '@value' => 'FR value', + ], + ], + 'language' => '', + 'expected' => 'DE value', + ], + 'has multiple languages, none matches' => [ + 'jsonLD' => [ + [ + '@language' => 'de', + '@value' => 'DE value', + ], + [ + '@language' => 'fr', + '@value' => 'FR value', + ], + ], + 'language' => 'en', + 'expected' => '', + ], + 'has multiple languages, missing @language key' => [ + 'jsonLD' => [ + [ + '@value' => 'DE value', + ], + [ + '@value' => 'FR value', + ], + ], + 'language' => 'en', + 'expected' => '', + ], + 'has single language, that one matches' => [ + 'jsonLD' => [ + '@language' => 'de', + '@value' => 'DE value', + ], + 'language' => 'de', + 'expected' => 'DE value', + ], + 'has single language, but another is requested' => [ + 'jsonLD' => [ + '@language' => 'de', + '@value' => 'DE value', + ], + 'language' => 'en', + 'expected' => '', + ], + 'has single language, no language specified' => [ + 'jsonLD' => [ + '@language' => 'de', + '@value' => 'DE value', + ], + 'language' => '', + 'expected' => 'DE value', + ], + 'has single language, missing @language key' => [ + 'jsonLD' => [ + '@value' => 'DE value', + ], + 'language' => '', + 'expected' => '', + ], + ]; + } +} diff --git a/Tests/Unit/Domain/Import/JsonLD/Parser/OffersTest.php b/Tests/Unit/Domain/Import/JsonLD/Parser/OffersTest.php new file mode 100644 index 0000000..927c31f --- /dev/null +++ b/Tests/Unit/Domain/Import/JsonLD/Parser/OffersTest.php @@ -0,0 +1,349 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Offers; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Offers + */ +class OffersTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $genericFields = $this->prophesize(GenericFields::class); + + $subject = new Offers( + $genericFields->reveal() + ); + + self::assertInstanceOf(Offers::class, $subject); + } + + /** + * @test + */ + public function returnsEmptyArrayIfNoOfferExists(): void + { + $genericFields = $this->prophesize(GenericFields::class); + + $subject = new Offers( + $genericFields->reveal() + ); + + $result = $subject->get([], ''); + + self::assertSame([], $result); + } + + /** + * @test + */ + public function returnsSingleOfferWithSinglePrice(): void + { + $jsonLD = [ + 'schema:makesOffer' => [ + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b5', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:Thing', + 2 => 'schema:Offer', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Führungen', + ], + 'schema:description' => [ + '@language' => 'de', + '@value' => 'Immer samstags, um 11:15 Uhr findet eine öffentliche Führung durch das Museum statt. Dauer etwa 90 Minuten', + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Führungen', + ], + 'schema:offeredBy' => [ + '@id' => 'https://thuecat.org/resources/165868194223-zmqf', + ], + 'schema:priceSpecification' => [ + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b6', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:StructuredValue', + 2 => 'schema:PriceSpecification', + 3 => 'schema:Thing', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Erwachsene', + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Erwachsene', + ], + 'schema:price' => [ + '@type' => 'schema:Number', + '@value' => '8', + ], + 'schema:priceCurrency' => [ + '@type' => 'thuecat:Currency', + '@value' => 'thuecat:EUR', + ], + 'thuecat:calculationRule' => [ + '@type' => 'thuecat:CalculationRule', + '@value' => 'thuecat:PerPerson', + ], + ], + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b7', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:StructuredValue', + 2 => 'schema:PriceSpecification', + 3 => 'schema:Thing', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Ermäßigt', + ], + 'schema:description' => [ + '@language' => 'de', + '@value' => 'als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt', + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Ermäßigt', + ], + 'schema:price' => [ + '@type' => 'schema:Number', + '@value' => '5', + ], + 'schema:priceCurrency' => [ + '@type' => 'thuecat:Currency', + '@value' => 'thuecat:EUR', + ], + 'thuecat:calculationRule' => [ + '@type' => 'thuecat:CalculationRule', + '@value' => 'thuecat:PerPerson', + ], + ], + ], + 'thuecat:offerType' => [ + '@type' => 'thuecat:OfferType', + '@value' => 'thuecat:GuidedTourOffer', + ], + ], + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b8', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:Thing', + 2 => 'schema:Offer', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Eintritt', + ], + 'schema:description' => [ + '@language' => 'de', + '@value' => "Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei", + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Eintritt', + ], + 'schema:offeredBy' => [ + '@id' => 'https://thuecat.org/resources/165868194223-zmqf', + ], + 'schema:priceSpecification' => [ + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b10', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:StructuredValue', + 2 => 'schema:PriceSpecification', + 3 => 'schema:Thing', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Ermäßigt', + ], + 'schema:description' => [ + '@language' => 'de', + '@value' => 'als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt', + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Ermäßigt', + ], + 'schema:price' => [ + '@type' => 'schema:Number', + '@value' => '5', + ], + 'schema:priceCurrency' => [ + '@type' => 'thuecat:Currency', + '@value' => 'thuecat:EUR', + ], + 'thuecat:calculationRule' => [ + '@type' => 'thuecat:CalculationRule', + '@value' => 'thuecat:PerPerson', + ], + ], + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b11', + '@type' => [ + 0 => 'schema:Intangible', + 1 => 'schema:StructuredValue', + 2 => 'schema:PriceSpecification', + 3 => 'schema:Thing', + ], + 'rdfs:label' => [ + '@language' => 'de', + '@value' => 'Familienkarte', + ], + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Familienkarte', + ], + 'schema:price' => [ + '@type' => 'schema:Number', + '@value' => '17', + ], + 'schema:priceCurrency' => [ + '@type' => 'thuecat:Currency', + '@value' => 'thuecat:EUR', + ], + 'thuecat:calculationRule' => [ + '@type' => 'thuecat:CalculationRule', + '@value' => 'thuecat:PerGroup', + ], + ], + ], + 'thuecat:offerType' => [ + '@type' => 'thuecat:OfferType', + '@value' => 'thuecat:EntryOffer', + ], + ], + ], + ]; + + $genericFields = $this->prophesize(GenericFields::class); + + // Offer 1 + $genericFields->getTitle($jsonLD['schema:makesOffer'][0], 'de')->willReturn('Führungen'); + $genericFields->getDescription($jsonLD['schema:makesOffer'][0], 'de')->willReturn('Immer samstags, um 11:15 Uhr findet eine öffentliche Führung durch das Museum statt. Dauer etwa 90 Minuten'); + $genericFields->getTitle( + $jsonLD['schema:makesOffer'][0]['schema:priceSpecification'][0], + 'de' + )->willReturn('Erwachsene'); + $genericFields->getDescription( + $jsonLD['schema:makesOffer'][0]['schema:priceSpecification'][0], + 'de' + )->willReturn(''); + $genericFields->getTitle( + $jsonLD['schema:makesOffer'][0]['schema:priceSpecification'][1], + 'de' + )->willReturn('Ermäßigt'); + $genericFields->getDescription( + $jsonLD['schema:makesOffer'][0]['schema:priceSpecification'][1], + 'de' + )->willReturn('als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt'); + + // Offer2 + $genericFields->getTitle($jsonLD['schema:makesOffer'][1], 'de')->willReturn('Eintritt'); + $genericFields->getDescription($jsonLD['schema:makesOffer'][1], 'de')->willReturn("Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei"); + $genericFields->getTitle( + $jsonLD['schema:makesOffer'][1]['schema:priceSpecification'][0], + 'de' + )->willReturn('Ermäßigt'); + $genericFields->getDescription( + $jsonLD['schema:makesOffer'][1]['schema:priceSpecification'][0], + 'de' + )->willReturn('als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt'); + $genericFields->getTitle( + $jsonLD['schema:makesOffer'][1]['schema:priceSpecification'][1], + 'de' + )->willReturn('Familienkarte'); + $genericFields->getDescription( + $jsonLD['schema:makesOffer'][1]['schema:priceSpecification'][1], + 'de' + )->willReturn(''); + + $subject = new Offers( + $genericFields->reveal() + ); + + $result = $subject->get($jsonLD, 'de'); + + self::assertSame([ + [ + 'title' => 'Führungen', + 'description' => 'Immer samstags, um 11:15 Uhr findet eine öffentliche Führung durch das Museum statt. Dauer etwa 90 Minuten', + 'prices' => [ + [ + 'title' => 'Erwachsene', + 'description' => '', + 'price' => 8.0, + 'currency' => 'EUR', + 'rule' => 'PerPerson', + ], + [ + 'title' => 'Ermäßigt', + 'description' => 'als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt', + 'price' => 5.0, + 'currency' => 'EUR', + 'rule' => 'PerPerson', + ], + ], + ], + [ + 'title' => 'Eintritt', + 'description' => "Schulklassen und Kitagruppen im Rahmen des Unterrichts: Eintritt frei\nAn jedem ersten Dienstag im Monat: Eintritt frei", + 'prices' => [ + [ + 'title' => 'Ermäßigt', + 'description' => 'als ermäßigt gelten schulpflichtige Kinder, Auszubildende, Studierende, Rentner/-innen, Menschen mit Behinderungen, Inhaber Sozialausweis der Landeshauptstadt Erfurt', + 'price' => 5.0, + 'currency' => 'EUR', + 'rule' => 'PerPerson', + ], + [ + 'title' => 'Familienkarte', + 'description' => '', + 'price' => 17.0, + 'currency' => 'EUR', + 'rule' => 'PerGroup', + ], + ], + ], + ], $result); + } +} diff --git a/Tests/Unit/Domain/Import/JsonLD/ParserTest.php b/Tests/Unit/Domain/Import/JsonLD/ParserTest.php index 5d48316..724abf7 100644 --- a/Tests/Unit/Domain/Import/JsonLD/ParserTest.php +++ b/Tests/Unit/Domain/Import/JsonLD/ParserTest.php @@ -27,6 +27,7 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Address; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\GenericFields; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\Media; use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours; @@ -42,10 +43,13 @@ class ParserTest extends TestCase */ public function canBeCreated(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -59,10 +63,13 @@ class ParserTest extends TestCase */ public function returnsId(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -78,12 +85,77 @@ class ParserTest extends TestCase /** * @test */ - public function returnsManagerId(): void + public function returnsTitle(): void { + $jsonLD = [ + 'schema:name' => [ + '@language' => 'de', + '@value' => 'Erfurt', + ], + ]; + + $genericFields = $this->prophesize(GenericFields::class); + $genericFields->getTitle($jsonLD, 'de')->willReturn('Erfurt'); + $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), + $openingHours->reveal(), + $address->reveal(), + $media->reveal() + ); + + $result = $subject->getTitle($jsonLD, 'de'); + + self::assertSame('Erfurt', $result); + } + + /** + * @test + */ + public function returnsDescription(): void + { + $jsonLD = [ + 'schema:description' => [ + '@language' => 'de', + '@value' => 'Erfurt', + ], + ]; + + $genericFields = $this->prophesize(GenericFields::class); + $genericFields->getDescription($jsonLD, 'de')->willReturn('Erfurt'); + + $openingHours = $this->prophesize(OpeningHours::class); + $address = $this->prophesize(Address::class); + $media = $this->prophesize(Media::class); + + $subject = new Parser( + $genericFields->reveal(), + $openingHours->reveal(), + $address->reveal(), + $media->reveal() + ); + + $result = $subject->getDescription($jsonLD, 'de'); + + self::assertSame('Erfurt', $result); + } + + /** + * @test + */ + public function returnsManagerId(): void + { + $genericFields = $this->prophesize(GenericFields::class); + $openingHours = $this->prophesize(OpeningHours::class); + $address = $this->prophesize(Address::class); + $media = $this->prophesize(Media::class); + + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -98,269 +170,18 @@ class ParserTest extends TestCase self::assertSame('https://example.com/resources/165868194223-manager', $result); } - /** - * @test - * @dataProvider titles - */ - public function returnsTitle(array $jsonLD, string $language, string $expected): void - { - $openingHours = $this->prophesize(OpeningHours::class); - $address = $this->prophesize(Address::class); - $media = $this->prophesize(Media::class); - $subject = new Parser( - $openingHours->reveal(), - $address->reveal(), - $media->reveal() - ); - - $result = $subject->getTitle($jsonLD, $language); - - self::assertSame($expected, $result); - } - - public function titles(): array - { - return [ - 'json has multiple lanugages, one matches' => [ - 'jsonLD' => [ - 'schema:name' => [ - [ - '@language' => 'de', - '@value' => 'DE Title', - ], - [ - '@language' => 'fr', - '@value' => 'FR Title', - ], - ], - ], - 'language' => 'de', - 'expected' => 'DE Title', - ], - 'json has multiple lanugages, no language specified' => [ - 'jsonLD' => [ - 'schema:name' => [ - [ - '@language' => 'de', - '@value' => 'DE Title', - ], - [ - '@language' => 'fr', - '@value' => 'FR Title', - ], - ], - ], - 'language' => '', - 'expected' => 'DE Title', - ], - 'json has multiple languages, none matches' => [ - 'jsonLD' => [ - 'schema:name' => [ - [ - '@language' => 'de', - '@value' => 'DE Title', - ], - [ - '@language' => 'fr', - '@value' => 'FR Title', - ], - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json has multiple languages, missing @language key' => [ - 'jsonLD' => [ - 'schema:name' => [ - [ - '@value' => 'DE Title', - ], - [ - '@value' => 'FR Title', - ], - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json has single language, that one matches' => [ - 'jsonLD' => [ - 'schema:name' => [ - '@language' => 'de', - '@value' => 'DE Title', - ], - ], - 'language' => 'de', - 'expected' => 'DE Title', - ], - 'json contains single language, but another is requested' => [ - 'jsonLD' => [ - 'schema:name' => [ - '@language' => 'de', - '@value' => 'DE Title', - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json contains single language, no language specified' => [ - 'jsonLD' => [ - 'schema:name' => [ - '@language' => 'de', - '@value' => 'DE Title', - ], - ], - 'language' => '', - 'expected' => 'DE Title', - ], - 'json contains single language, missing @language key' => [ - 'jsonLD' => [ - 'schema:name' => [ - '@value' => 'DE Title', - ], - ], - 'language' => '', - 'expected' => '', - ], - ]; - } - - /** - * @test - * @dataProvider descriptions - */ - public function returnsDescription(array $jsonLD, string $language, string $expected): void - { - $openingHours = $this->prophesize(OpeningHours::class); - $address = $this->prophesize(Address::class); - $media = $this->prophesize(Media::class); - $subject = new Parser( - $openingHours->reveal(), - $address->reveal(), - $media->reveal() - ); - - $result = $subject->getDescription($jsonLD, $language); - - self::assertSame($expected, $result); - } - - public function descriptions(): array - { - return [ - 'json has multiple lanugages, one matches' => [ - 'jsonLD' => [ - 'schema:description' => [ - [ - '@language' => 'de', - '@value' => 'DE Description', - ], - [ - '@language' => 'fr', - '@value' => 'FR Description', - ], - ], - ], - 'language' => 'de', - 'expected' => 'DE Description', - ], - 'json has multiple lanugages, no language specified' => [ - 'jsonLD' => [ - 'schema:description' => [ - [ - '@language' => 'de', - '@value' => 'DE Description', - ], - [ - '@language' => 'fr', - '@value' => 'FR Description', - ], - ], - ], - 'language' => '', - 'expected' => 'DE Description', - ], - 'json has multiple languages, none matches' => [ - 'jsonLD' => [ - 'schema:description' => [ - [ - '@language' => 'de', - '@value' => 'DE Description', - ], - [ - '@language' => 'fr', - '@value' => 'FR Description', - ], - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json has multiple languages, missing @language key' => [ - 'jsonLD' => [ - 'schema:description' => [ - [ - '@value' => 'DE Description', - ], - [ - '@value' => 'FR Description', - ], - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json has single language, that one matches' => [ - 'jsonLD' => [ - 'schema:description' => [ - '@language' => 'de', - '@value' => 'DE Description', - ], - ], - 'language' => 'de', - 'expected' => 'DE Description', - ], - 'json contains single language, but another is requested' => [ - 'jsonLD' => [ - 'schema:description' => [ - '@language' => 'de', - '@value' => 'DE Description', - ], - ], - 'language' => 'en', - 'expected' => '', - ], - 'json contains single language, no language specified' => [ - 'jsonLD' => [ - 'schema:description' => [ - '@language' => 'de', - '@value' => 'DE Description', - ], - ], - 'language' => '', - 'expected' => 'DE Description', - ], - 'json contains single language, missing @language key' => [ - 'jsonLD' => [ - 'schema:description' => [ - '@value' => 'DE Description', - ], - ], - 'language' => '', - 'expected' => '', - ], - ]; - } - /** * @test */ public function returnsContainedInPlaceIds(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -390,10 +211,13 @@ class ParserTest extends TestCase */ public function returnsLanguages(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -428,10 +252,13 @@ class ParserTest extends TestCase */ public function throwsExceptionOnUnkownLanguage(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -453,10 +280,13 @@ class ParserTest extends TestCase */ public function returnsNoLanguagesIfInfoIsMissing(): void { + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -489,11 +319,14 @@ class ParserTest extends TestCase 'daysOfWeek' => [], ]; + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $openingHours->get($jsonLD)->willReturn($generatedOpeningHours); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); + $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -527,12 +360,14 @@ class ParserTest extends TestCase 'fax' => '', ]; + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $address->get($jsonLD)->willReturn($generatedAddress); $media = $this->prophesize(Media::class); $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() @@ -583,12 +418,14 @@ class ParserTest extends TestCase ], ]; + $genericFields = $this->prophesize(GenericFields::class); $openingHours = $this->prophesize(OpeningHours::class); $address = $this->prophesize(Address::class); $media = $this->prophesize(Media::class); $media->get($jsonLD)->willReturn($generatedMedia); $subject = new Parser( + $genericFields->reveal(), $openingHours->reveal(), $address->reveal(), $media->reveal() diff --git a/ecs.php b/ecs.php index 7970c87..169ee16 100644 --- a/ecs.php +++ b/ecs.php @@ -3,6 +3,7 @@ declare(strict_types=1); use PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer; +use PhpCsFixer\Fixer\ArrayNotation\NoMultilineWhitespaceAroundDoubleArrowFixer; use PhpCsFixer\Fixer\ArrayNotation\TrailingCommaInMultilineArrayFixer; use PhpCsFixer\Fixer\Import\FullyQualifiedStrictTypesFixer; use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; @@ -39,6 +40,7 @@ return static function (ContainerConfigurator $containerConfigurator): void { $services->set(NoUnusedImportsFixer::class); $services->set(FullyQualifiedStrictTypesFixer::class); + $services->set(NoMultilineWhitespaceAroundDoubleArrowFixer::class); $services->set(ArraySyntaxFixer::class)->call('configure', [[ 'syntax' => 'short', ]]); diff --git a/ext_tables.sql b/ext_tables.sql index cfd3d4f..6a5f55e 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -55,4 +55,5 @@ CREATE TABLE tx_thuecat_tourist_attraction ( opening_hours text DEFAULT '' NOT NULL, address text DEFAULT '' NOT NULL, media text DEFAULT '' NOT NULL, + offers text DEFAULT '' NOT NULL, );