Skip to content

Commit 514bc97

Browse files
authored
feat: Introduce excludeSlugs option for StoriesRequest (#36)
1 parent ea245c8 commit 514bc97

File tree

7 files changed

+406
-4
lines changed

7 files changed

+406
-4
lines changed

phpstan-baseline.neon

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,83 @@
11
parameters:
22
ignoreErrors:
3+
-
4+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:assetResponse\(\) should return array\{asset\: array\{filename\: string, created_at\: string, updated_at\: string, expire_at\: string\|null, content_length\: int, signed_url\: string, content_type\: string\}\} but returns array\.$#'
5+
identifier: return.type
6+
count: 1
7+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
8+
9+
-
10+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:datasourceDimensionResponse\(\) should return array\{id\: int, name\: string, entry_value\: string, datasource_id\: int, created_at\: string, updated_at\: string\} but returns array\.$#'
11+
identifier: return.type
12+
count: 1
13+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
14+
15+
-
16+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:datasourceEntriesResponse\(\) should return array\{datasource_entries\: list\<array\{id\: int, name\: string, value\: string, dimension_value\: string\|null\}\>\} but returns array\.$#'
17+
identifier: return.type
18+
count: 1
19+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
20+
21+
-
22+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:datasourceEntryResponse\(\) should return array\{id\: int, name\: string, value\: string, dimension_value\: string\|null\} but returns array\.$#'
23+
identifier: return.type
24+
count: 1
25+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
26+
27+
-
28+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:datasourceResponse\(\) should return array\{id\: int, name\: string, slug\: string, dimensions\: list\<array\{id\: int, name\: string, entry_value\: string, datasource_id\: int, created_at\: string, updated_at\: string\}\>\} but returns array\.$#'
29+
identifier: return.type
30+
count: 1
31+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
32+
33+
-
34+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:datasourcesResponse\(\) should return array\{datasources\: list\<array\<string, mixed\>\>\} but returns array\.$#'
35+
identifier: return.type
36+
count: 1
37+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
38+
39+
-
40+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:linkAlternateResponse\(\) should return array\{lang\: string, name\: string, path\: string, published\: bool, translated_slug\: string\} but returns array\.$#'
41+
identifier: return.type
42+
count: 1
43+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
44+
45+
-
46+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:linkResponse\(\) should return array\{uuid\: string, id\: int, parent_id\: int\|null, name\: string, slug\: string, path\: string, real_path\: string, is_folder\: bool, \.\.\.\} but returns array\.$#'
47+
identifier: return.type
48+
count: 1
49+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
50+
51+
-
52+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:linksResponse\(\) should return array\{links\: list\<array\<string, mixed\>\>\} but returns array\.$#'
53+
identifier: return.type
54+
count: 1
55+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
56+
57+
-
58+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:spaceResponse\(\) should return array\{id\: int, name\: string, domain\: string, version\: int, language_codes\: list\<string\>\} but returns array\.$#'
59+
identifier: return.type
60+
count: 1
61+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
62+
63+
-
64+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:storiesResponse\(\) should return array\{cv\: int, stories\: list\<array\<string, mixed\>\>, links\: array\<string\>, rels\: array\<string\>\} but returns array\.$#'
65+
identifier: return.type
66+
count: 1
67+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
68+
69+
-
70+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:storyResponse\(\) should return array\{cv\: int, story\: array\<string, mixed\>, links\: array\<string\>, rels\: array\<string\>\} but returns array\.$#'
71+
identifier: return.type
72+
count: 1
73+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
74+
75+
-
76+
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider\:\:tagsResponse\(\) should return array\{tags\: list\<array\{name\: string, taggings_count\: int\}\>\} but returns array\.$#'
77+
identifier: return.type
78+
count: 1
79+
path: src/Bridge/Faker/Provider/StoryblokProvider.php
80+
381
-
482
message: '#^Property Storyblok\\Api\\Domain\\Value\\Datasource\:\:\$dimensions \(list\<Storyblok\\Api\\Domain\\Value\\DatasourceDimension\>\) does not accept array\<Storyblok\\Api\\Domain\\Value\\DatasourceDimension\>\.$#'
583
identifier: assign.propertyType
@@ -42,6 +120,12 @@ parameters:
42120
count: 1
43121
path: src/Domain/Value/Resolver/RelationCollection.php
44122

123+
-
124+
message: '#^Property Storyblok\\Api\\Domain\\Value\\Slug\\SlugCollection\:\:\$items \(list\<Storyblok\\Api\\Domain\\Value\\Slug\\Slug\>\) does not accept array\<int\<0, max\>, Storyblok\\Api\\Domain\\Value\\Slug\\Slug\>\.$#'
125+
identifier: assign.propertyType
126+
count: 1
127+
path: src/Domain/Value/Slug/SlugCollection.php
128+
45129
-
46130
message: '#^Property Storyblok\\Api\\Domain\\Value\\Tag\\TagCollection\:\:\$items \(list\<Storyblok\\Api\\Domain\\Value\\Tag\\Tag\>\) does not accept array\<int\<0, max\>, Storyblok\\Api\\Domain\\Value\\Tag\\Tag\>\.$#'
47131
identifier: assign.propertyType
@@ -103,11 +187,13 @@ parameters:
103187
path: tests/Unit/Domain/Value/Resolver/RelationCollectionTest.php
104188

105189
-
106-
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertEmpty\(\) with Storyblok\\Api\\Domain\\Value\\Tag\\TagCollection will always evaluate to false\.$#'
190+
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertEmpty\(\) with Storyblok\\Api\\Domain\\Value\\Slug\\SlugCollection will always evaluate to false\.$#'
107191
identifier: staticMethod.impossibleType
108192
count: 2
109-
path: tests/Unit/Domain/Value/Tag/TagCollectionTest.php
193+
path: tests/Unit/Domain/Value/Slug/SlugCollectionTest.php
110194

111195
-
112-
message: '#^Method Storyblok\\Api\\Bridge\\Faker\\Provider\\StoryblokProvider::.+ should return array.+ but returns array\.$#'
113-
path: src/Bridge/Faker/Provider/StoryblokProvider.php
196+
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertEmpty\(\) with Storyblok\\Api\\Domain\\Value\\Tag\\TagCollection will always evaluate to false\.$#'
197+
identifier: staticMethod.impossibleType
198+
count: 2
199+
path: tests/Unit/Domain/Value/Tag/TagCollectionTest.php

src/Domain/Value/Slug/Slug.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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\Slug;
15+
16+
use OskarStark\Value\TrimmedNonEmptyString;
17+
18+
/**
19+
* @author Silas Joisten <[email protected]>
20+
*/
21+
final readonly class Slug
22+
{
23+
public function __construct(
24+
public string $value,
25+
) {
26+
TrimmedNonEmptyString::fromString($value);
27+
}
28+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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\Slug;
15+
16+
/**
17+
* @author Silas Joisten <[email protected]>
18+
*
19+
* @implements \IteratorAggregate<int, Slug>
20+
*/
21+
final class SlugCollection implements \Countable, \IteratorAggregate
22+
{
23+
/**
24+
* @var list<Slug>
25+
*/
26+
private array $items = [];
27+
28+
/**
29+
* @param list<Slug|string> $items
30+
*/
31+
public function __construct(
32+
array $items = [],
33+
) {
34+
foreach ($items as $item) {
35+
if (\is_string($item)) {
36+
$item = new Slug($item);
37+
}
38+
39+
$this->add($item);
40+
}
41+
}
42+
43+
/**
44+
* @return \Traversable<int, Slug>
45+
*/
46+
public function getIterator(): \Traversable
47+
{
48+
return new \ArrayIterator($this->items);
49+
}
50+
51+
public function count(): int
52+
{
53+
return \count($this->items);
54+
}
55+
56+
public function add(Slug $field): void
57+
{
58+
if ($this->has($field)) {
59+
return;
60+
}
61+
62+
$this->items[] = $field;
63+
}
64+
65+
public function has(Slug $field): bool
66+
{
67+
foreach ($this->items as $item) {
68+
if ($item->value === $field->value) {
69+
return true;
70+
}
71+
}
72+
73+
return false;
74+
}
75+
76+
public function remove(Slug $field): void
77+
{
78+
foreach ($this->items as $key => $item) {
79+
if ($item->value === $field->value) {
80+
unset($this->items[$key]);
81+
82+
break;
83+
}
84+
}
85+
}
86+
87+
public function toString(): string
88+
{
89+
return implode(',', array_map(static fn (Slug $slug): string => $slug->value, $this->items));
90+
}
91+
}

src/Request/StoriesRequest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Storyblok\Api\Domain\Value\IdCollection;
2222
use Storyblok\Api\Domain\Value\Resolver\RelationCollection;
2323
use Storyblok\Api\Domain\Value\Resolver\ResolveLinks;
24+
use Storyblok\Api\Domain\Value\Slug\SlugCollection;
2425
use Storyblok\Api\Domain\Value\Tag\TagCollection;
2526
use Webmozart\Assert\Assert;
2627

@@ -44,6 +45,7 @@ public function __construct(
4445
public ?Version $version = null,
4546
public ?string $searchTerm = null,
4647
public ResolveLinks $resolveLinks = new ResolveLinks(),
48+
public SlugCollection $excludeSlugs = new SlugCollection(),
4749
) {
4850
Assert::stringNotEmpty($language);
4951
Assert::lessThanEq($this->pagination->perPage, self::MAX_PER_PAGE);
@@ -64,6 +66,7 @@ public function __construct(
6466
* resolve_links_level?: string,
6567
* search_term?: string,
6668
* version?: string,
69+
* excluding_slugs?: string,
6770
* }
6871
*/
6972
public function toArray(): array
@@ -111,6 +114,10 @@ public function toArray(): array
111114
$array['version'] = $this->version->value;
112115
}
113116

117+
if ($this->excludeSlugs->count() > 0) {
118+
$array['excluding_slugs'] = $this->excludeSlugs->toString();
119+
}
120+
114121
return $array;
115122
}
116123
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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\Tests\Unit\Domain\Value\Slug;
15+
16+
use PHPUnit\Framework\Attributes\Test;
17+
use PHPUnit\Framework\TestCase;
18+
use Storyblok\Api\Domain\Value\Slug\Slug;
19+
use Storyblok\Api\Domain\Value\Slug\SlugCollection;
20+
use Storyblok\Api\Tests\Util\FakerTrait;
21+
22+
/**
23+
* @author Silas Joisten <[email protected]>
24+
*/
25+
final class SlugCollectionTest extends TestCase
26+
{
27+
use FakerTrait;
28+
29+
#[Test]
30+
public function constructWithString(): void
31+
{
32+
$faker = self::faker();
33+
$collection = new SlugCollection([$faker->word(), $faker->word()]);
34+
35+
self::assertCount(2, $collection);
36+
self::assertContainsOnlyInstancesOf(Slug::class, $collection);
37+
}
38+
39+
#[Test]
40+
public function add(): void
41+
{
42+
$faker = self::faker();
43+
44+
$collection = new SlugCollection();
45+
self::assertEmpty($collection);
46+
47+
$collection->add(new Slug($faker->word()));
48+
self::assertCount(1, $collection);
49+
}
50+
51+
#[Test]
52+
public function remove(): void
53+
{
54+
$faker = self::faker();
55+
56+
$slug = new Slug($faker->word());
57+
58+
$collection = new SlugCollection([$slug]);
59+
self::assertCount(1, $collection);
60+
61+
$collection->remove($slug);
62+
self::assertEmpty($collection);
63+
}
64+
65+
#[Test]
66+
public function hasReturnsTrue(): void
67+
{
68+
$faker = self::faker();
69+
70+
$slug = new Slug($faker->word());
71+
72+
$collection = new SlugCollection([$slug, new Slug($faker->word())]);
73+
74+
self::assertTrue($collection->has($slug));
75+
}
76+
77+
#[Test]
78+
public function hasReturnsFalse(): void
79+
{
80+
$faker = self::faker();
81+
82+
$collection = new SlugCollection([new Slug($faker->word())]);
83+
84+
self::assertFalse($collection->has(new Slug($faker->word())));
85+
}
86+
87+
#[Test]
88+
public function isCountable(): void
89+
{
90+
$faker = self::faker();
91+
92+
$slug = new Slug($faker->word());
93+
94+
$collection = new SlugCollection([$slug]);
95+
96+
self::assertCount(1, $collection);
97+
}
98+
99+
#[Test]
100+
public function toStringMethod(): void
101+
{
102+
$slugs = [
103+
new Slug('foo/*'),
104+
new Slug('test/bar'),
105+
new Slug('baz/bar/*'),
106+
];
107+
108+
$collection = new SlugCollection($slugs);
109+
110+
self::assertSame('foo/*,test/bar,baz/bar/*', $collection->toString());
111+
}
112+
113+
#[Test]
114+
public function getIterator(): void
115+
{
116+
$slugs = [
117+
new Slug('foo'),
118+
new Slug('bar/baz'),
119+
];
120+
121+
self::assertInstanceOf(\ArrayIterator::class, (new SlugCollection($slugs))->getIterator());
122+
}
123+
}

0 commit comments

Comments
 (0)