diff --git a/Classes/Domain/Import/Converter/Converter.php b/Classes/Domain/Import/Converter/Converter.php index 01ce484..d61ea5e 100644 --- a/Classes/Domain/Import/Converter/Converter.php +++ b/Classes/Domain/Import/Converter/Converter.php @@ -23,7 +23,7 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Converter; * 02110-1301, USA. */ -use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; interface Converter { @@ -33,5 +33,9 @@ interface Converter */ public function canConvert(array $type): bool; - public function convert(array $jsonIdOfEntity): Entity; + /** + * A single JSONLD entity can have multiple languages. + * That may result in multiple entities in TYPO3. + */ + public function convert(array $jsonLD): EntityCollection; } diff --git a/Classes/Domain/Import/Converter/Organisation.php b/Classes/Domain/Import/Converter/Organisation.php index acdc574..ffae345 100644 --- a/Classes/Domain/Import/Converter/Organisation.php +++ b/Classes/Domain/Import/Converter/Organisation.php @@ -23,21 +23,38 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Converter; * 02110-1301, USA. */ +use TYPO3\CMS\Core\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; class Organisation implements Converter { - public function convert(array $jsonIdOfEntity): GenericEntity + private Parser $parser; + + public function __construct( + Parser $parser + ) { + $this->parser = $parser; + } + + public function convert(array $jsonLD): EntityCollection { - return new GenericEntity( + $entity = GeneralUtility::makeInstance( + GenericEntity::class, 10, 'tx_thuecat_organisation', - $jsonIdOfEntity['@id'], + 0, + $this->parser->getId($jsonLD), [ - 'title' => $jsonIdOfEntity['schema:name']['@value'], - 'description' => $jsonIdOfEntity['schema:description']['@value'], + 'title' => $this->parser->getTitle($jsonLD), + 'description' => $this->parser->getDescription($jsonLD), ] ); + $entities = GeneralUtility::makeInstance(EntityCollection::class); + $entities->add($entity); + + return $entities; } public function canConvert(array $type): bool diff --git a/Classes/Domain/Import/Converter/TouristAttraction.php b/Classes/Domain/Import/Converter/TouristAttraction.php new file mode 100644 index 0000000..4e595a4 --- /dev/null +++ b/Classes/Domain/Import/Converter/TouristAttraction.php @@ -0,0 +1,86 @@ + + * + * 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\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; +use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; + +class TouristAttraction implements Converter +{ + private Parser $parser; + private OrganisationRepository $organisationRepository; + private TownRepository $townRepository; + + public function __construct( + Parser $parser, + OrganisationRepository $organisationRepository, + TownRepository $townRepository + ) { + $this->parser = $parser; + $this->organisationRepository = $organisationRepository; + $this->townRepository = $townRepository; + } + + public function convert(array $jsonLD): EntityCollection + { + $storagePid = 10; + $manager = $this->organisationRepository->findOneByRemoteId($this->parser->getManagerId($jsonLD)); + $town = $this->townRepository->findOneByRemoteIds($this->parser->getContainedInPlaceIds($jsonLD)); + $entities = GeneralUtility::makeInstance(EntityCollection::class); + + foreach ($this->parser->getLanguages($jsonLD) as $language) { + if ($language !== 'de') { + continue; + } + $systemLanguageUid = 0; + + $entity = GeneralUtility::makeInstance( + GenericEntity::class, + $storagePid, + 'tx_thuecat_tourist_attraction', + $systemLanguageUid, + $this->parser->getId($jsonLD), + [ + 'title' => $this->parser->getTitle($jsonLD, $language), + 'description' => $this->parser->getDescription($jsonLD, $language), + 'managed_by' => $manager ? $manager->getUid() : 0, + 'town' => $town ? $town->getUid() : 0, + 'opening_hours' => json_encode($this->parser->getOpeningHours($jsonLD)), + ] + ); + $entities->add($entity); + } + + return $entities; + } + + public function canConvert(array $type): bool + { + return array_search('schema:TouristAttraction', $type) !== false; + } +} diff --git a/Classes/Domain/Import/Converter/TouristInformation.php b/Classes/Domain/Import/Converter/TouristInformation.php index c4e37ec..809a357 100644 --- a/Classes/Domain/Import/Converter/TouristInformation.php +++ b/Classes/Domain/Import/Converter/TouristInformation.php @@ -23,50 +23,59 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Converter; * 02110-1301, USA. */ +use TYPO3\CMS\Core\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; class TouristInformation implements Converter { + private Parser $parser; private OrganisationRepository $organisationRepository; private TownRepository $townRepository; public function __construct( + Parser $parser, OrganisationRepository $organisationRepository, TownRepository $townRepository ) { + $this->parser = $parser; $this->organisationRepository = $organisationRepository; $this->townRepository = $townRepository; } - public function convert(array $jsonIdOfEntity): GenericEntity + public function convert(array $jsonLD): EntityCollection { - $manager = $this->organisationRepository->findOneByRemoteId($jsonIdOfEntity['thuecat:managedBy']['@id']); - $town = $this->townRepository->findOneByRemoteIds($this->getContainedInPlaceIds($jsonIdOfEntity)); + $manager = $this->organisationRepository->findOneByRemoteId( + $this->parser->getManagerId($jsonLD) + ); + $town = $this->townRepository->findOneByRemoteIds( + $this->parser->getContainedInPlaceIds($jsonLD) + ); - return new GenericEntity( + $entity = GeneralUtility::makeInstance( + GenericEntity::class, 10, 'tx_thuecat_tourist_information', - $jsonIdOfEntity['@id'], + 0, + $this->parser->getId($jsonLD), [ - 'title' => $jsonIdOfEntity['schema:name']['@value'], - 'description' => $jsonIdOfEntity['schema:description'][0]['@value'], + 'title' => $this->parser->getTitle($jsonLD), + 'description' => $this->parser->getDescription($jsonLD), 'managed_by' => $manager ? $manager->getUid() : 0, 'town' => $town ? $town->getUid() : 0, ] ); + $entities = GeneralUtility::makeInstance(EntityCollection::class); + $entities->add($entity); + + return $entities; } public function canConvert(array $type): bool { return array_search('thuecat:TouristInformation', $type) !== false; } - - private function getContainedInPlaceIds(array $jsonIdOfEntity): array - { - return array_map(function (array $place) { - return $place['@id']; - }, $jsonIdOfEntity['schema:containedInPlace']); - } } diff --git a/Classes/Domain/Import/Converter/Town.php b/Classes/Domain/Import/Converter/Town.php index 3b332f1..61cdbdf 100644 --- a/Classes/Domain/Import/Converter/Town.php +++ b/Classes/Domain/Import/Converter/Town.php @@ -23,32 +23,47 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Converter; * 02110-1301, USA. */ +use TYPO3\CMS\Core\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; class Town implements Converter { + private Parser $parser; private OrganisationRepository $organisationRepository; public function __construct( + Parser $parser, OrganisationRepository $organisationRepository ) { + $this->parser = $parser; $this->organisationRepository = $organisationRepository; } - public function convert(array $jsonIdOfEntity): GenericEntity + public function convert(array $jsonLD): EntityCollection { - $manager = $this->organisationRepository->findOneByRemoteId($jsonIdOfEntity['thuecat:managedBy']['@id']); - return new GenericEntity( + $manager = $this->organisationRepository->findOneByRemoteId( + $this->parser->getManagerId($jsonLD) + ); + + $entity = GeneralUtility::makeInstance( + GenericEntity::class, 10, 'tx_thuecat_town', - $jsonIdOfEntity['@id'], + 0, + $this->parser->getId($jsonLD), [ - 'title' => $jsonIdOfEntity['schema:name']['@value'], - 'description' => $jsonIdOfEntity['schema:description']['@value'] ?? '', + 'title' => $this->parser->getTitle($jsonLD), + 'description' => $this->parser->getDescription($jsonLD), 'managed_by' => $manager ? $manager->getUid() : 0, ] ); + $entities = GeneralUtility::makeInstance(EntityCollection::class); + $entities->add($entity); + + return $entities; } public function canConvert(array $type): bool diff --git a/Classes/Domain/Import/Importer/SaveData.php b/Classes/Domain/Import/Importer/SaveData.php index a727afb..c6539fa 100644 --- a/Classes/Domain/Import/Importer/SaveData.php +++ b/Classes/Domain/Import/Importer/SaveData.php @@ -26,6 +26,7 @@ namespace WerkraumMedia\ThueCat\Domain\Import\Importer; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\DataHandling\DataHandler; use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog; use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry; @@ -33,6 +34,7 @@ class SaveData { private DataHandler $dataHandler; private ConnectionPool $connectionPool; + private array $errorLog; public function __construct( DataHandler $dataHandler, @@ -42,31 +44,50 @@ class SaveData $this->connectionPool = $connectionPool; } - public function import(Entity $entity, ImportLog $log): void + public function import(EntityCollection $entityCollection, ImportLog $log): void { - $dataHandler = clone $this->dataHandler; + $this->errorLog = []; - $identifier = $this->getIdentifier($entity); - $dataHandler->start([ - $entity->getTypo3DatabaseTableName() => [ - $identifier => array_merge($entity->getData(), [ - 'pid' => $entity->getTypo3StoragePid(), - 'remote_id' => $entity->getRemoteId(), - ]), - ], - ], []); - $dataHandler->process_datamap(); + $this->processSimpleDataHandlerDataMap($entityCollection); + // TODO: Insert update / insert of localization - if (isset($dataHandler->substNEWwithIDs[$identifier])) { - $entity->setImportedTypo3Uid($dataHandler->substNEWwithIDs[$identifier]); - } elseif (is_numeric($identifier)) { - $entity->setExistingTypo3Uid((int) $identifier); + foreach ($entityCollection->getEntities() as $entity) { + $log->addEntry(new ImportLogEntry($entity, $this->errorLog)); + } + } + + private function processSimpleDataHandlerDataMap(EntityCollection $collection): void + { + $dataArray = []; + $identifierMapping = []; + + foreach ($collection->getEntities() as $entity) { + $identifier = $this->getIdentifier($entity); + if (strpos($identifier, 'NEW') === 0 && $entity->isTranslation()) { + continue; + } + if (is_numeric($identifier)) { + $entity->setExistingTypo3Uid((int) $identifier); + } else { + $identifierMapping[spl_object_id($entity)] = $identifier; + } + + $dataArray[$entity->getTypo3DatabaseTableName()][$identifier] = $this->getEntityData($entity); } - $log->addEntry(new ImportLogEntry( - $entity, - $dataHandler->errorLog - )); + $dataHandler = clone $this->dataHandler; + $dataHandler->start($dataArray, []); + $dataHandler->process_datamap(); + $this->errorLog = array_merge($this->errorLog, $dataHandler->errorLog); + + foreach ($collection->getEntities() as $entity) { + if ( + isset($identifierMapping[spl_object_id($entity)]) + && isset($dataHandler->substNEWwithIDs[$identifierMapping[spl_object_id($entity)]]) + ) { + $entity->setImportedTypo3Uid($dataHandler->substNEWwithIDs[$identifierMapping[spl_object_id($entity)]]); + } + } } private function getIdentifier(Entity $entity): string @@ -77,7 +98,17 @@ class SaveData return (string) $existingUid; } - return 'NEW_1'; + $identifier = 'NEW_' . sha1($entity->getRemoteId() . $entity->getTypo3SystemLanguageUid()); + // Ensure new ID is max 30, as this is max volumn of the sys_log column + return substr($identifier, 0, 30); + } + + private function getEntityData(Entity $entity): array + { + return array_merge($entity->getData(), [ + 'pid' => $entity->getTypo3StoragePid(), + 'remote_id' => $entity->getRemoteId(), + ]); } private function getExistingUid(Entity $entity): int diff --git a/Classes/Domain/Import/JsonLD/Parser.php b/Classes/Domain/Import/JsonLD/Parser.php new file mode 100644 index 0000000..b41c72d --- /dev/null +++ b/Classes/Domain/Import/JsonLD/Parser.php @@ -0,0 +1,140 @@ + + * + * 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 WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours; + +class Parser +{ + private OpeningHours $openingHours; + + public function __construct( + OpeningHours $openingHours + ) { + $this->openingHours = $openingHours; + } + public function getId(array $jsonLD): string + { + return $jsonLD['@id']; + } + + public function getTitle(array $jsonLD, string $language = ''): string + { + return $this->getValueForLanguage($jsonLD['schema:name'], $language); + } + + public function getDescription(array $jsonLD, string $language = ''): string + { + return $this->getValueForLanguage($jsonLD['schema:description'], $language); + } + + public function getManagerId(array $jsonLD): string + { + return $jsonLD['thuecat:contentResponsible']['@id']; + } + + /** + * @return string[] + */ + public function getContainedInPlaceIds(array $jsonLD): array + { + return array_map(function (array $place) { + return $place['@id']; + }, $jsonLD['schema:containedInPlace']); + } + + public function getOpeningHours(array $jsonLD): array + { + return $this->openingHours->get($jsonLD); + } + + /** + * @return string[] + */ + public function getLanguages(array $jsonLD): array + { + if (isset($jsonLD['schema:availableLanguage']) === false) { + return []; + } + + $languages = $jsonLD['schema:availableLanguage']; + + $languages = array_filter($languages, function (array $language) { + return isset($language['@type']) + && $language['@type'] === 'thuecat:Language' + ; + }); + + $languages = array_map(function (array $language) { + $language = $language['@value']; + + if ($language === 'thuecat:German') { + return 'de'; + } + if ($language === 'thuecat:English') { + return 'en'; + } + if ($language === 'thuecat:French') { + return 'fr'; + } + + throw new \Exception('Unsupported language "' . $language . '".', 1612367481); + }, $languages); + + 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/OpeningHours.php b/Classes/Domain/Import/JsonLD/Parser/OpeningHours.php new file mode 100644 index 0000000..3fa43a9 --- /dev/null +++ b/Classes/Domain/Import/JsonLD/Parser/OpeningHours.php @@ -0,0 +1,102 @@ + + * + * 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 OpeningHours +{ + public function get(array $jsonLD): array + { + $openingHours = $jsonLD['schema:openingHoursSpecification'] ?? []; + if ($openingHours === []) { + return []; + } + + if (isset($openingHours['@id'])) { + return [$this->parseSingleEntry($openingHours)]; + } + + return array_values(array_map([$this, 'parseSingleEntry'], $openingHours)); + } + + private function parseSingleEntry(array $openingHour): array + { + return [ + 'opens' => $this->getOpens($openingHour), + 'closes' => $this->getCloses($openingHour), + 'from' => $this->getFrom($openingHour), + 'through' => $this->getThrough($openingHour), + 'daysOfWeek' => $this->getDaysOfWeek($openingHour), + ]; + } + + private function getOpens(array $openingHour): string + { + return $openingHour['schema:opens']['@value'] ?? ''; + } + + private function getCloses(array $openingHour): string + { + return $openingHour['schema:closes']['@value'] ?? ''; + } + + private function getFrom(array $openingHour): ?\DateTimeImmutable + { + if (isset($openingHour['schema:validFrom']['@value'])) { + return new \DateTimeImmutable($openingHour['schema:validFrom']['@value']); + } + + return null; + } + + private function getThrough(array $openingHour): ?\DateTimeImmutable + { + if (isset($openingHour['schema:validThrough']['@value'])) { + return new \DateTimeImmutable($openingHour['schema:validThrough']['@value']); + } + + return null; + } + + private function getDaysOfWeek(array $openingHour): array + { + if (isset($openingHour['schema:dayOfWeek']['@value'])) { + return [$this->getDayOfWeekString($openingHour['schema:dayOfWeek']['@value'])]; + } + $daysOfWeek = array_map(function ($dayOfWeek) { + return $this->getDayOfWeekString($dayOfWeek['@value']); + }, $openingHour['schema:dayOfWeek'] ?? []); + + sort($daysOfWeek); + return $daysOfWeek; + } + + private function getDayOfWeekString(string $jsonLDValue): string + { + return str_replace( + 'schema:', + '', + $jsonLDValue + ); + } +} diff --git a/Classes/Domain/Import/Model/Entity.php b/Classes/Domain/Import/Model/Entity.php index 86a7b70..45b5584 100644 --- a/Classes/Domain/Import/Model/Entity.php +++ b/Classes/Domain/Import/Model/Entity.php @@ -29,6 +29,12 @@ interface Entity public function getTypo3DatabaseTableName(): string; + public function getTypo3SystemLanguageUid(): int; + + public function isForDefaultLanguage(): bool; + + public function isTranslation(): bool; + /** * Return full remote id as delivered by remote API. */ diff --git a/Classes/Domain/Import/Model/EntityCollection.php b/Classes/Domain/Import/Model/EntityCollection.php new file mode 100644 index 0000000..cb3eea3 --- /dev/null +++ b/Classes/Domain/Import/Model/EntityCollection.php @@ -0,0 +1,66 @@ + + * + * 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 EntityCollection +{ + /** + * @var Entity[] + */ + private array $entities = []; + + public function add(Entity $entity): void + { + $this->entities[] = $entity; + } + + /** + * @return Entity[] + */ + public function getEntities(): array + { + return $this->entities; + } + + public function getDefaultLanguageEntity(): ?Entity + { + foreach ($this->entities as $entity) { + if ($entity->isForDefaultLanguage()) { + return $entity; + } + } + + return null; + } + + /** + * @return Entity[] + */ + public function getTranslatedEntities(): array + { + return array_filter($this->entities, function (Entity $entity) { + return $entity->isTranslation(); + }); + } +} diff --git a/Classes/Domain/Import/Model/GenericEntity.php b/Classes/Domain/Import/Model/GenericEntity.php index 2383bdf..d90ef52 100644 --- a/Classes/Domain/Import/Model/GenericEntity.php +++ b/Classes/Domain/Import/Model/GenericEntity.php @@ -27,6 +27,7 @@ class GenericEntity implements Entity { private int $typo3StoragePid; private string $typo3DatabaseTableName; + private int $typo3SystemLanguageUid; private bool $created = false; private int $typo3Uid = 0; private string $remoteId; @@ -35,11 +36,13 @@ class GenericEntity implements Entity public function __construct( int $typo3StoragePid, string $typo3DatabaseTableName, + int $typo3SystemLanguageUid, string $remoteId, array $data ) { $this->typo3StoragePid = $typo3StoragePid; $this->typo3DatabaseTableName = $typo3DatabaseTableName; + $this->typo3SystemLanguageUid = $typo3SystemLanguageUid; $this->remoteId = $remoteId; $this->data = $data; } @@ -54,6 +57,21 @@ class GenericEntity implements Entity return $this->typo3DatabaseTableName; } + public function getTypo3SystemLanguageUid(): int + { + return $this->typo3SystemLanguageUid; + } + + public function isForDefaultLanguage(): bool + { + return $this->getTypo3SystemLanguageUid() === 0; + } + + public function isTranslation(): bool + { + return $this->getTypo3SystemLanguageUid() !== 0; + } + public function getRemoteId(): string { return $this->remoteId; diff --git a/Classes/Domain/Model/Backend/ImportLog.php b/Classes/Domain/Model/Backend/ImportLog.php index 4b47387..569239d 100644 --- a/Classes/Domain/Model/Backend/ImportLog.php +++ b/Classes/Domain/Model/Backend/ImportLog.php @@ -80,6 +80,7 @@ class ImportLog extends Typo3AbstractEntity foreach ($this->getEntries() as $entry) { if ($entry->hasErrors()) { $errors = array_merge($errors, $entry->getErrors()); + $errors = array_unique($errors); } } diff --git a/Classes/Domain/Model/Backend/ImportLogEntry.php b/Classes/Domain/Model/Backend/ImportLogEntry.php index afdda84..3d550cf 100644 --- a/Classes/Domain/Model/Backend/ImportLogEntry.php +++ b/Classes/Domain/Model/Backend/ImportLogEntry.php @@ -85,6 +85,7 @@ class ImportLogEntry extends Typo3AbstractEntity { if ($this->errorsAsArray === [] && $this->errors !== '') { $this->errorsAsArray = json_decode($this->errors, true); + $this->errorsAsArray = array_unique($this->errorsAsArray); } return $this->errorsAsArray; diff --git a/Classes/Domain/Model/Frontend/OpeningHour.php b/Classes/Domain/Model/Frontend/OpeningHour.php new file mode 100644 index 0000000..ecff698 --- /dev/null +++ b/Classes/Domain/Model/Frontend/OpeningHour.php @@ -0,0 +1,127 @@ + + * + * 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 OpeningHour +{ + private string $opens; + private string $closes; + private array $daysOfWeek; + private ?\DateTimeImmutable $from; + private ?\DateTimeImmutable $through; + + private function __construct( + string $opens, + string $closes, + array $daysOfWeek, + ?\DateTimeImmutable $from, + ?\DateTimeImmutable $through + ) { + $this->opens = $opens; + $this->closes = $closes; + $this->daysOfWeek = $daysOfWeek; + $this->from = $from; + $this->through = $through; + } + + public static function createFromArray(array $rawData): self + { + $from = null; + if (isset($rawData['from'])) { + $timeZone = new \DateTimeZone($rawData['from']['timezone']); + $from = new \DateTimeImmutable($rawData['from']['date'], $timeZone); + } + $through = null; + if (isset($rawData['through'])) { + $timeZone = new \DateTimeZone($rawData['through']['timezone']); + $through = new \DateTimeImmutable($rawData['through']['date'], $timeZone); + } + + return new self( + $rawData['opens'] ?? '', + $rawData['closes'] ?? '', + $rawData['daysOfWeek'] ?? '', + $from, + $through + ); + } + + public function getOpens(): string + { + return $this->opens; + } + + public function getCloses(): string + { + return $this->closes; + } + + public function getDaysOfWeek(): array + { + return $this->daysOfWeek; + } + + public function getDaysOfWeekWithMondayFirstWeekDay(): array + { + return $this->sortedDaysOfWeek([ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + ]); + } + + public function getFrom(): ?\DateTimeImmutable + { + return $this->from; + } + + public function getThrough(): ?\DateTimeImmutable + { + return $this->through; + } + + private function sortedDaysOfWeek(array $sorting): array + { + if ($this->daysOfWeek === []) { + return []; + } + + $days = []; + + foreach ($sorting as $weekDay) { + $position = array_search($weekDay, $this->daysOfWeek); + if ($position === false) { + continue; + } + + $days[] = $this->daysOfWeek[$position]; + } + + return $days; + } +} diff --git a/Classes/Domain/Model/Frontend/OpeningHours.php b/Classes/Domain/Model/Frontend/OpeningHours.php new file mode 100644 index 0000000..a70d476 --- /dev/null +++ b/Classes/Domain/Model/Frontend/OpeningHours.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 OpeningHours 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( + [OpeningHour::class, 'createFromArray'], + json_decode($serialized, true) + ); + } + + public function __toString(): string + { + return $this->serialized; + } + + public function current(): OpeningHour + { + 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/TouristAttraction.php b/Classes/Domain/Model/Frontend/TouristAttraction.php new file mode 100644 index 0000000..a69c8ee --- /dev/null +++ b/Classes/Domain/Model/Frontend/TouristAttraction.php @@ -0,0 +1,54 @@ + + * + * 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\Extbase\DomainObject\AbstractEntity; + +class TouristAttraction extends AbstractEntity +{ + protected string $title = ''; + protected string $description = ''; + protected ?OpeningHours $openingHours = null; + protected ?Town $town = null; + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getOpeningHours(): ?OpeningHours + { + return $this->openingHours; + } + + public function getTown(): ?Town + { + return $this->town; + } +} diff --git a/Classes/Domain/Model/Frontend/Town.php b/Classes/Domain/Model/Frontend/Town.php new file mode 100644 index 0000000..4bdd44e --- /dev/null +++ b/Classes/Domain/Model/Frontend/Town.php @@ -0,0 +1,42 @@ + + * + * 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\Extbase\DomainObject\AbstractEntity; + +class Town extends AbstractEntity +{ + protected string $title = ''; + protected string $description = ''; + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } +} diff --git a/Classes/Extension.php b/Classes/Extension.php index c717285..293a135 100644 --- a/Classes/Extension.php +++ b/Classes/Extension.php @@ -23,6 +23,7 @@ namespace WerkraumMedia\ThueCat; * 02110-1301, USA. */ +use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Extbase\Utility\ExtensionUtility; use WerkraumMedia\ThueCat\Controller\Backend\ImportController; use WerkraumMedia\ThueCat\Controller\Backend\OverviewController; @@ -33,6 +34,8 @@ class Extension public const EXTENSION_NAME = 'Thuecat'; + public const TT_CONTENT_GROUP = 'thuecat'; + public static function getLanguagePath(): string { return 'LLL:EXT:' . self::EXTENSION_KEY . '/Resources/Private/Language/'; @@ -56,4 +59,26 @@ class Extension ] ); } + + public static function registerConfig(): void + { + $languagePath = self::getLanguagePath() . 'locallang_tca.xlf:tt_content'; + + // TODO: Add Icon + ExtensionManagementUtility::addPageTSConfig(' + mod.wizards.newContentElement.wizardItems.thuecat { + header = ' . $languagePath . '.group + show = * + elements { + thuecat_tourist_attraction{ + title = ' . $languagePath . '.thuecat_tourist_attraction + description = ' . $languagePath . '.thuecat_tourist_attraction.description + tt_content_defValues { + CType = thuecat_tourist_attraction + } + } + } + } + '); + } } diff --git a/Classes/Frontend/DataProcessing/ResolveEntities.php b/Classes/Frontend/DataProcessing/ResolveEntities.php new file mode 100644 index 0000000..47dee3b --- /dev/null +++ b/Classes/Frontend/DataProcessing/ResolveEntities.php @@ -0,0 +1,85 @@ + + * + * 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\Database\Connection; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; +use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; + +class ResolveEntities implements DataProcessorInterface +{ + private ConnectionPool $connectionPool; + private DataMapper $dataMapper; + + public function __construct( + ConnectionPool $connectionPool, + DataMapper $dataMapper + ) { + $this->connectionPool = $connectionPool; + $this->dataMapper = $dataMapper; + } + + public function process( + ContentObjectRenderer $cObj, + array $contentObjectConfiguration, + array $processorConfiguration, + array $processedData + ) { + $as = $cObj->stdWrapValue('as', $processorConfiguration, 'entities'); + $table = $cObj->stdWrapValue('table', $processorConfiguration, ''); + $uids = $cObj->stdWrapValue('uids', $processorConfiguration, ''); + + $uids = GeneralUtility::intExplode(',', $uids); + if ($uids === [] || $table === '') { + return $processedData; + } + + $processedData[$as] = $this->resolveEntities($table, $uids); + return $processedData; + } + + private function resolveEntities(string $table, array $uids): array + { + $targetType = '\WerkraumMedia\ThueCat\Domain\Model\Frontend\\' . $this->convertTableToEntity($table); + + $queryBuilder = $this->connectionPool->getQueryBuilderForTable($table); + $queryBuilder->select('*'); + $queryBuilder->from($table); + $queryBuilder->where($queryBuilder->expr()->in( + 'uid', + $queryBuilder->createNamedParameter($uids, Connection::PARAM_INT_ARRAY) + )); + + return $this->dataMapper->map($targetType, $queryBuilder->execute()->fetchAll()); + } + + private function convertTableToEntity(string $table): string + { + $entityPart = str_replace('tx_thuecat_', '', $table); + return GeneralUtility::underscoredToUpperCamelCase($entityPart); + } +} diff --git a/Configuration/Extbase/Persistence/Classes.php b/Configuration/Extbase/Persistence/Classes.php index 280fbe1..8cd3d03 100644 --- a/Configuration/Extbase/Persistence/Classes.php +++ b/Configuration/Extbase/Persistence/Classes.php @@ -19,4 +19,10 @@ return [ \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry::class => [ 'tableName' => 'tx_thuecat_import_log_entry', ], + \WerkraumMedia\ThueCat\Domain\Model\Frontend\TouristAttraction::class => [ + 'tableName' => 'tx_thuecat_tourist_attraction', + ], + \WerkraumMedia\ThueCat\Domain\Model\Frontend\Town::class => [ + 'tableName' => 'tx_thuecat_town', + ], ]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 7820594..0f313d9 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -10,3 +10,7 @@ services: WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData: arguments: $requestFactory: '@WerkraumMedia\ThueCat\Domain\Import\RequestFactory' + + WerkraumMedia\ThueCat\Frontend\DataProcessing\: + resource: '../Classes/Frontend/DataProcessing/*' + public: true diff --git a/Configuration/SiteConfiguration/Overrides/sites.php b/Configuration/SiteConfiguration/Overrides/sites.php index 7b392ff..2c644b4 100644 --- a/Configuration/SiteConfiguration/Overrides/sites.php +++ b/Configuration/SiteConfiguration/Overrides/sites.php @@ -3,7 +3,7 @@ defined('TYPO3') or die(); (static function (string $extensionKey, string $tableName) { - $languagePath = 'LLL:EXT:' . $extensionKey . '/Resources/Private/Language/locallang_be.xlf:' . $tableName . '.'; + $languagePath = \WerkraumMedia\ThueCat\Extension::getLanguagePath() . 'locallang_be.xlf:' . $tableName . '.'; \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['SiteConfiguration']['site'], [ 'columns' => [ diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/sys_template.php new file mode 100644 index 0000000..44ca9d7 --- /dev/null +++ b/Configuration/TCA/Overrides/sys_template.php @@ -0,0 +1,14 @@ + [ + // TODO: Add Icon + // 'typeicon_classes' => [ + // $cType => '', + // ], + ], + 'types' => [ + $cType => [ + 'showitem' => + '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,' + . '--palette--;;general,' + . '--palette--;;headers,' + . 'records,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,' + . '--palette--;;frames,' + . '--palette--;;appearanceLinks,' + . '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,' + . '--palette--;;language,' + . '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,' + . '--palette--;;hidden,' + . '--palette--;;access,' + . '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,' + . '--div--;LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_category.tabs.category,' + . 'categories,' + . '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes,' + . 'rowDescription,' + . '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended', + 'columnsOverrides' => [ + 'records' => [ + 'config' => [ + 'allowed' => 'tx_thuecat_tourist_attraction', + ], + ], + ], + ], + ], + ]); + + \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem( + $tableName, + 'CType', + [ + $languagePath, + $cType, + // TODO: Add Icon + '', + \WerkraumMedia\ThueCat\Extension::TT_CONTENT_GROUP, + ] + ); +})( + \WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, + 'tt_content', + 'thuecat_tourist_attraction' +); diff --git a/Configuration/TCA/tx_thuecat_tourist_attraction.php b/Configuration/TCA/tx_thuecat_tourist_attraction.php new file mode 100644 index 0000000..acfa0c7 --- /dev/null +++ b/Configuration/TCA/tx_thuecat_tourist_attraction.php @@ -0,0 +1,89 @@ + [ + 'label' => 'title', + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'searchFields' => 'title, description', + ], + 'columns' => [ + 'title' => [ + 'label' => $languagePath . '.title', + 'config' => [ + 'type' => 'input', + 'size' => 20, + 'max' => 255, + 'readOnly' => true, + ], + ], + 'description' => [ + 'label' => $languagePath . '.description', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'opening_hours' => [ + 'label' => $languagePath . '.opening_hours', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'remote_id' => [ + 'label' => $languagePath . '.remote_id', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'town' => [ + 'label' => $languagePath . '.town', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_town', + 'items' => [ + [ + $languagePath . '.town.unkown', + 0, + ], + ], + 'readOnly' => true, + ], + ], + 'managed_by' => [ + 'label' => $languagePath . '.managed_by', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_organisation', + 'items' => [ + [ + $languagePath . '.managed_by.unkown', + 0, + ], + ], + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'title, description, opening_hours, remote_id, town, managed_by', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_tourist_attraction'); diff --git a/Configuration/TypoScript/Rendering.typoscript b/Configuration/TypoScript/Rendering.typoscript new file mode 100644 index 0000000..bcc5032 --- /dev/null +++ b/Configuration/TypoScript/Rendering.typoscript @@ -0,0 +1,6 @@ +lib.thuecatContentElement =< lib.contentElement +lib.thuecatContentElement { + templateRootPaths { + 9999 = EXT:thuecat/Resources/Private/Templates/Frontend/ContentElement/ + } +} diff --git a/Configuration/TypoScript/Rendering/TouristAttraction.typoscript b/Configuration/TypoScript/Rendering/TouristAttraction.typoscript new file mode 100644 index 0000000..67be9f9 --- /dev/null +++ b/Configuration/TypoScript/Rendering/TouristAttraction.typoscript @@ -0,0 +1,13 @@ +tt_content { + thuecat_tourist_attraction =< lib.thuecatContentElement + thuecat_tourist_attraction { + templateName = TouristAttraction + dataProcessing { + 10 = WerkraumMedia\ThueCat\Frontend\DataProcessing\ResolveEntities + 10 { + table = tx_thuecat_tourist_attraction + uids.data = field:records + } + } + } +} diff --git a/Configuration/TypoScript/Rendering/_base.typoscript b/Configuration/TypoScript/Rendering/_base.typoscript new file mode 100644 index 0000000..bcc5032 --- /dev/null +++ b/Configuration/TypoScript/Rendering/_base.typoscript @@ -0,0 +1,6 @@ +lib.thuecatContentElement =< lib.contentElement +lib.thuecatContentElement { + templateRootPaths { + 9999 = EXT:thuecat/Resources/Private/Templates/Frontend/ContentElement/ + } +} diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript new file mode 100644 index 0000000..beee004 --- /dev/null +++ b/Configuration/TypoScript/setup.typoscript @@ -0,0 +1 @@ +@import 'EXT:thuecat/Configuration/TypoScript/Rendering/*.typoscript' diff --git a/README.md b/README.md index 9b133cb..15ce77e 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ The extension already allows: * Tourist information + * Tourist attraction + * Backend module: * To inspect current existing organisations @@ -36,8 +38,6 @@ The extension already allows: * Integrate proper icons -* Import of tourist attraction - * Content element to display tourist attraction, town, tourist information and organisation. diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 6b2e759..7882d95 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -90,6 +90,9 @@ Tourist Information + + Tourist Attraction + Import finished diff --git a/Resources/Private/Language/locallang_tca.xlf b/Resources/Private/Language/locallang_tca.xlf index 4445cf7..3c66576 100644 --- a/Resources/Private/Language/locallang_tca.xlf +++ b/Resources/Private/Language/locallang_tca.xlf @@ -72,6 +72,31 @@ Unkown + + Tourist Attraction + + + Title + + + Description + + + Remote ID + + + Town + + + Unkown + + + Managed by + + + Unkown + + Import Configuration @@ -128,6 +153,16 @@ Created + + + ThüCAT + + + Tourist Attraction + + + Renders selected tourist attractions + diff --git a/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html b/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html new file mode 100644 index 0000000..e648a00 --- /dev/null +++ b/Resources/Private/Templates/Frontend/ContentElement/TouristAttraction.html @@ -0,0 +1,21 @@ + + +
+
+
{entity.title} ({entity.town.title})
+

{entity.description}

+ + +

+ + {weekday}: {openingHour.opens} - {openingHour.closes}
+
+ {openingHour.from -> f:format.date(format: 'd.m.Y')} - + {openingHour.through -> f:format.date(format: 'd.m.Y')} +

+
+
+
+
+ diff --git a/Tests/Unit/Domain/Import/Converter/OrganisationTest.php b/Tests/Unit/Domain/Import/Converter/OrganisationTest.php index 10c309c..200d8c4 100644 --- a/Tests/Unit/Domain/Import/Converter/OrganisationTest.php +++ b/Tests/Unit/Domain/Import/Converter/OrganisationTest.php @@ -24,21 +24,28 @@ namespace WerkraumMedia\ThueCat\Tests\Unit\Domain\Import\Converter; */ use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; use WerkraumMedia\ThueCat\Domain\Import\Converter\Organisation; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; /** * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\Organisation + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity */ class OrganisationTest extends TestCase { + use ProphecyTrait; + /** * @test */ public function instanceCanBeCreated(): void { - $subject = new Organisation(); + $parser = $this->prophesize(Parser::class); + $subject = new Organisation($parser->reveal()); self::assertInstanceOf(Organisation::class, $subject); } @@ -47,7 +54,8 @@ class OrganisationTest extends TestCase */ public function isInstanceOfConverter(): void { - $subject = new Organisation(); + $parser = $this->prophesize(Parser::class); + $subject = new Organisation($parser->reveal()); self::assertInstanceOf(Converter::class, $subject); } @@ -56,7 +64,8 @@ class OrganisationTest extends TestCase */ public function canConvertTouristMarketingCompany(): void { - $subject = new Organisation(); + $parser = $this->prophesize(Parser::class); + $subject = new Organisation($parser->reveal()); self::assertTrue($subject->canConvert([ 'thuecat:TouristMarketingCompany', 'schema:Thing', @@ -69,8 +78,7 @@ class OrganisationTest extends TestCase */ public function convertsJsonIdToGenericEntity(): void { - $subject = new Organisation(); - $entity = $subject->convert([ + $jsonLD = [ '@id' => 'https://example.com/resources/018132452787-ngbe', 'schema:name' => [ '@value' => 'Title', @@ -78,8 +86,20 @@ class OrganisationTest extends TestCase 'schema:description' => [ '@value' => 'Description', ], - ]); + ]; + $parser = $this->prophesize(Parser::class); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD)->willReturn('Title'); + $parser->getDescription($jsonLD)->willReturn('Description'); + + $subject = new Organisation($parser->reveal()); + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; self::assertSame(10, $entity->getTypo3StoragePid()); self::assertSame('tx_thuecat_organisation', $entity->getTypo3DatabaseTableName()); self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); diff --git a/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php b/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php new file mode 100644 index 0000000..29748c1 --- /dev/null +++ b/Tests/Unit/Domain/Import/Converter/TouristAttractionTest.php @@ -0,0 +1,224 @@ + + * + * 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\Converter\TouristAttraction; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Town; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\TouristAttraction + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity + */ +class TouristAttractionTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $parser = $this->prophesize(Parser::class); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristAttraction( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + + self::assertInstanceOf(TouristAttraction::class, $subject); + } + + /** + * @test + */ + public function canConvert(): void + { + $parser = $this->prophesize(Parser::class); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristAttraction( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + + self::assertTrue($subject->canConvert(['schema:TouristAttraction'])); + } + + /** + * @test + */ + public function convertsWithoutRelations(): void + { + $jsonLD = [ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:containedInPlace' => [ + [ + '@id' => 'https://example.com/resources/043064193523-jcyt', + ], + [ + '@id' => 'https://example.com/resources/573211638937-gmqb', + ], + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + [ + '@value' => 'Description', + ], + ], + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getContainedInPlaceIds($jsonLD)->willReturn([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ]); + $parser->getLanguages($jsonLD)->willReturn(['de']); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD, 'de')->willReturn('Title'); + $parser->getDescription($jsonLD, 'de')->willReturn('Description'); + $parser->getOpeningHours($jsonLD)->willReturn([]); + + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristAttraction( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; + self::assertSame(10, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_tourist_attraction', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 0, + 'town' => 0, + 'opening_hours' => '[]', + ], $entity->getData()); + } + + /** + * @test + */ + public function convertsWithRelations(): void + { + $jsonLD = [ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:containedInPlace' => [ + [ + '@id' => 'https://example.com/resources/043064193523-jcyt', + ], + [ + '@id' => 'https://example.com/resources/573211638937-gmqb', + ], + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + [ + '@value' => 'Description', + ], + ], + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getContainedInPlaceIds($jsonLD)->willReturn([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ]); + $parser->getLanguages($jsonLD)->willReturn(['de']); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD, 'de')->willReturn('Title'); + $parser->getDescription($jsonLD, 'de')->willReturn('Description'); + $parser->getOpeningHours($jsonLD)->willReturn([]); + + $organisation = $this->prophesize(Organisation::class); + $organisation->getUid()->willReturn(10); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn($organisation->reveal()); + + $town = $this->prophesize(Town::class); + $town->getUid()->willReturn(20); + $townRepository = $this->prophesize(TownRepository::class); + $townRepository->findOneByRemoteIds([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ])->willReturn($town->reveal()); + + $subject = new TouristAttraction( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; + self::assertSame(10, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_tourist_attraction', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 10, + 'town' => 20, + 'opening_hours' => '[]', + ], $entity->getData()); + } +} diff --git a/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php b/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php index 5e7d5ea..7b536f3 100644 --- a/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php +++ b/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php @@ -27,6 +27,8 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; use WerkraumMedia\ThueCat\Domain\Import\Converter\TouristInformation; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; use WerkraumMedia\ThueCat\Domain\Model\Backend\Town; use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; @@ -34,6 +36,7 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; /** * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\TouristInformation + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity */ class TouristInformationTest extends TestCase @@ -45,10 +48,12 @@ class TouristInformationTest extends TestCase */ public function instanceCanBeCreated(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); $townRepository = $this->prophesize(TownRepository::class); $subject = new TouristInformation( + $parser->reveal(), $organisationRepository->reveal(), $townRepository->reveal() ); @@ -60,10 +65,12 @@ class TouristInformationTest extends TestCase */ public function isInstanceOfConverter(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); $townRepository = $this->prophesize(TownRepository::class); $subject = new TouristInformation( + $parser->reveal(), $organisationRepository->reveal(), $townRepository->reveal() ); @@ -75,10 +82,12 @@ class TouristInformationTest extends TestCase */ public function canConvertTouristMarketingCompany(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); $townRepository = $this->prophesize(TownRepository::class); $subject = new TouristInformation( + $parser->reveal(), $organisationRepository->reveal(), $townRepository->reveal() ); @@ -92,21 +101,7 @@ class TouristInformationTest extends TestCase */ public function convertsJsonIdToGenericEntityWithoutRelations(): void { - $organisationRepository = $this->prophesize(OrganisationRepository::class); - $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') - ->willReturn(null); - - $townRepository = $this->prophesize(TownRepository::class); - $townRepository->findOneByRemoteIds([ - 'https://example.com/resources/043064193523-jcyt', - 'https://example.com/resources/573211638937-gmqb', - ])->willReturn(null); - - $subject = new TouristInformation( - $organisationRepository->reveal(), - $townRepository->reveal() - ); - $entity = $subject->convert([ + $jsonLD = [ '@id' => 'https://example.com/resources/018132452787-ngbe', 'thuecat:managedBy' => [ '@id' => 'https://example.com/resources/018132452787-xxxx', @@ -127,7 +122,39 @@ class TouristInformationTest extends TestCase '@value' => 'Description', ], ], + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getContainedInPlaceIds($jsonLD)->willReturn([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', ]); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD)->willReturn('Title'); + $parser->getDescription($jsonLD)->willReturn('Description'); + + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') + ->willReturn(null); + + $townRepository = $this->prophesize(TownRepository::class); + $townRepository->findOneByRemoteIds([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ])->willReturn(null); + + $subject = new TouristInformation( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; self::assertSame(10, $entity->getTypo3StoragePid()); self::assertSame('tx_thuecat_tourist_information', $entity->getTypo3DatabaseTableName()); @@ -145,25 +172,7 @@ class TouristInformationTest extends TestCase */ public function convertsJsonIdToGenericEntityWithRelations(): void { - $organisation = $this->prophesize(Organisation::class); - $organisation->getUid()->willReturn(10); - $organisationRepository = $this->prophesize(OrganisationRepository::class); - $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') - ->willReturn($organisation->reveal()); - - $town = $this->prophesize(Town::class); - $town->getUid()->willReturn(20); - $townRepository = $this->prophesize(TownRepository::class); - $townRepository->findOneByRemoteIds([ - 'https://example.com/resources/043064193523-jcyt', - 'https://example.com/resources/573211638937-gmqb', - ])->willReturn($town->reveal()); - - $subject = new TouristInformation( - $organisationRepository->reveal(), - $townRepository->reveal() - ); - $entity = $subject->convert([ + $jsonLD = [ '@id' => 'https://example.com/resources/018132452787-ngbe', 'thuecat:managedBy' => [ '@id' => 'https://example.com/resources/018132452787-xxxx', @@ -184,7 +193,43 @@ class TouristInformationTest extends TestCase '@value' => 'Description', ], ], + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getContainedInPlaceIds($jsonLD)->willReturn([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', ]); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD)->willReturn('Title'); + $parser->getDescription($jsonLD)->willReturn('Description'); + + $organisation = $this->prophesize(Organisation::class); + $organisation->getUid()->willReturn(10); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') + ->willReturn($organisation->reveal()); + + $town = $this->prophesize(Town::class); + $town->getUid()->willReturn(20); + $townRepository = $this->prophesize(TownRepository::class); + $townRepository->findOneByRemoteIds([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ])->willReturn($town->reveal()); + + $subject = new TouristInformation( + $parser->reveal(), + $organisationRepository->reveal(), + $townRepository->reveal() + ); + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; self::assertSame(10, $entity->getTypo3StoragePid()); self::assertSame('tx_thuecat_tourist_information', $entity->getTypo3DatabaseTableName()); diff --git a/Tests/Unit/Domain/Import/Converter/TownTest.php b/Tests/Unit/Domain/Import/Converter/TownTest.php index b19e8cf..a3612ad 100644 --- a/Tests/Unit/Domain/Import/Converter/TownTest.php +++ b/Tests/Unit/Domain/Import/Converter/TownTest.php @@ -27,11 +27,14 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; use WerkraumMedia\ThueCat\Domain\Import\Converter\Town; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; /** * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\Town + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity */ class TownTest extends TestCase @@ -43,9 +46,13 @@ class TownTest extends TestCase */ public function instanceCanBeCreated(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); - $subject = new Town($organisationRepository->reveal()); + $subject = new Town( + $parser->reveal(), + $organisationRepository->reveal() + ); self::assertInstanceOf(Town::class, $subject); } @@ -54,9 +61,13 @@ class TownTest extends TestCase */ public function isInstanceOfConverter(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); - $subject = new Town($organisationRepository->reveal()); + $subject = new Town( + $parser->reveal(), + $organisationRepository->reveal() + ); self::assertInstanceOf(Converter::class, $subject); } @@ -65,9 +76,13 @@ class TownTest extends TestCase */ public function canConvertTouristMarketingCompany(): void { + $parser = $this->prophesize(Parser::class); $organisationRepository = $this->prophesize(OrganisationRepository::class); - $subject = new Town($organisationRepository->reveal()); + $subject = new Town( + $parser->reveal(), + $organisationRepository->reveal() + ); self::assertTrue($subject->canConvert([ 'thuecat:Town', ])); @@ -78,11 +93,7 @@ class TownTest extends TestCase */ public function convertsJsonIdToGenericEntityWithoutOrganisation(): void { - $organisationRepository = $this->prophesize(OrganisationRepository::class); - $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn(null); - - $subject = new Town($organisationRepository->reveal()); - $entity = $subject->convert([ + $jsonLD = [ '@id' => 'https://example.com/resources/018132452787-ngbe', 'thuecat:managedBy' => [ '@id' => 'https://example.com/resources/018132452787-xxxx', @@ -93,7 +104,27 @@ class TownTest extends TestCase 'schema:description' => [ '@value' => 'Description', ], - ]); + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD)->willReturn('Title'); + $parser->getDescription($jsonLD)->willReturn('Description'); + + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn(null); + + $subject = new Town( + $parser->reveal(), + $organisationRepository->reveal() + ); + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; self::assertSame(10, $entity->getTypo3StoragePid()); self::assertSame('tx_thuecat_town', $entity->getTypo3DatabaseTableName()); @@ -110,13 +141,7 @@ class TownTest extends TestCase */ public function convertsJsonIdToGenericEntityWithOrganisation(): void { - $organisation = $this->prophesize(Organisation::class); - $organisation->getUid()->willReturn(10); - $organisationRepository = $this->prophesize(OrganisationRepository::class); - $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn($organisation->reveal()); - - $subject = new Town($organisationRepository->reveal()); - $entity = $subject->convert([ + $jsonLD = [ '@id' => 'https://example.com/resources/018132452787-ngbe', 'thuecat:managedBy' => [ '@id' => 'https://example.com/resources/018132452787-xxxx', @@ -127,7 +152,29 @@ class TownTest extends TestCase 'schema:description' => [ '@value' => 'Description', ], - ]); + ]; + + $parser = $this->prophesize(Parser::class); + $parser->getManagerId($jsonLD)->willReturn('https://example.com/resources/018132452787-xxxx'); + $parser->getId($jsonLD)->willReturn('https://example.com/resources/018132452787-ngbe'); + $parser->getTitle($jsonLD)->willReturn('Title'); + $parser->getDescription($jsonLD)->willReturn('Description'); + + $organisation = $this->prophesize(Organisation::class); + $organisation->getUid()->willReturn(10); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn($organisation->reveal()); + + $subject = new Town( + $parser->reveal(), + $organisationRepository->reveal() + ); + $entities = $subject->convert($jsonLD); + + self::assertInstanceOf(EntityCollection::class, $entities); + self::assertCount(1, $entities->getEntities()); + + $entity = $entities->getEntities()[0]; self::assertSame(10, $entity->getTypo3StoragePid()); self::assertSame('tx_thuecat_town', $entity->getTypo3DatabaseTableName()); diff --git a/Tests/Unit/Domain/Import/ImporterTest.php b/Tests/Unit/Domain/Import/ImporterTest.php index 31a0fca..d0ad6bb 100644 --- a/Tests/Unit/Domain/Import/ImporterTest.php +++ b/Tests/Unit/Domain/Import/ImporterTest.php @@ -31,7 +31,7 @@ use WerkraumMedia\ThueCat\Domain\Import\Converter\Registry as ConverterRegistry; use WerkraumMedia\ThueCat\Domain\Import\Importer; use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData; use WerkraumMedia\ThueCat\Domain\Import\Importer\SaveData; -use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry as UrlProviderRegistry; use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\UrlProvider; use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; @@ -67,6 +67,35 @@ class ImporterTest extends TestCase self::assertInstanceOf(Importer::class, $subject); } + /** + * @test + */ + public function importsNothingIfUrlProviderCouldNotBeResolved(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $converter = $this->prophesize(ConverterRegistry::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + $configuration = $this->prophesize(ImportConfiguration::class); + + $urls->getProviderForConfiguration($configuration->reveal())->willReturn(null); + $fetchData->jsonLDFromUrl()->shouldNotBeCalled(); + $saveData->import()->shouldNotBeCalled(); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + $result = $subject->importConfiguration($configuration->reveal()); + + self::assertInstanceOf(ImportLog::class, $result); + self::assertCount(0, $result->getEntries()); + } + /** * @test */ @@ -109,8 +138,8 @@ class ImporterTest extends TestCase $saveData = $this->prophesize(SaveData::class); $configuration = $this->prophesize(ImportConfiguration::class); - $entity1 = $this->prophesize(Entity::class); - $entity2 = $this->prophesize(Entity::class); + $entities1 = $this->prophesize(EntityCollection::class); + $entities2 = $this->prophesize(EntityCollection::class); $urls->getProviderForConfiguration($configuration->reveal())->willReturn($urlProvider->reveal()); $urlProvider->getUrls()->willReturn([ @@ -144,13 +173,13 @@ class ImporterTest extends TestCase $concreteConverter->convert(Argument::that(function (array $jsonEntity) { return $jsonEntity['@id'] === 'https://example.com/resources/34343-ex'; - }))->willReturn($entity1->reveal()); + }))->willReturn($entities1->reveal()); $concreteConverter->convert(Argument::that(function (array $jsonEntity) { return $jsonEntity['@id'] === 'https://example.com/resources/34344-es'; - }))->willReturn($entity2->reveal()); + }))->willReturn($entities2->reveal()); - $saveData->import($entity1->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); - $saveData->import($entity2->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); + $saveData->import($entities1->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); + $saveData->import($entities2->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); $subject = new Importer( $urls->reveal(), diff --git a/Tests/Unit/Domain/Import/JsonLD/Parser/OpeningHoursTest.php b/Tests/Unit/Domain/Import/JsonLD/Parser/OpeningHoursTest.php new file mode 100644 index 0000000..0017ad6 --- /dev/null +++ b/Tests/Unit/Domain/Import/JsonLD/Parser/OpeningHoursTest.php @@ -0,0 +1,310 @@ + + * + * 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\OpeningHours; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours + */ +class OpeningHoursTest extends TestCase +{ + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new OpeningHours(); + + self::assertInstanceOf(OpeningHours::class, $subject); + } + + /** + * @test + */ + public function returnsEmptyArrayIfOpeningHoursAreMissing(): void + { + $subject = new OpeningHours(); + + $result = $subject->get([ + ]); + + self::assertSame([], $result); + } + + /** + * @test + */ + public function returnsSingleOpeningHourWrappedInArray(): void + { + $subject = new OpeningHours(); + + $result = $subject->get([ + 'schema:openingHoursSpecification' => [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + 'schema:opens' => [ + '@type' => 'schema:Time', + '@value' => '10:00:00', + ], + 'schema:closes' => [ + '@type' => 'schema:Time', + '@value' => '18:00:00', + ], + 'schema:validFrom' => [ + '@type' => 'schema:Date', + '@value' => '2021-03-01', + ], + 'schema:validThrough' => [ + '@type' => 'schema:Date', + '@value' => '2021-12-31', + ], + 'schema:dayOfWeek' => [ + 0 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Saturday', + ], + 1 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Sunday', + ], + 2 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Friday', + ], + 3 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Thursday', + ], + 4 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Tuesday', + ], + 5 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Wednesday', + ], + ], + ], + ]); + + self::assertCount(1, $result); + self::assertSame('10:00:00', $result[0]['opens']); + self::assertSame('18:00:00', $result[0]['closes']); + self::assertSame([ + 'Friday', + 'Saturday', + 'Sunday', + 'Thursday', + 'Tuesday', + 'Wednesday', + ], $result[0]['daysOfWeek']); + self::assertInstanceOf(\DateTimeImmutable::class, $result[0]['from']); + self::assertSame('2021-03-01 00:00:00', $result[0]['from']->format('Y-m-d H:i:s')); + + self::assertInstanceOf(\DateTimeImmutable::class, $result[0]['through']); + self::assertSame('2021-12-31 00:00:00', $result[0]['through']->format('Y-m-d H:i:s')); + } + + /** + * @test + */ + public function returnsSingleWeekDay(): void + { + $subject = new OpeningHours(); + + $result = $subject->get([ + 'schema:openingHoursSpecification' => [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + 'schema:dayOfWeek' => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Saturday', + ], + ], + ]); + + self::assertCount(1, $result); + self::assertSame([ + 'Saturday', + ], $result[0]['daysOfWeek']); + } + + /** + * @test + */ + public function returnsMultipleOpeningHours(): void + { + $subject = new OpeningHours(); + + $result = $subject->get([ + 'schema:openingHoursSpecification' => [ + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + 'schema:opens' => [ + '@type' => 'schema:Time', + '@value' => '10:00:00', + ], + 'schema:closes' => [ + '@type' => 'schema:Time', + '@value' => '18:00:00', + ], + 'schema:validFrom' => [ + '@type' => 'schema:Date', + '@value' => '2021-03-01', + ], + 'schema:validThrough' => [ + '@type' => 'schema:Date', + '@value' => '2021-12-31', + ], + 'schema:dayOfWeek' => [ + 0 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Saturday', + ], + 1 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Sunday', + ], + 2 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Friday', + ], + 3 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Thursday', + ], + 4 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Tuesday', + ], + 5 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Wednesday', + ], + ], + ], + [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + 'schema:opens' => [ + '@type' => 'schema:Time', + '@value' => '09:00:00', + ], + 'schema:closes' => [ + '@type' => 'schema:Time', + '@value' => '17:00:00', + ], + 'schema:validFrom' => [ + '@type' => 'schema:Date', + '@value' => '2022-03-01', + ], + 'schema:validThrough' => [ + '@type' => 'schema:Date', + '@value' => '2022-12-31', + ], + 'schema:dayOfWeek' => [ + 0 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Saturday', + ], + 1 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Sunday', + ], + 2 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Friday', + ], + 3 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Thursday', + ], + 4 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Tuesday', + ], + 5 => [ + '@type' => 'schema:DayOfWeek', + '@value' => 'schema:Wednesday', + ], + ], + ], + ], + ]); + + self::assertCount(2, $result); + + self::assertSame('10:00:00', $result[0]['opens']); + self::assertSame('18:00:00', $result[0]['closes']); + self::assertSame([ + 'Friday', + 'Saturday', + 'Sunday', + 'Thursday', + 'Tuesday', + 'Wednesday', + ], $result[0]['daysOfWeek']); + self::assertInstanceOf(\DateTimeImmutable::class, $result[0]['from']); + self::assertSame('2021-03-01 00:00:00', $result[0]['from']->format('Y-m-d H:i:s')); + + self::assertInstanceOf(\DateTimeImmutable::class, $result[0]['through']); + self::assertSame('2021-12-31 00:00:00', $result[0]['through']->format('Y-m-d H:i:s')); + + self::assertSame('09:00:00', $result[1]['opens']); + self::assertSame('17:00:00', $result[1]['closes']); + self::assertSame([ + 'Friday', + 'Saturday', + 'Sunday', + 'Thursday', + 'Tuesday', + 'Wednesday', + ], $result[1]['daysOfWeek']); + self::assertInstanceOf(\DateTimeImmutable::class, $result[1]['from']); + self::assertSame('2022-03-01 00:00:00', $result[1]['from']->format('Y-m-d H:i:s')); + + self::assertInstanceOf(\DateTimeImmutable::class, $result[1]['through']); + self::assertSame('2022-12-31 00:00:00', $result[1]['through']->format('Y-m-d H:i:s')); + } + + /** + * @test + */ + public function returnsProperDefaultsOnMissingValues(): void + { + $subject = new OpeningHours(); + + $result = $subject->get([ + 'schema:openingHoursSpecification' => [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + ], + ]); + + self::assertCount(1, $result); + self::assertSame('', $result[0]['opens']); + self::assertSame('', $result[0]['closes']); + self::assertSame([], $result[0]['daysOfWeek']); + self::assertSame(null, $result[0]['from']); + self::assertSame(null, $result[0]['through']); + } +} diff --git a/Tests/Unit/Domain/Import/JsonLD/ParserTest.php b/Tests/Unit/Domain/Import/JsonLD/ParserTest.php new file mode 100644 index 0000000..d3d7d32 --- /dev/null +++ b/Tests/Unit/Domain/Import/JsonLD/ParserTest.php @@ -0,0 +1,457 @@ + + * + * 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; +use WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser\OpeningHours; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\JsonLD\Parser + */ +class ParserTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + + self::assertInstanceOf(Parser::class, $subject); + } + + /** + * @test + */ + public function returnsId(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + $result = $subject->getId([ + '@id' => 'https://example.com/resources/165868194223-id', + ]); + + self::assertSame('https://example.com/resources/165868194223-id', $result); + } + + /** + * @test + */ + public function returnsManagerId(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + $result = $subject->getManagerId([ + 'thuecat:contentResponsible' => [ + '@id' => 'https://example.com/resources/165868194223-manager', + ], + ]); + + 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); + $subject = new Parser( + $openingHours->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); + $subject = new Parser( + $openingHours->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 + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + $result = $subject->getContainedInPlaceIds([ + 'schema:containedInPlace' => [ + ['@id' => 'https://thuecat.org/resources/043064193523-jcyt'], + ['@id' => 'https://thuecat.org/resources/349986440346-kbkf'], + ['@id' => 'https://thuecat.org/resources/794900260253-wjab'], + ['@id' => 'https://thuecat.org/resources/476888881990-xpwq'], + ['@id' => 'https://thuecat.org/resources/573211638937-gmqb'], + ], + ]); + self::assertSame([ + 'https://thuecat.org/resources/043064193523-jcyt', + 'https://thuecat.org/resources/349986440346-kbkf', + 'https://thuecat.org/resources/794900260253-wjab', + 'https://thuecat.org/resources/476888881990-xpwq', + 'https://thuecat.org/resources/573211638937-gmqb', + ], $result); + } + + /** + * @test + */ + public function returnsLanguages(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + + $result = $subject->getLanguages([ + 'schema:availableLanguage' => [ + 0 => [ + '@type' => 'thuecat:Language', + '@value' => 'thuecat:German', + ], + 1 => [ + '@type' => 'thuecat:Language', + '@value' => 'thuecat:English', + ], + 2 => [ + '@type' => 'thuecat:Language', + '@value' => 'thuecat:French', + ], + ], + ]); + + self::assertSame([ + 'de', + 'en', + 'fr', + ], $result); + } + + /** + * @test + */ + public function throwsExceptionOnUnkownLanguage(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + + $this->expectExceptionCode(1612367481); + $result = $subject->getLanguages([ + 'schema:availableLanguage' => [ + 0 => [ + '@type' => 'thuecat:Language', + '@value' => 'thuecat:Unkown', + ], + ], + ]); + } + + /** + * @test + */ + public function returnsNoLanguagesIfInfoIsMissing(): void + { + $openingHours = $this->prophesize(OpeningHours::class); + $subject = new Parser( + $openingHours->reveal() + ); + + $result = $subject->getLanguages([]); + + self::assertSame([], $result); + } + + /** + * @test + */ + public function returnsOpeningHours(): void + { + $jsonLD = [ + 'schema:openingHoursSpecification' => [ + '@id' => 'genid-28b33237f71b41e3ad54a99e1da769b9-b13', + 'schema:opens' => [ + '@type' => 'schema:Time', + '@value' => '10:00:00', + ], + ], + ]; + $generatedOpeningHours = [ + 'opens' => '10:00:00', + 'closes' => '', + 'from' => null, + 'through' => null, + 'daysOfWeek' => [], + ]; + + $openingHours = $this->prophesize(OpeningHours::class); + $openingHours->get($jsonLD)->willReturn($generatedOpeningHours); + + $subject = new Parser( + $openingHours->reveal() + ); + + $result = $subject->getOpeningHours($jsonLD); + + self::assertSame($generatedOpeningHours, $result); + } +} diff --git a/Tests/Unit/Domain/Import/Model/EntityCollectionTest.php b/Tests/Unit/Domain/Import/Model/EntityCollectionTest.php new file mode 100644 index 0000000..182cff4 --- /dev/null +++ b/Tests/Unit/Domain/Import/Model/EntityCollectionTest.php @@ -0,0 +1,138 @@ + + * + * 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\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Model\EntityCollection + */ +class EntityCollectionTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new EntityCollection(); + + self::assertInstanceOf(EntityCollection::class, $subject); + } + + /** + * @test + */ + public function returnsEmptyArrayAsDefaultEntities(): void + { + $subject = new EntityCollection(); + + self::assertSame([], $subject->getEntities()); + } + + /** + * @test + */ + public function returnsFirstEntityForDefaultLanguage(): void + { + $entityWithTranslation = $this->prophesize(Entity::class); + $entityWithTranslation->isForDefaultLanguage()->willReturn(false); + + $entityWithDefaultLanguage = $this->prophesize(Entity::class); + $entityWithDefaultLanguage->isForDefaultLanguage()->willReturn(true); + + $subject = new EntityCollection(); + $subject->add($entityWithTranslation->reveal()); + $subject->add($entityWithDefaultLanguage->reveal()); + + self::assertSame( + $entityWithDefaultLanguage->reveal(), + $subject->getDefaultLanguageEntity() + ); + } + + /** + * @test + */ + public function returnsNullIfNoEntityForDefaultLanguageExists(): void + { + $entityWithTranslation = $this->prophesize(Entity::class); + $entityWithTranslation->isForDefaultLanguage()->willReturn(false); + + $subject = new EntityCollection(); + $subject->add($entityWithTranslation->reveal()); + + self::assertSame( + null, + $subject->getDefaultLanguageEntity() + ); + } + + /** + * @test + */ + public function returnsAllTranslatedEntities(): void + { + $entityWithTranslation1 = $this->prophesize(Entity::class); + $entityWithTranslation1->isTranslation()->willReturn(true); + $entityWithTranslation2 = $this->prophesize(Entity::class); + $entityWithTranslation2->isTranslation()->willReturn(true); + $entitiyWithDefaultLanguage = $this->prophesize(Entity::class); + $entitiyWithDefaultLanguage->isTranslation()->willReturn(false); + + $subject = new EntityCollection(); + $subject->add($entityWithTranslation1->reveal()); + $subject->add($entitiyWithDefaultLanguage->reveal()); + $subject->add($entityWithTranslation2->reveal()); + + self::assertSame( + [ + 0 => $entityWithTranslation1->reveal(), + 2 => $entityWithTranslation2->reveal(), + ], + $subject->getTranslatedEntities() + ); + } + + /** + * @test + */ + public function returnsEmptyArrayIfNoTranslatedEntityExists(): void + { + $entitiyWithDefaultLanguage = $this->prophesize(Entity::class); + $entitiyWithDefaultLanguage->isTranslation()->willReturn(false); + + $subject = new EntityCollection(); + $subject->add($entitiyWithDefaultLanguage->reveal()); + + self::assertSame( + [], + $subject->getTranslatedEntities() + ); + } +} diff --git a/Tests/Unit/Domain/Import/Model/GenericEntityTest.php b/Tests/Unit/Domain/Import/Model/GenericEntityTest.php index 863285a..85c31d8 100644 --- a/Tests/Unit/Domain/Import/Model/GenericEntityTest.php +++ b/Tests/Unit/Domain/Import/Model/GenericEntityTest.php @@ -39,6 +39,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [] ); @@ -53,6 +54,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 10, '', + 0, '', [] ); @@ -67,12 +69,58 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, 'tx_thuecat_entity', + 0, '', [] ); self::assertSame('tx_thuecat_entity', $subject->getTypo3DatabaseTableName()); } + /** + * @test + */ + public function returnsTypo3SystemLanguageUid(): void + { + $subject = new GenericEntity( + 0, + '', + 10, + '', + [] + ); + self::assertSame(10, $subject->getTypo3SystemLanguageUid()); + } + + /** + * @test + */ + public function claimsIsForDefaultLanguage(): void + { + $subject = new GenericEntity( + 0, + '', + 0, + '', + [] + ); + self::assertTrue($subject->isForDefaultLanguage()); + } + + /** + * @test + */ + public function claimsIsTranslation(): void + { + $subject = new GenericEntity( + 0, + '', + 10, + '', + [] + ); + self::assertTrue($subject->isTranslation()); + } + /** * @test */ @@ -81,6 +129,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, 'https://example.com/resources/333039283321-xxwg', [] ); @@ -98,6 +147,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [ 'column_name_1' => 'value 1', @@ -121,6 +171,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [] ); @@ -138,6 +189,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [] ); @@ -155,6 +207,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [] ); @@ -172,6 +225,7 @@ class GenericEntityTest extends TestCase $subject = new GenericEntity( 0, '', + 0, '', [] ); diff --git a/composer.json b/composer.json index 57e187a..da0f5ad 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "symfony/dependency-injection": "^5.2", "typo3/cms-backend": "^10.4", "typo3/cms-core": "^10.4", - "typo3/cms-extbase": "^10.4" + "typo3/cms-extbase": "^10.4", + "typo3/cms-frontend": "^10.4" }, "require-dev": { "friendsoftypo3/phpstan-typo3": "^0.6.0", diff --git a/dependency-checker.json b/dependency-checker.json index e961f67..304dd3a 100644 --- a/dependency-checker.json +++ b/dependency-checker.json @@ -5,6 +5,7 @@ "Configuration/SiteConfiguration/Overrides/*.php", "Configuration/TCA/*.php", "ext_emconf.php", + "ext_localconf.php", "ext_tables.php" ] } diff --git a/ext_localconf.php b/ext_localconf.php new file mode 100644 index 0000000..443e5ae --- /dev/null +++ b/ext_localconf.php @@ -0,0 +1,5 @@ +&TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\) does not accept TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\.$#" + count: 1 + path: Classes/Domain/Model/Backend/ImportLog.php + + - + message: "#^Cannot call method getFirst\\(\\) on array\\|TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\QueryResultInterface\\.$#" + count: 1 + path: Classes/Domain/Repository/Backend/TownRepository.php + + - + message: "#^Method WerkraumMedia\\\\ThueCat\\\\Domain\\\\Repository\\\\Backend\\\\TownRepository\\:\\:findOneByRemoteIds\\(\\) should return WerkraumMedia\\\\ThueCat\\\\Domain\\\\Model\\\\Backend\\\\Town\\|null but returns object\\.$#" + count: 1 + path: Classes/Domain/Repository/Backend/TownRepository.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\Driver\\\\Statement\\|int\\.$#" + count: 1 + path: Classes/Frontend/DataProcessing/ResolveEntities.php + diff --git a/phpstan.neon b/phpstan.neon index c685620..3981980 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,6 @@ includes: - vendor/friendsoftypo3/phpstan-typo3/extension.neon + - phpstan-baseline.neon parameters: level: max paths: @@ -8,20 +9,3 @@ parameters: - Tests checkMissingIterableValueType: false reportUnmatchedIgnoredErrors: true - ignoreErrors: - - - message: "#^Cannot call method fetchColumn\\(\\) on Doctrine\\\\DBAL\\\\Driver\\\\Statement\\|int\\.$#" - count: 1 - path: Classes/Domain/Import/Importer/SaveData.php - - - message: "#^Property WerkraumMedia\\\\ThueCat\\\\Domain\\\\Model\\\\Backend\\\\ImportLog\\:\\:\\$logEntries \\(iterable\\&TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\) does not accept TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage\\.$#" - count: 1 - path: Classes/Domain/Model/Backend/ImportLog.php - - - message: "#^Cannot call method getFirst\\(\\) on array\\|TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\QueryResultInterface\\.$#" - count: 1 - path: Classes/Domain/Repository/Backend/TownRepository.php - - - message: "#^Method WerkraumMedia\\\\ThueCat\\\\Domain\\\\Repository\\\\Backend\\\\TownRepository\\:\\:findOneByRemoteIds\\(\\) should return WerkraumMedia\\\\ThueCat\\\\Domain\\\\Model\\\\Backend\\\\Town\\|null but returns object\\.$#" - count: 1 - path: Classes/Domain/Repository/Backend/TownRepository.php