diff --git a/src/Resolver/StoryResolver.php b/src/Resolver/StoryResolver.php index 1027b89..82036fd 100644 --- a/src/Resolver/StoryResolver.php +++ b/src/Resolver/StoryResolver.php @@ -31,21 +31,34 @@ public function resolve(array $target, array $relations): array $relationMap[$relation['uuid']] = $relation; } - foreach ($target as &$value) { - if (\is_string($value) && \array_key_exists($value, $relationMap)) { - $value = $relationMap[$value]; + return $this->doResolve($target, $relationMap, []); + } + private function doResolve(array $target, array $relationMap, array $seen): array + { + foreach ($target as $key => $value) { + if ('uuid' === $key) { continue; } - if (\is_array($value) && \array_key_exists('id', $value) && \array_key_exists($value['id'], $relationMap)) { - $value = $relationMap[$value['id']]; + if (\is_string($value) && isset($relationMap[$value])) { + if (\in_array($value, $seen, true)) { + continue; + } - continue; - } + $seen[] = $value; + $target[$key] = $this->doResolve($relationMap[$value], $relationMap, $seen); + } elseif (\is_array($value) && isset($value['id'], $relationMap[$value['id']])) { + $id = $value['id']; + + if (\in_array($id, $seen, true)) { + continue; + } - if (\is_array($value)) { - $value = $this->resolve($value, $relations); + $seen[] = $id; + $target[$key] = $this->doResolve($relationMap[$id], $relationMap, $seen); + } elseif (\is_array($value)) { + $target[$key] = $this->doResolve($value, $relationMap, $seen); } } diff --git a/tests/Unit/Resolver/StoryResolverTest.php b/tests/Unit/Resolver/StoryResolverTest.php index 8690eb2..3054b8e 100644 --- a/tests/Unit/Resolver/StoryResolverTest.php +++ b/tests/Unit/Resolver/StoryResolverTest.php @@ -188,4 +188,121 @@ public function resolveReplacesMultiLinkWithLinkPayload(): void self::assertSame($expected, $resolver->resolve($story, $references)); } + + #[Test] + public function resolveRecursive(): void + { + $resolver = new StoryResolver(); + + $faker = self::faker(); + + $story = [ + 'name' => $faker->word(), + 'content' => [ + 'uuid' => $faker->uuid(), + 'reference' => $cUuid = $faker->uuid(), + 'some_field' => $faker->word(), + ], + ]; + + $references = [ + $b = [ + 'uuid' => $bUuid = $faker->uuid(), + 'name' => $faker->word(), + 'another_field' => $faker->sentence(), + ], + $c = [ + 'uuid' => $cUuid, + 'name' => $faker->word(), + 'some_field' => [ + 'test' => $bUuid, + ], + ], + ]; + + $expected = $story; + $expected['content']['reference'] = $c; + $expected['content']['reference']['some_field']['test'] = $b; + + self::assertSame($expected, $resolver->resolve($story, $references)); + } + + #[Test] + public function resolveWithManyNestedRels(): void + { + $resolver = new StoryResolver(); + + $faker = self::faker(); + + $story = [ + 'name' => $faker->word(), + 'content' => [ + 'uuid' => $faker->uuid(), + 'reference' => $cUuid = $faker->uuid(), + 'some_field' => $faker->word(), + ], + ]; + + $references = [ + $a = [ + 'uuid' => $aUuid = $faker->uuid, + 'name' => $faker->word(), + ], + $b = [ + 'uuid' => $bUuid = $faker->uuid(), + 'name' => $faker->word(), + 'another_field' => $faker->sentence(), + 'field' => $aUuid, + ], + $c = [ + 'uuid' => $cUuid, + 'name' => $faker->word(), + 'some_field' => [ + 'test' => $bUuid, + ], + ], + ]; + + $expected = $story; + $expected['content']['reference'] = $c; + $expected['content']['reference']['some_field']['test'] = $b; + $expected['content']['reference']['some_field']['test']['field'] = $a; + + self::assertSame($expected, $resolver->resolve($story, $references)); + } + + #[Test] + public function resolveMustNeverResolveUuidOfStory(): void + { + $resolver = new StoryResolver(); + + $faker = self::faker(); + + $story = [ + 'name' => $faker->word(), + 'content' => [ + 'uuid' => $uuid = $faker->uuid(), + 'reference' => $referenceUuid = $faker->uuid(), + 'some_field' => $faker->word(), + ], + ]; + + $references = [ + $a = [ + 'uuid' => $referenceUuid, + 'name' => $faker->word(), + 'another_field' => $faker->sentence(), + ], + $b = [ + 'uuid' => $uuid, + 'name' => $faker->word(), + 'another_field' => $faker->sentence(), + ], + ]; + + $expected = $story; + $expected['content']['reference'] = $a; + + self::assertSame($expected, $resolver->resolve($story, $references)); + } }