diff --git a/README.md b/README.md index beb5cdd..20d5296 100644 --- a/README.md +++ b/README.md @@ -309,6 +309,38 @@ new OrFilter( ); ``` +#### Filtering by native date fields + +The Storyblok API allows filtering by published or updated dates. +Those fields could not be filtered by `gt_date` or `lt_date` operations and have dedicated parameters in the request. + +Supported date parameters are: + * `published_at_gt` + * `published_at_lt` + * `first_published_at_gt` + * `first_published_at_lt` + * `updated_at_gt` + * `updated_at_lt` + +Ensure that the dates used have to be in UTC timezone. + +```php +use Storyblok\Api\StoriesApi; +use Storyblok\Api\StoryblokClient; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtGt; +use Storyblok\Api\Request\StoriesRequest; + +$dateTime = new \DateTimeImmutable('1969-12-28 12:12:12.425', new \DateTimeZone('UTC')); + +$client = new StoryblokClient(/* ... */); + +$storiesApi = new StoriesApi($client); +$response = $storiesApi->all(new StoriesRequest( + language: 'de', + publishedAtGt: new PublishedAtGt($dateTime) +)); +``` + ### Get all available stories by Content Type (`string`) ```php diff --git a/src/Domain/Value/QueryParameter/FirstPublishedAtGt.php b/src/Domain/Value/QueryParameter/FirstPublishedAtGt.php new file mode 100644 index 0000000..f8981f1 --- /dev/null +++ b/src/Domain/Value/QueryParameter/FirstPublishedAtGt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class FirstPublishedAtGt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'first_published_at_gt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Domain/Value/QueryParameter/FirstPublishedAtLt.php b/src/Domain/Value/QueryParameter/FirstPublishedAtLt.php new file mode 100644 index 0000000..d3b4b8a --- /dev/null +++ b/src/Domain/Value/QueryParameter/FirstPublishedAtLt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class FirstPublishedAtLt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'first_published_at_lt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Domain/Value/QueryParameter/PublishedAtGt.php b/src/Domain/Value/QueryParameter/PublishedAtGt.php new file mode 100644 index 0000000..b21a6bf --- /dev/null +++ b/src/Domain/Value/QueryParameter/PublishedAtGt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class PublishedAtGt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'published_at_gt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Domain/Value/QueryParameter/PublishedAtLt.php b/src/Domain/Value/QueryParameter/PublishedAtLt.php new file mode 100644 index 0000000..9f87bb2 --- /dev/null +++ b/src/Domain/Value/QueryParameter/PublishedAtLt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class PublishedAtLt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'published_at_lt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Domain/Value/QueryParameter/UpdatedAtGt.php b/src/Domain/Value/QueryParameter/UpdatedAtGt.php new file mode 100644 index 0000000..b1acfb0 --- /dev/null +++ b/src/Domain/Value/QueryParameter/UpdatedAtGt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class UpdatedAtGt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'updated_at_gt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Domain/Value/QueryParameter/UpdatedAtLt.php b/src/Domain/Value/QueryParameter/UpdatedAtLt.php new file mode 100644 index 0000000..c3490c4 --- /dev/null +++ b/src/Domain/Value/QueryParameter/UpdatedAtLt.php @@ -0,0 +1,35 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Domain\Value\QueryParameter; + +/** + * @author Frank Stelzer + */ +final readonly class UpdatedAtLt +{ + public function __construct(private \DateTimeInterface $dateTime) + { + } + + public function getName(): string + { + return 'updated_at_lt'; + } + + public function toString(): string + { + return $this->dateTime->format('Y-m-d\TH:i:s.v\Z'); + } +} diff --git a/src/Request/StoriesRequest.php b/src/Request/StoriesRequest.php index 8463ed8..e034c1e 100644 --- a/src/Request/StoriesRequest.php +++ b/src/Request/StoriesRequest.php @@ -20,6 +20,12 @@ use Storyblok\Api\Domain\Value\Field\FieldCollection; use Storyblok\Api\Domain\Value\Filter\FilterCollection; use Storyblok\Api\Domain\Value\IdCollection; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtLt; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtLt; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtLt; use Storyblok\Api\Domain\Value\Resolver\RelationCollection; use Storyblok\Api\Domain\Value\Resolver\ResolveLinks; use Storyblok\Api\Domain\Value\Slug\Slug; @@ -49,6 +55,12 @@ public function __construct( public ResolveLinks $resolveLinks = new ResolveLinks(), public SlugCollection $excludeSlugs = new SlugCollection(), public ?Slug $startsWith = null, + public ?PublishedAtGt $publishedAtGt = null, + public ?PublishedAtLt $publishedAtLt = null, + public ?FirstPublishedAtGt $firstPublishedAtGt = null, + public ?FirstPublishedAtLt $firstPublishedAtLt = null, + public ?UpdatedAtGt $updatedAtGt = null, + public ?UpdatedAtLt $updatedAtLt = null, ) { Assert::stringNotEmpty($language); Assert::lessThanEq($this->pagination->perPage, self::MAX_PER_PAGE); @@ -71,6 +83,12 @@ public function __construct( * version?: string, * excluding_slugs?: string, * starts_with?: string, + * published_at_gt?: string, + * published_at_lt?: string, + * first_published_at_gt?: string, + * first_published_at_lt?: string, + * updated_at_gt?: string, + * updated_at_lt?: string, * } */ public function toArray(): array @@ -126,6 +144,30 @@ public function toArray(): array $array['starts_with'] = $this->startsWith->value; } + if (null !== $this->publishedAtGt) { + $array['published_at_gt'] = $this->publishedAtGt->toString(); + } + + if (null !== $this->publishedAtLt) { + $array['published_at_lt'] = $this->publishedAtLt->toString(); + } + + if (null !== $this->firstPublishedAtGt) { + $array['first_published_at_gt'] = $this->firstPublishedAtGt->toString(); + } + + if (null !== $this->firstPublishedAtLt) { + $array['first_published_at_lt'] = $this->firstPublishedAtLt->toString(); + } + + if (null !== $this->updatedAtGt) { + $array['updated_at_gt'] = $this->updatedAtGt->toString(); + } + + if (null !== $this->updatedAtLt) { + $array['updated_at_lt'] = $this->updatedAtLt->toString(); + } + return $array; } } diff --git a/tests/Unit/Domain/Type/AssetTest.php b/tests/Unit/Domain/Type/AssetTest.php index 688094e..51ceb83 100644 --- a/tests/Unit/Domain/Type/AssetTest.php +++ b/tests/Unit/Domain/Type/AssetTest.php @@ -285,17 +285,6 @@ public function orientation(Orientation $expected, int $width, int $height): voi self::assertTrue($expected->equals((new Asset($response))->orientation)); } - #[Test] - public function orientationWithNoImage(): void - { - $faker = self::faker(); - $response = $faker->storyAssetResponse([ - 'filename' => $faker->url(), - ]); - - self::assertTrue(Orientation::Unknown->equals((new Asset($response))->orientation)); - } - /** * @return iterable */ @@ -309,6 +298,17 @@ public static function orientationProvider(): iterable yield 'portrait' => [Orientation::Portrait, 1080, 1920]; } + #[Test] + public function orientationWithNoImage(): void + { + $faker = self::faker(); + $response = $faker->storyAssetResponse([ + 'filename' => $faker->url(), + ]); + + self::assertTrue(Orientation::Unknown->equals((new Asset($response))->orientation)); + } + #[Test] public function isExternalUrl(): void { diff --git a/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtGtTest.php b/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtGtTest.php new file mode 100644 index 0000000..a00908c --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtGtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtGt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class FirstPublishedAtGtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new FirstPublishedAtGt($value); + self::assertSame('first_published_at_gt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtLtTest.php b/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtLtTest.php new file mode 100644 index 0000000..64b089c --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/FirstPublishedAtLtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtLt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class FirstPublishedAtLtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new FirstPublishedAtLt($value); + self::assertSame('first_published_at_lt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Domain/Value/QueryParameter/PublishedAtGtTest.php b/tests/Unit/Domain/Value/QueryParameter/PublishedAtGtTest.php new file mode 100644 index 0000000..0d98d56 --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/PublishedAtGtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtGt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class PublishedAtGtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new PublishedAtGt($value); + self::assertSame('published_at_gt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Domain/Value/QueryParameter/PublishedAtLtTest.php b/tests/Unit/Domain/Value/QueryParameter/PublishedAtLtTest.php new file mode 100644 index 0000000..2f1b5d6 --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/PublishedAtLtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtLt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class PublishedAtLtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new PublishedAtLt($value); + self::assertSame('published_at_lt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Domain/Value/QueryParameter/UpdatedAtGtTest.php b/tests/Unit/Domain/Value/QueryParameter/UpdatedAtGtTest.php new file mode 100644 index 0000000..a3b4f97 --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/UpdatedAtGtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtGt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class UpdatedAtGtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new UpdatedAtGt($value); + self::assertSame('updated_at_gt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Domain/Value/QueryParameter/UpdatedAtLtTest.php b/tests/Unit/Domain/Value/QueryParameter/UpdatedAtLtTest.php new file mode 100644 index 0000000..1f93ce2 --- /dev/null +++ b/tests/Unit/Domain/Value/QueryParameter/UpdatedAtLtTest.php @@ -0,0 +1,39 @@ + + * in cooperation with SensioLabs Deutschland + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Storyblok\Api\Tests\Unit\Domain\Value\QueryParameter; + +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\TestCase; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtLt; +use Storyblok\Api\Tests\Util\FakerTrait; + +/** + * @author Frank Stelzer + */ +class UpdatedAtLtTest extends TestCase +{ + use FakerTrait; + + #[Test] + public function valueToString(): void + { + $value = self::faker()->dateTime(); + $expectedValue = $value->format('Y-m-d\TH:i:s.v\Z'); + + $valueObject = new UpdatedAtLt($value); + self::assertSame('updated_at_lt', $valueObject->getName()); + self::assertSame($expectedValue, $valueObject->toString()); + } +} diff --git a/tests/Unit/Request/StoriesRequestTest.php b/tests/Unit/Request/StoriesRequestTest.php index 456fcf1..c8dda1f 100644 --- a/tests/Unit/Request/StoriesRequestTest.php +++ b/tests/Unit/Request/StoriesRequestTest.php @@ -25,6 +25,12 @@ use Storyblok\Api\Domain\Value\Filter\Filters\InFilter; use Storyblok\Api\Domain\Value\Id; use Storyblok\Api\Domain\Value\IdCollection; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\FirstPublishedAtLt; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\PublishedAtLt; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtGt; +use Storyblok\Api\Domain\Value\QueryParameter\UpdatedAtLt; use Storyblok\Api\Domain\Value\Resolver\Relation; use Storyblok\Api\Domain\Value\Resolver\RelationCollection; use Storyblok\Api\Domain\Value\Slug\Slug; @@ -40,6 +46,7 @@ final class StoriesRequestTest extends TestCase { use FakerTrait; + private const string EXPECTED_DATE_TIME_FORMAT = 'Y-m-d\TH:i:s.v\Z'; #[Test] public function toArrayWithDefaults(): void @@ -221,4 +228,124 @@ public function toArrayStartsWith(): void 'starts_with' => 'my/path', ], $request->toArray()); } + + #[Test] + public function toArrayWithPublishedAtGt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + publishedAtGt: new PublishedAtGt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'published_at_gt' => $expectedDate, + ], $request->toArray()); + } + + #[Test] + public function toArrayWithPublishedAtLt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + publishedAtLt: new PublishedAtLt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'published_at_lt' => $expectedDate, + ], $request->toArray()); + } + + #[Test] + public function toArrayWithFirstPublishedAtGt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + firstPublishedAtGt: new FirstPublishedAtGt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'first_published_at_gt' => $expectedDate, + ], $request->toArray()); + } + + #[Test] + public function toArrayWithFirstPublishedAtLt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + firstPublishedAtLt: new FirstPublishedAtLt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'first_published_at_lt' => $expectedDate, + ], $request->toArray()); + } + + #[Test] + public function toArrayWithUpdatedAtGt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + updatedAtGt: new UpdatedAtGt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'updated_at_gt' => $expectedDate, + ], $request->toArray()); + } + + #[Test] + public function toArrayWithUpdatedAtLt(): void + { + $faker = self::faker(); + + $date = $faker->dateTime(); + $expectedDate = $date->format(self::EXPECTED_DATE_TIME_FORMAT); + + $request = new StoriesRequest( + updatedAtLt: new UpdatedAtLt($date), + ); + + self::assertSame([ + 'language' => 'default', + 'page' => 1, + 'per_page' => 25, + 'updated_at_lt' => $expectedDate, + ], $request->toArray()); + } }