Skip to content

Commit 61ca0e1

Browse files
authored
Enhancement: Automatic Relation Resolution (#8)
1 parent e4f305d commit 61ca0e1

File tree

16 files changed

+927
-15
lines changed

16 files changed

+927
-15
lines changed

src/Bridge/Faker/Generator.php

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@
2020
/**
2121
* @author Silas Joisten <[email protected]>
2222
*
23-
* @method array assetResponse(array $overrides = [])
24-
* @method array datasourceDimensionResponse(array $overrides = [])
25-
* @method array datasourceEntriesResponse(array $overrides = [])
26-
* @method array datasourceEntryResponse(array $overrides = [])
27-
* @method array datasourceResponse(array $overrides = [])
28-
* @method array datasourcesResponse(array $overrides = [])
29-
* @method array linkAlternateResponse(array $overrides = [])
30-
* @method array linkResponse(array $overrides = [])
31-
* @method array linksResponse(array $overrides = [])
32-
* @method array spaceResponse(array $overrides = [])
33-
* @method array storiesResponse(array $overrides = [])
34-
* @method array storyResponse(array $overrides = [])
35-
* @method array tagsResponse(array $overrides = [])
23+
* @method array assetResponse(array $overrides = [])
24+
* @method array datasourceDimensionResponse(array $overrides = [])
25+
* @method array datasourceEntriesResponse(array $overrides = [])
26+
* @method array datasourceEntryResponse(array $overrides = [])
27+
* @method array datasourceResponse(array $overrides = [])
28+
* @method array datasourcesResponse(array $overrides = [])
29+
* @method array linkAlternateResponse(array $overrides = [])
30+
* @method array linkResponse(array $overrides = [])
31+
* @method array linksResponse(array $overrides = [])
32+
* @method string relation()
33+
* @method array spaceResponse(array $overrides = [])
34+
* @method array storiesResponse(array $overrides = [])
35+
* @method array storyResponse(array $overrides = [])
36+
* @method array tagsResponse(array $overrides = [])
3637
*/
3738
final class Generator extends BaseGenerator
3839
{

src/Bridge/Faker/Provider/StoryblokProvider.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,4 +503,13 @@ public function assetResponse(array $overrides = []): array
503503
$overrides,
504504
);
505505
}
506+
507+
public function relation(): string
508+
{
509+
return \sprintf(
510+
'%s.%s',
511+
$this->generator->word(),
512+
$this->generator->word(),
513+
);
514+
}
506515
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of Storyblok-Api.
7+
*
8+
* (c) SensioLabs Deutschland <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Storyblok\Api\Domain\Value\Resolver;
15+
16+
use OskarStark\Value\TrimmedNonEmptyString;
17+
use Webmozart\Assert\Assert;
18+
19+
/**
20+
* @author Silas Joisten <[email protected]>
21+
*/
22+
final readonly class Relation
23+
{
24+
public function __construct(
25+
public string $value,
26+
) {
27+
TrimmedNonEmptyString::fromString($value);
28+
Assert::regex($value, '/^([a-zA-Z].+)\.([a-zA-Z].+)$/');
29+
}
30+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of Storyblok-Api.
7+
*
8+
* (c) SensioLabs Deutschland <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Storyblok\Api\Domain\Value\Resolver;
15+
16+
/**
17+
* @author Silas Joisten <[email protected]>
18+
*
19+
* @implements \IteratorAggregate<int, Relation>
20+
*/
21+
final class RelationCollection implements \Countable, \IteratorAggregate
22+
{
23+
/**
24+
* @var list<Relation>
25+
*/
26+
private array $items = [];
27+
28+
/**
29+
* @param list<Relation> $items
30+
*/
31+
public function __construct(
32+
array $items = [],
33+
) {
34+
foreach ($items as $item) {
35+
$this->add($item);
36+
}
37+
}
38+
39+
/**
40+
* @return \Traversable<int, Relation>
41+
*/
42+
public function getIterator(): \Traversable
43+
{
44+
return new \ArrayIterator($this->items);
45+
}
46+
47+
public function count(): int
48+
{
49+
return \count($this->items);
50+
}
51+
52+
public function add(Relation $relation): void
53+
{
54+
if ($this->has($relation)) {
55+
return;
56+
}
57+
58+
$this->items[] = $relation;
59+
}
60+
61+
public function has(Relation $relation): bool
62+
{
63+
foreach ($this->items as $item) {
64+
if ($item->value === $relation->value) {
65+
return true;
66+
}
67+
}
68+
69+
return false;
70+
}
71+
72+
public function remove(Relation $relation): void
73+
{
74+
foreach ($this->items as $key => $item) {
75+
if ($item->value === $relation->value) {
76+
unset($this->items[$key]);
77+
78+
break;
79+
}
80+
}
81+
}
82+
83+
public function toString(): string
84+
{
85+
return implode(',', array_map(static fn (Relation $relation): string => $relation->value, $this->items));
86+
}
87+
}

src/Request/StoriesRequest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Storyblok\Api\Domain\Value\Field\FieldCollection;
2020
use Storyblok\Api\Domain\Value\Filter\FilterCollection;
2121
use Storyblok\Api\Domain\Value\IdCollection;
22+
use Storyblok\Api\Domain\Value\Resolver\RelationCollection;
2223
use Storyblok\Api\Domain\Value\Tag\TagCollection;
2324
use Webmozart\Assert\Assert;
2425

@@ -38,6 +39,7 @@ public function __construct(
3839
public ?FieldCollection $excludeFields = null,
3940
public ?TagCollection $withTags = null,
4041
public ?IdCollection $excludeIds = null,
42+
public ?RelationCollection $withRelations = null,
4143
public ?Version $version = null,
4244
) {
4345
Assert::stringNotEmpty($language);
@@ -85,6 +87,10 @@ public function toArray(): array
8587
$array['excluding_ids'] = $this->excludeIds->toString();
8688
}
8789

90+
if (null !== $this->withRelations && $this->withRelations->count() > 0) {
91+
$array['resolve_relations'] = $this->withRelations->toString();
92+
}
93+
8894
if (null !== $this->version) {
8995
$array['version'] = $this->version->value;
9096
}

src/Request/StoryRequest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace Storyblok\Api\Request;
1515

1616
use Storyblok\Api\Domain\Value\Dto\Version;
17+
use Storyblok\Api\Domain\Value\Resolver\RelationCollection;
1718
use Webmozart\Assert\Assert;
1819

1920
/**
@@ -24,6 +25,7 @@
2425
public function __construct(
2526
public string $language = 'default',
2627
public ?Version $version = null,
28+
public ?RelationCollection $withRelations = null,
2729
) {
2830
Assert::stringNotEmpty($language);
2931
}
@@ -32,6 +34,7 @@ public function __construct(
3234
* @return array{
3335
* language: string,
3436
* version?: string,
37+
* resolve_relations?: string,
3538
* }
3639
*/
3740
public function toArray(): array
@@ -44,6 +47,10 @@ public function toArray(): array
4447
$array['version'] = $this->version->value;
4548
}
4649

50+
if (null !== $this->withRelations && $this->withRelations->count() > 0) {
51+
$array['resolve_relations'] = $this->withRelations->toString();
52+
}
53+
4754
return $array;
4855
}
4956
}

src/Resolver/ResolverInterface.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of Storyblok-Api.
7+
*
8+
* (c) SensioLabs Deutschland <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Storyblok\Api\Resolver;
15+
16+
/**
17+
* @author Silas Joisten <[email protected]>
18+
*/
19+
interface ResolverInterface
20+
{
21+
/**
22+
* Resolves relations in the target content using the given relations collection.
23+
*
24+
* @param array<string, mixed> $target the target story content containing UUIDs to resolve
25+
* @param array<int, array<string, mixed>> $relations the target story content containing UUIDs to resolve
26+
*
27+
* @return array<string, mixed>
28+
*/
29+
public function resolve(array $target, array $relations): array;
30+
}

src/Resolver/StoryResolver.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of Storyblok-Api.
7+
*
8+
* (c) SensioLabs Deutschland <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
namespace Storyblok\Api\Resolver;
15+
16+
use Webmozart\Assert\Assert;
17+
18+
/**
19+
* @author Silas Joisten <[email protected]>
20+
*/
21+
final readonly class StoryResolver implements ResolverInterface
22+
{
23+
public function resolve(array $target, array $relations): array
24+
{
25+
$relationMap = [];
26+
27+
foreach ($relations as $key => $relation) {
28+
Assert::keyExists($relation, 'uuid');
29+
Assert::uuid($relation['uuid']);
30+
$relationMap[$relation['uuid']] = $relation;
31+
32+
// There is a limit of possible resolvable relations.
33+
// @see https://www.storyblok.com/docs/api/content-delivery/v2/stories/retrieve-a-single-story
34+
if (50 === $key) {
35+
break;
36+
}
37+
}
38+
39+
foreach ($target as &$value) {
40+
if (\is_string($value) && \array_key_exists($value, $relationMap)) {
41+
$value = $relationMap[$value];
42+
43+
continue;
44+
}
45+
46+
if (\is_array($value)) {
47+
$value = $this->resolve($value, $relations);
48+
}
49+
}
50+
51+
return $target;
52+
}
53+
}

src/Response/StoriesResponse.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
public int $cv;
3030

3131
/**
32-
* @var list<string>
32+
* @var list<array<string, mixed>>
3333
*/
3434
public array $rels;
3535

src/Response/StoryResponse.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public int $cv;
2828

2929
/**
30-
* @var list<array<mixed>>
30+
* @var list<array<string, mixed>>
3131
*/
3232
public array $rels;
3333

0 commit comments

Comments
 (0)