Skip to content

Commit 9916a07

Browse files
committed
feat(stories): Add published_at_gt and other date related query parameters (#64)
1 parent 006cdab commit 9916a07

File tree

9 files changed

+494
-11
lines changed

9 files changed

+494
-11
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,37 @@ new OrFilter(
309309
);
310310
```
311311

312+
#### Filtering by native date fields
313+
314+
The Storyblok API allows filtering by publish or updated dates.
315+
Those fields could not be filtered by `gt_date` or `lt_date` operations and have dedicated parameters in the request.
316+
317+
Supported date parameters are:
318+
* `published_at_gt`
319+
* `published_at_lt`
320+
* `first_published_at_gt`
321+
* `first_published_at_lt`
322+
* `updated_at_gt`
323+
* `updated_at_lt`
324+
325+
```php
326+
use Storyblok\Api\StoriesApi;
327+
use Storyblok\Api\StoryblokClient;
328+
use Storyblok\Api\Domain\Value\QueryParameter\DateQueryParameter;
329+
use Storyblok\Api\Domain\Value\QueryParameter\QueryParameterCollection;
330+
use Storyblok\Api\Request\StoriesRequest;
331+
332+
$client = new StoryblokClient(/* ... */);
333+
334+
$storiesApi = new StoriesApi($client);
335+
$response = $storiesApi->all(new StoriesRequest(
336+
language: 'de',
337+
queryParameterCollection: new QueryParameterCollection([
338+
new DateQueryParameter(DateQueryParameter::PUBLISHED_AT_GT, '<YYYY-MM-DD H:i>')
339+
])
340+
));
341+
```
342+
312343
### Get all available stories by Content Type (`string`)
313344

314345
```php
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of storyblok/php-content-api-client.
7+
*
8+
* (c) Storyblok GmbH <[email protected]>
9+
* in cooperation with SensioLabs Deutschland <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Storyblok\Api\Domain\Value\QueryParameter;
16+
17+
use Webmozart\Assert\Assert;
18+
19+
/**
20+
* Represents top level query parameters which are holding simple string values.
21+
*
22+
* @author Frank Stelzer <[email protected]>
23+
*/
24+
final readonly class DateQueryParameter extends QueryParameter
25+
{
26+
public const string PUBLISHED_AT_GT = 'published_at_gt';
27+
public const string PUBLISHED_AT_LT = 'published_at_lt';
28+
public const string FIRST_PUBLISHED_AT_GT = 'first_published_at_gt';
29+
public const string FIRST_PUBLISHED_AT_LT = 'first_published_at_lt';
30+
public const string UPDATED_AT_GT = 'updated_at_gt';
31+
public const string UPDATED_AT_LT = 'updated_at_lt';
32+
33+
protected function validateName(string $name): void
34+
{
35+
Assert::oneOf($name, [
36+
self::PUBLISHED_AT_GT,
37+
self::PUBLISHED_AT_LT,
38+
self::FIRST_PUBLISHED_AT_GT,
39+
self::FIRST_PUBLISHED_AT_LT,
40+
self::UPDATED_AT_GT,
41+
self::UPDATED_AT_LT,
42+
]);
43+
}
44+
45+
protected function validateValue(string $value): void
46+
{
47+
parent::validateValue($value);
48+
49+
Assert::regex(
50+
$value,
51+
'/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/',
52+
\sprintf('The date must be in the format YYYY-MM-DD HH:MM, given: %s', $value),
53+
);
54+
}
55+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of storyblok/php-content-api-client.
7+
*
8+
* (c) Storyblok GmbH <[email protected]>
9+
* in cooperation with SensioLabs Deutschland <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Storyblok\Api\Domain\Value\QueryParameter;
16+
17+
use OskarStark\Value\TrimmedNonEmptyString;
18+
19+
/**
20+
* Represents top level query parameters which are holding simple string values.
21+
*
22+
* @author Frank Stelzer <[email protected]>
23+
*/
24+
abstract readonly class QueryParameter
25+
{
26+
public function __construct(
27+
public string $name,
28+
public string $value,
29+
) {
30+
$this->validateName($name);
31+
$this->validateValue($value);
32+
}
33+
34+
abstract protected function validateName(string $name): void;
35+
36+
protected function validateValue(string $value): void
37+
{
38+
TrimmedNonEmptyString::fromString($value);
39+
}
40+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of storyblok/php-content-api-client.
7+
*
8+
* (c) Storyblok GmbH <[email protected]>
9+
* in cooperation with SensioLabs Deutschland <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Storyblok\Api\Domain\Value\QueryParameter;
16+
17+
/**
18+
* @author Frank Stelzer <[email protected]>
19+
* @author Silas Joisten <[email protected]>
20+
*
21+
* @implements \IteratorAggregate<int, QueryParameter>
22+
*/
23+
final class QueryParameterCollection implements \Countable, \IteratorAggregate
24+
{
25+
/**
26+
* @var QueryParameter[]
27+
*/
28+
private array $items = [];
29+
30+
/**
31+
* @param QueryParameter[] $items
32+
*/
33+
public function __construct(
34+
array $items = [],
35+
) {
36+
foreach ($items as $item) {
37+
$this->add($item);
38+
}
39+
}
40+
41+
/**
42+
* @return \Traversable<int, QueryParameter>
43+
*/
44+
public function getIterator(): \Traversable
45+
{
46+
return new \ArrayIterator($this->items);
47+
}
48+
49+
public function count(): int
50+
{
51+
return \count($this->items);
52+
}
53+
54+
public function add(QueryParameter $queryParameter): void
55+
{
56+
if ($this->has($queryParameter)) {
57+
return;
58+
}
59+
60+
$this->items[] = $queryParameter;
61+
}
62+
63+
public function has(QueryParameter $queryParameter): bool
64+
{
65+
foreach ($this->items as $item) {
66+
if ($item->name === $queryParameter->name) {
67+
return true;
68+
}
69+
}
70+
71+
return false;
72+
}
73+
74+
public function remove(QueryParameter $queryParameter): void
75+
{
76+
foreach ($this->items as $key => $item) {
77+
if ($item->name === $queryParameter->name) {
78+
unset($this->items[$key]);
79+
80+
break;
81+
}
82+
}
83+
}
84+
85+
/**
86+
* @return array{
87+
* published_at_gt?: string,
88+
* published_at_lt?: string,
89+
* first_published_at_gt?: string,
90+
* first_published_at_lt?: string,
91+
* updated_at_gt?: string,
92+
* updated_at_lt?: string,
93+
* }
94+
*/
95+
public function toArray(): array
96+
{
97+
$result = [];
98+
99+
foreach ($this->items as $queryParameter) {
100+
$result[$queryParameter->name] = $queryParameter->value;
101+
}
102+
103+
return $result;
104+
}
105+
}

src/Request/StoriesRequest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Storyblok\Api\Domain\Value\Field\FieldCollection;
2121
use Storyblok\Api\Domain\Value\Filter\FilterCollection;
2222
use Storyblok\Api\Domain\Value\IdCollection;
23+
use Storyblok\Api\Domain\Value\QueryParameter\QueryParameterCollection;
2324
use Storyblok\Api\Domain\Value\Resolver\RelationCollection;
2425
use Storyblok\Api\Domain\Value\Resolver\ResolveLinks;
2526
use Storyblok\Api\Domain\Value\Slug\Slug;
@@ -49,6 +50,8 @@ public function __construct(
4950
public ResolveLinks $resolveLinks = new ResolveLinks(),
5051
public SlugCollection $excludeSlugs = new SlugCollection(),
5152
public ?Slug $startsWith = null,
53+
public QueryParameterCollection $queryParameterCollection = new QueryParameterCollection(
54+
),
5255
) {
5356
Assert::stringNotEmpty($language);
5457
Assert::lessThanEq($this->pagination->perPage, self::MAX_PER_PAGE);
@@ -71,6 +74,12 @@ public function __construct(
7174
* version?: string,
7275
* excluding_slugs?: string,
7376
* starts_with?: string,
77+
* published_at_gt?: string,
78+
* published_at_lt?: string,
79+
* first_published_at_gt?: string,
80+
* first_published_at_lt?: string,
81+
* updated_at_gt?: string,
82+
* updated_at_lt?: string,
7483
* }
7584
*/
7685
public function toArray(): array
@@ -126,6 +135,10 @@ public function toArray(): array
126135
$array['starts_with'] = $this->startsWith->value;
127136
}
128137

138+
if ($this->queryParameterCollection->count() > 0) {
139+
$array = array_merge($array, $this->queryParameterCollection->toArray());
140+
}
141+
129142
return $array;
130143
}
131144
}

tests/Unit/Domain/Type/AssetTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -285,17 +285,6 @@ public function orientation(Orientation $expected, int $width, int $height): voi
285285
self::assertTrue($expected->equals((new Asset($response))->orientation));
286286
}
287287

288-
#[Test]
289-
public function orientationWithNoImage(): void
290-
{
291-
$faker = self::faker();
292-
$response = $faker->storyAssetResponse([
293-
'filename' => $faker->url(),
294-
]);
295-
296-
self::assertTrue(Orientation::Unknown->equals((new Asset($response))->orientation));
297-
}
298-
299288
/**
300289
* @return iterable<string, array{0: Orientation, 1: int, 2: int}>
301290
*/
@@ -309,6 +298,17 @@ public static function orientationProvider(): iterable
309298
yield 'portrait' => [Orientation::Portrait, 1080, 1920];
310299
}
311300

301+
#[Test]
302+
public function orientationWithNoImage(): void
303+
{
304+
$faker = self::faker();
305+
$response = $faker->storyAssetResponse([
306+
'filename' => $faker->url(),
307+
]);
308+
309+
self::assertTrue(Orientation::Unknown->equals((new Asset($response))->orientation));
310+
}
311+
312312
#[Test]
313313
public function isExternalUrl(): void
314314
{
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of storyblok/php-content-api-client.
7+
*
8+
* (c) Storyblok GmbH <[email protected]>
9+
* in cooperation with SensioLabs Deutschland <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter;
16+
17+
use PHPUnit\Framework\Attributes\DataProvider;
18+
use PHPUnit\Framework\Attributes\Test;
19+
use PHPUnit\Framework\TestCase;
20+
use Storyblok\Api\Domain\Value\QueryParameter\DateQueryParameter;
21+
use Storyblok\Api\Tests\Util\FakerTrait;
22+
23+
/**
24+
* @author Frank Stelzer <[email protected]>
25+
* @author Silas Joisten <[email protected]>
26+
*/
27+
class DateQueryParameterTest extends TestCase
28+
{
29+
use FakerTrait;
30+
31+
#[DataProvider('parametersProvider')]
32+
#[Test]
33+
public function constructValid(string $parameterName): void
34+
{
35+
$value = self::faker()->dateTime()->format('Y-m-d H:i');
36+
37+
$valueObject = new DateQueryParameter($parameterName, $value);
38+
self::assertSame($parameterName, $valueObject->name);
39+
self::assertSame($value, $valueObject->value);
40+
}
41+
42+
/**
43+
* @return iterable<string, array{0: string}>
44+
*/
45+
public static function parametersProvider(): iterable
46+
{
47+
yield 'published_at_gt' => ['published_at_gt'];
48+
yield 'published_at_lt' => ['published_at_lt'];
49+
yield 'first_published_at_gt' => ['first_published_at_gt'];
50+
yield 'first_published_at_lt' => ['first_published_at_lt'];
51+
yield 'updated_at_gt' => ['updated_at_gt'];
52+
yield 'updated_at_lt' => ['updated_at_lt'];
53+
}
54+
55+
#[Test]
56+
public function constructorInvalidName(): void
57+
{
58+
$name = self::faker()->word();
59+
$value = self::faker()->dateTime()->format('Y-m-d H:i');
60+
61+
self::expectException(\InvalidArgumentException::class);
62+
63+
new DateQueryParameter($name, $value);
64+
}
65+
66+
#[Test]
67+
public function constructorInvalidValue(): void
68+
{
69+
$value = self::faker()->word();
70+
71+
self::expectException(\InvalidArgumentException::class);
72+
73+
new DateQueryParameter(DateQueryParameter::PUBLISHED_AT_GT, $value);
74+
}
75+
}

0 commit comments

Comments
 (0)