Skip to content

Commit 80fc78c

Browse files
authored
fix: ResolveLinks looses anchor (#94)
1 parent ec78b77 commit 80fc78c

File tree

4 files changed

+132
-1
lines changed

4 files changed

+132
-1
lines changed

src/Domain/Value/Link.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
public bool $isFolder;
3535
public bool $isStartPage;
3636
public bool $isPublished;
37+
public ?string $anchor;
3738

3839
/**
3940
* @var list<LinkAlternate>
@@ -103,6 +104,16 @@ public function __construct(array $values)
103104
Assert::keyExists($values, 'published');
104105
$this->isPublished = true === $values['published'];
105106

107+
// This field is only set when using the stories api with resolve_links flag
108+
// and the original multilink field contains an anchor.
109+
$anchor = null;
110+
111+
if (\array_key_exists('anchor', $values) && null !== $values['anchor'] && '' !== $values['anchor']) {
112+
$anchor = TrimmedNonEmptyString::fromString($values['anchor'], 'The value of the "anchor" key must be a non-empty, trimmed string. Got: %s')->toString();
113+
}
114+
115+
$this->anchor = $anchor;
116+
106117
$alternates = [];
107118

108119
if (\array_key_exists('alternates', $values) && null !== $values['alternates'] && [] !== $values['alternates']) {

src/Resolver/StoryResolver.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@ private function doResolve(array &$target, array &$relationMap): void
5050
if (\is_string($value) && isset($relationMap[$value])) {
5151
$value = $relationMap[$value];
5252
} elseif (\is_array($value) && isset($value['id'], $relationMap[$value['id']])) {
53-
$value = $relationMap[$value['id']];
53+
$resolved = $relationMap[$value['id']];
54+
55+
// Preserve the anchor from the original multilink field, as it is not part
56+
// of the resolved link payload but user-defined on the multilink itself.
57+
if (\array_key_exists('anchor', $value) && null !== $value['anchor'] && '' !== $value['anchor']) {
58+
$resolved['anchor'] = $value['anchor'];
59+
}
60+
61+
$value = $resolved;
5462
} elseif (\is_array($value)) {
5563
$this->doResolve($value, $relationMap);
5664
}

tests/Unit/Domain/Value/LinkTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,47 @@ public function fullSlugMustBeValidString(string $value): void
480480
new Link($values);
481481
}
482482

483+
#[Test]
484+
public function anchor(): void
485+
{
486+
$faker = self::faker();
487+
488+
$values = $faker->linkResponse(['anchor' => $anchor = $faker->word()]);
489+
490+
self::assertSame($anchor, (new Link($values))->anchor);
491+
}
492+
493+
#[Test]
494+
public function anchorIsOptional(): void
495+
{
496+
$faker = self::faker();
497+
498+
$values = $faker->linkResponse();
499+
unset($values['anchor']);
500+
501+
self::assertNull((new Link($values))->anchor);
502+
}
503+
504+
#[Test]
505+
public function anchorIsOptionalWithNull(): void
506+
{
507+
$faker = self::faker();
508+
509+
$values = $faker->linkResponse(['anchor' => null]);
510+
511+
self::assertNull((new Link($values))->anchor);
512+
}
513+
514+
#[Test]
515+
public function anchorIsOptionalWithEmptyString(): void
516+
{
517+
$faker = self::faker();
518+
519+
$values = $faker->linkResponse(['anchor' => '']);
520+
521+
self::assertNull((new Link($values))->anchor);
522+
}
523+
483524
#[Test]
484525
public function getSlugWithNullReturnsFullSlugWhenAvailable(): void
485526
{

tests/Unit/Resolver/StoryResolverTest.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,77 @@ public function resolveWithComplexStructure(): void
159159
self::assertSame($expected, $resolver->resolve($story, $references));
160160
}
161161

162+
#[Test]
163+
public function resolveReplacesMultiLinkWithLinkPayloadAndPreservesAnchor(): void
164+
{
165+
$resolver = new StoryResolver();
166+
167+
$faker = self::faker();
168+
169+
$story = [
170+
'name' => $faker->word(),
171+
'content' => [
172+
'link' => [
173+
'id' => $referenceUuid = $faker->uuid(),
174+
'url' => '',
175+
'anchor' => $anchor = $faker->word(),
176+
'linktype' => 'story',
177+
'fieldtype' => 'multilink',
178+
'cached_url' => 'contact',
179+
],
180+
],
181+
];
182+
183+
$references = [
184+
$reference = [
185+
'uuid' => $referenceUuid,
186+
'name' => $faker->word(),
187+
'another_field' => $faker->sentence(),
188+
],
189+
];
190+
191+
$expected = $story;
192+
$expected['content']['link'] = $reference;
193+
$expected['content']['link']['anchor'] = $anchor;
194+
195+
self::assertSame($expected, $resolver->resolve($story, $references));
196+
}
197+
198+
#[Test]
199+
public function resolveReplacesMultiLinkWithLinkPayloadAndDoesNotPreserveEmptyAnchor(): void
200+
{
201+
$resolver = new StoryResolver();
202+
203+
$faker = self::faker();
204+
205+
$story = [
206+
'name' => $faker->word(),
207+
'content' => [
208+
'link' => [
209+
'id' => $referenceUuid = $faker->uuid(),
210+
'url' => '',
211+
'anchor' => '',
212+
'linktype' => 'story',
213+
'fieldtype' => 'multilink',
214+
'cached_url' => 'contact',
215+
],
216+
],
217+
];
218+
219+
$references = [
220+
$reference = [
221+
'uuid' => $referenceUuid,
222+
'name' => $faker->word(),
223+
'another_field' => $faker->sentence(),
224+
],
225+
];
226+
227+
$expected = $story;
228+
$expected['content']['link'] = $reference;
229+
230+
self::assertSame($expected, $resolver->resolve($story, $references));
231+
}
232+
162233
#[Test]
163234
public function resolveReplacesMultiLinkWithLinkPayload(): void
164235
{

0 commit comments

Comments
 (0)