diff --git a/src/Application/Link.php b/src/Application/Link.php index ae5b55d..08bcc93 100644 --- a/src/Application/Link.php +++ b/src/Application/Link.php @@ -8,11 +8,12 @@ use Fau\DegreeProgram\Common\Domain\MultilingualLink; /** - * @psalm-type LinkType = array{ + * @psalm-type Link = array{ * name: string, * link_text: string, * link_url: string, * } + * @psalm-type LinkType = Link & array{parent?: Link|null} */ final class Link { @@ -20,6 +21,7 @@ private function __construct( private string $name, private string $linkText, private string $linkUrl, + private ?Link $parent, ) { } @@ -27,15 +29,16 @@ public static function new( string $name, string $linkText, string $linkUrl, + ?Link $parent = null ): self { - return new self($name, $linkText, $linkUrl); + return new self($name, $linkText, $linkUrl, $parent); } public static function empty(): self { - return new self('', '', ''); + return new self('', '', '', null); } public static function fromMultilingualLink( @@ -47,6 +50,7 @@ public static function fromMultilingualLink( $multilingualLink->name()->asString($languageCode), $multilingualLink->linkText()->asString($languageCode), $multilingualLink->linkUrl()->asString($languageCode), + !is_null($multilingualLink->parent()) ? self::fromMultilingualLink($multilingualLink->parent(), $languageCode) : null, ); } @@ -55,10 +59,14 @@ public static function fromMultilingualLink( */ public static function fromArray(array $data): self { + /** @var Link|null $parentData */ + $parentData = $data[MultilingualLink::PARENT] ?? null; + return new self( $data[MultilingualLink::NAME], $data[MultilingualLink::LINK_TEXT], $data[MultilingualLink::LINK_URL], + !empty($parentData) ? self::fromArray($parentData) : null, ); } @@ -77,15 +85,24 @@ public function linkUrl(): string return $this->linkUrl; } + public function parent(): ?Link + { + return $this->parent; + } + /** * @return LinkType $data */ public function asArray(): array { + /** @var Link|null $parentData */ + $parentData = $this->parent?->asArray(); + return [ MultilingualLink::NAME => $this->name, MultilingualLink::LINK_TEXT => $this->linkText, MultilingualLink::LINK_URL => $this->linkUrl, + MultilingualLink::PARENT => $parentData, ]; } diff --git a/src/Domain/MultilingualLink.php b/src/Domain/MultilingualLink.php index 32e9aad..fb11a54 100644 --- a/src/Domain/MultilingualLink.php +++ b/src/Domain/MultilingualLink.php @@ -6,12 +6,13 @@ /** * @psalm-import-type MultilingualStringType from MultilingualString - * @psalm-type MultilingualLinkType = array{ + * @psalm-type MultilingualLink = array{ * id: string, * name: MultilingualStringType, * link_text: MultilingualStringType, * link_url: MultilingualStringType * } + * @psalm-type MultilingualLinkType = MultilingualLink & array{parent?: MultilingualLink|null} */ final class MultilingualLink { @@ -19,10 +20,11 @@ final class MultilingualLink public const NAME = 'name'; public const LINK_TEXT = 'link_text'; public const LINK_URL = 'link_url'; + public const PARENT = 'parent'; public const SCHEMA = [ 'type' => 'object', - 'additionalProperties' => false, + 'additionalProperties' => true, 'required' => [ MultilingualLink::ID, MultilingualLink::NAME, @@ -47,6 +49,7 @@ final class MultilingualLink MultilingualLink::NAME, MultilingualLink::LINK_TEXT, MultilingualLink::LINK_URL, + MultilingualLink::PARENT, ], 'properties' => [ MultilingualLink::ID => [ @@ -56,6 +59,19 @@ final class MultilingualLink MultilingualLink::NAME => MultilingualString::SCHEMA, MultilingualLink::LINK_TEXT => MultilingualString::SCHEMA, MultilingualLink::LINK_URL => MultilingualString::SCHEMA, + MultilingualLink::PARENT => [ + 'type' => ['object', 'null'], + 'additionalProperties' => true, + 'required' => [ + MultilingualLink::ID, + ], + 'properties' => [ + MultilingualLink::ID => [ + 'type' => 'string', + 'minLength' => 1, + ], + ], + ], ], ]; @@ -64,6 +80,7 @@ private function __construct( private MultilingualString $name, private MultilingualString $linkText, private MultilingualString $linkUrl, + private ?MultilingualLink $parent, ) { } @@ -72,9 +89,10 @@ public static function new( MultilingualString $name, MultilingualString $linkText, MultilingualString $linkUrl, + ?MultilingualLink $parent = null ): self { - return new self($id, $name, $linkText, $linkUrl); + return new self($id, $name, $linkText, $linkUrl, $parent); } public static function empty(): self @@ -84,6 +102,7 @@ public static function empty(): self MultilingualString::empty(), MultilingualString::empty(), MultilingualString::empty(), + null ); } @@ -92,11 +111,15 @@ public static function empty(): self */ public static function fromArray(array $data): self { + /** @var MultilingualLink|null $parentData */ + $parentData = $data[self::PARENT] ?? null; + return new self( $data[self::ID], MultilingualString::fromArray($data[self::NAME]), MultilingualString::fromArray($data[self::LINK_TEXT]), MultilingualString::fromArray($data[self::LINK_URL]), + !empty($parentData) ? self::fromArray($parentData) : null, ); } @@ -105,11 +128,15 @@ public static function fromArray(array $data): self */ public function asArray(): array { + /** @var MultilingualLink|null $parentData */ + $parentData = $this->parent?->asArray(); + return [ self::ID => $this->id, self::NAME => $this->name->asArray(), self::LINK_TEXT => $this->linkText->asArray(), self::LINK_URL => $this->linkUrl->asArray(), + self::PARENT => $parentData, ]; } @@ -132,4 +159,9 @@ public function linkUrl(): MultilingualString { return $this->linkUrl; } + + public function parent(): ?MultilingualLink + { + return $this->parent; + } } diff --git a/src/Infrastructure/Repository/BilingualRepository.php b/src/Infrastructure/Repository/BilingualRepository.php index cc76f3f..e899ef4 100644 --- a/src/Infrastructure/Repository/BilingualRepository.php +++ b/src/Infrastructure/Repository/BilingualRepository.php @@ -125,13 +125,16 @@ final protected function bilingualOption(string $key): MultilingualString ); } - final protected function bilingualLinkFromTerm(?WP_Term $term): MultilingualLink + final protected function bilingualLinkFromTerm(?WP_Term $term, string $taxonomy): MultilingualLink { + $parentTerm = $term instanceof WP_Term && $term->parent ? get_term($term->parent, $taxonomy) : null; + return MultilingualLink::new( $term instanceof WP_Term ? $this->idGenerator->generateTermId($term) : '', name: $this->bilingualTermName($term), linkText: $this->bilingualTermMeta($term, MultilingualLink::LINK_TEXT), linkUrl: $this->bilingualTermMeta($term, MultilingualLink::LINK_URL), + parent: $parentTerm instanceof WP_Term ? $this->bilingualLinkFromTerm($parentTerm, $taxonomy) : null, ); } @@ -144,7 +147,7 @@ final protected function bilingualTermLinks(WP_Post $post, string $taxonomy): Mu $links = []; foreach ($terms as $term) { - $links[] = $this->bilingualLinkFromTerm($term); + $links[] = $this->bilingualLinkFromTerm($term, $taxonomy); } return MultilingualLinks::new(...$links); diff --git a/src/Infrastructure/Repository/WordPressDatabaseDegreeProgramRepository.php b/src/Infrastructure/Repository/WordPressDatabaseDegreeProgramRepository.php index ab71bd4..a18f6b2 100644 --- a/src/Infrastructure/Repository/WordPressDatabaseDegreeProgramRepository.php +++ b/src/Infrastructure/Repository/WordPressDatabaseDegreeProgramRepository.php @@ -144,20 +144,23 @@ public function getById(DegreeProgramId $degreeProgramId): DegreeProgram bachelorOrTeachingDegree: $this->admissionRequirement( $this->firstTerm( $post, - BachelorOrTeachingDegreeAdmissionRequirementTaxonomy::KEY - ) + BachelorOrTeachingDegreeAdmissionRequirementTaxonomy::KEY, + ), + BachelorOrTeachingDegreeAdmissionRequirementTaxonomy::KEY, ), teachingDegreeHigherSemester: $this->admissionRequirement( $this->firstTerm( $post, - TeachingDegreeHigherSemesterAdmissionRequirementTaxonomy::KEY - ) + TeachingDegreeHigherSemesterAdmissionRequirementTaxonomy::KEY, + ), + TeachingDegreeHigherSemesterAdmissionRequirementTaxonomy::KEY, ), master: $this->admissionRequirement( $this->firstTerm( $post, MasterDegreeAdmissionRequirementTaxonomy::KEY, - ) + ), + MasterDegreeAdmissionRequirementTaxonomy::KEY, ), ), contentRelatedMasterRequirements: $this->bilingualPostMeta( @@ -182,15 +185,17 @@ public function getById(DegreeProgramId $degreeProgramId): DegreeProgram true ), germanLanguageSkillsForInternationalStudents: $this->bilingualLinkFromTerm( - $this->firstTopTerm( + $this->firstTerm( $post, GermanLanguageSkillsForInternationalStudentsTaxonomy::KEY, - ) + ), + GermanLanguageSkillsForInternationalStudentsTaxonomy::KEY, ), startOfSemester: $this->bilingualLinkFromOption(DegreeProgram::START_OF_SEMESTER), semesterDates: $this->bilingualLinkFromOption(DegreeProgram::SEMESTER_DATES), examinationsOffice: $this->bilingualLinkFromTerm( - $this->firstTerm($post, ExaminationsOfficeTaxonomy::KEY) + $this->firstTerm($post, ExaminationsOfficeTaxonomy::KEY), + ExaminationsOfficeTaxonomy::KEY, ), examinationRegulations: (string) get_post_meta( $postId, @@ -206,7 +211,8 @@ public function getById(DegreeProgramId $degreeProgramId): DegreeProgram department: $this->bilingualPostMeta($post, DegreeProgram::DEPARTMENT), studentAdvice: $this->bilingualLinkFromOption(DegreeProgram::STUDENT_ADVICE), subjectSpecificAdvice: $this->bilingualLinkFromTerm( - $this->firstTerm($post, SubjectSpecificAdviceTaxonomy::KEY) + $this->firstTerm($post, SubjectSpecificAdviceTaxonomy::KEY), + SubjectSpecificAdviceTaxonomy::KEY, ), serviceCenters: $this->bilingualLinkFromOption(DegreeProgram::SERVICE_CENTERS), infoBrochure: (string) get_post_meta( @@ -235,6 +241,7 @@ public function getById(DegreeProgramId $degreeProgramId): DegreeProgram ), applyNowLink: $this->bilingualLinkFromTerm( $this->firstTerm($post, ApplyNowLinkTaxonomy::KEY), + ApplyNowLinkTaxonomy::KEY, ), combinations: $this->idsFromPostMeta($postId, DegreeProgram::COMBINATIONS), limitedCombinations: $this->idsFromPostMeta( @@ -273,27 +280,6 @@ private function firstTerm(WP_Post $post, string $taxonomy): ?WP_Term return $terms[0]; } - private function firstTopTerm(WP_Post $post, string $taxonomy): ?WP_Term - { - $terms = get_the_terms($post, $taxonomy); - - if (!is_array($terms)) { - return null; - } - - if (!isset($terms[0]) || !$terms[0] instanceof WP_Term) { - return null; - } - - if (!$terms[0]->parent) { - return $terms[0]; - } - - $parent = get_term($terms[0]->parent); - - return $parent instanceof WP_Term ? $parent : $terms[0]; - } - private function degree(WP_Post $post): Degree { $term = $this->firstTerm($post, DegreeTaxonomy::KEY); @@ -317,7 +303,7 @@ private function degreeFromTerm(WP_Term $term): Degree ); } - private function admissionRequirement(?WP_Term $term): AdmissionRequirement + private function admissionRequirement(?WP_Term $term, string $taxonomy): AdmissionRequirement { if (!$term instanceof WP_Term) { return AdmissionRequirement::empty(); @@ -326,8 +312,8 @@ private function admissionRequirement(?WP_Term $term): AdmissionRequirement $parent = $term->parent ? get_term($term->parent) : null; return AdmissionRequirement::new( - $this->bilingualLinkFromTerm($term), - $parent instanceof WP_Term ? $this->admissionRequirement($parent) : null, + $this->bilingualLinkFromTerm($term, $taxonomy), + $parent instanceof WP_Term ? $this->admissionRequirement($parent, $taxonomy) : null, $term->slug ); } diff --git a/tests/resources/fixtures/degree_program.json b/tests/resources/fixtures/degree_program.json index 0c8ce1a..c4d73b7 100644 --- a/tests/resources/fixtures/degree_program.json +++ b/tests/resources/fixtures/degree_program.json @@ -103,7 +103,8 @@ "id": "term_meta:11:link_url", "de": "https:\/\/fau.localhost\/faculty-math", "en": "https:\/\/fau.localhost\/faculty-math-en" - } + }, + "parent": null } ], "location": [ @@ -380,6 +381,25 @@ "id": "term_meta:16:link_url", "de": "https:\/\/fau.localhost\/german-language-skills-international-students", "en": "https:\/\/fau.localhost\/german-language-skills-international-students-en" + }, + "parent": { + "id": "term:12", + "name": { + "id": "term:12:name", + "de": "German language skills for international students (parent)", + "en": "German language skills for international students EN (parent)" + }, + "link_text": { + "id": "term_meta:12:link_text", + "de": "Link to german language skills for international students (parent)", + "en": "Link to german language skills for international students EN (parent)" + }, + "link_url": { + "id": "term_meta:12:link_url", + "de": "https:\/\/fau.localhost\/german-language-skills-international-students-parent", + "en": "https:\/\/fau.localhost\/german-language-skills-international-students-en-parent" + }, + "parent": null } }, "start_of_semester": { @@ -398,7 +418,8 @@ "id": "option:fau_start_of_semester:link_url", "de": "https:\/\/fau.localhost\/start-of-semester", "en": "https:\/\/fau.localhost\/start-of-semester-en" - } + }, + "parent": null }, "semester_dates": { "id": "option:fau_semester_dates", @@ -416,7 +437,8 @@ "id": "option:fau_semester_dates:link_url", "de": "https:\/\/fau.localhost\/semester-dates", "en": "https:\/\/fau.localhost\/semester-dates-en" - } + }, + "parent": null }, "examinations_office": { "id": "term:15", @@ -434,7 +456,8 @@ "id": "term_meta:15:link_url", "de": "https:\/\/fau.localhost\/examinations-office", "en": "https:\/\/fau.localhost\/examinations-office-en" - } + }, + "parent": null }, "examination_regulations": "https://fau.localhost/examinations-regulations", "module_handbook": "Module handbook value", @@ -464,7 +487,8 @@ "id": "option:fau_student_advice:link_url", "de": "https:\/\/fau.localhost\/career-service", "en": "https:\/\/fau.localhost\/career-service-en" - } + }, + "parent": null }, "subject_specific_advice": { "id": "term:6", @@ -482,7 +506,8 @@ "id": "term_meta:6:link_url", "de": "https:\/\/fau.localhost\/advice", "en": "https:\/\/fau.localhost\/advice-en" - } + }, + "parent": null }, "service_centers": { "id": "option:fau_service_centers", @@ -500,7 +525,8 @@ "id": "option:fau_service_centers:link_url", "de": "https:\/\/fau.localhost\/counseling", "en": "https:\/\/fau.localhost\/counseling-en" - } + }, + "parent": null }, "info_brochure": "Info Brochure 2023", "semester_fee": { @@ -519,7 +545,8 @@ "id": "option:fau_semester_fee:link_url", "de": "https:\/\/fau.localhost\/semester-fee", "en": "https:\/\/fau.localhost\/semester-fee-en" - } + }, + "parent": null }, "degree_program_fees": { "id": "post_meta:25:degree_program_fees", @@ -542,7 +569,8 @@ "id": "option:fau_abroad_opportunities:link_url", "de": "https:\/\/fau.localhost\/abroad", "en": "https:\/\/fau.localhost\/abroad-en" - } + }, + "parent": null }, "keywords": [ { @@ -573,7 +601,8 @@ "id": "term_meta:3:link_url", "de": "https:\/\/fau.localhost\/biology", "en": "https:\/\/fau.localhost\/biology-en" - } + }, + "parent": null }, { "id": "term:38", @@ -591,7 +620,8 @@ "id": "term_meta:38:link_url", "de": "https:\/\/fau.localhost\/biology-math", "en": "https:\/\/fau.localhost\/biology-math-en" - } + }, + "parent": null } ], "combinations": [ @@ -617,7 +647,8 @@ "id": "option:fau_notes_for_international_applicants:link_url", "de": "https:\/\/fau.localhost\/notes-for-intl-applicants", "en": "https:\/\/fau.localhost\/notes-for-intl-applicants-en" - } + }, + "parent": null }, "student_initiatives": { "id": "option:student_initiatives", @@ -635,7 +666,8 @@ "id": "option:student_initiatives:link_url", "de": "https:\/\/fau.localhost\/fsi", "en": "https:\/\/fau.localhost\/fsi-en" - } + }, + "parent": null }, "apply_now_link": { "id": "term:39", @@ -653,7 +685,8 @@ "id": "term_meta:39:link_url", "de": "https://campo.fau.de", "en": "https://campo.fau.de" - } + }, + "parent": null }, "entry_text": { "id": "post_meta:25:entry_text", diff --git a/tests/unit/Domain/MultilingualLinkTest.php b/tests/unit/Domain/MultilingualLinkTest.php index 3ed4d40..359c4a0 100644 --- a/tests/unit/Domain/MultilingualLinkTest.php +++ b/tests/unit/Domain/MultilingualLinkTest.php @@ -28,6 +28,7 @@ public function testFromArray(): void 'de' => 'https://fau.localhost/faculty-math', 'en' => 'https://fau.localhost/faculty-math-en', ], + 'parent' => null ]; $sut = MultilingualLink::fromArray($array); diff --git a/tests/unit/Domain/MultilingualLinksTest.php b/tests/unit/Domain/MultilingualLinksTest.php index ee25f77..1e8b020 100644 --- a/tests/unit/Domain/MultilingualLinksTest.php +++ b/tests/unit/Domain/MultilingualLinksTest.php @@ -29,6 +29,7 @@ public function testFromArray(): void 'de' => 'https://fau.localhost/biology', 'en' => 'https://fau.localhost/biology-en', ], + 'parent' => null ], [ 'id' => 'term:38', @@ -47,6 +48,7 @@ public function testFromArray(): void 'de' => 'https://fau.localhost/biology-math', 'en' => 'https://fau.localhost/biology-math-en', ], + 'parent' => null ], ];