Skip to content

storyblok/php-content-api-client

Storyblok PHP Content API Client

Storyblok Content Delivery API Client

Co-created with SensioLabs, the creators of Symfony.

BranchPHPCode Coverage
masterPHPcodecov

Symfony

Use the storyblok/storyblok-bundle to integrate this library into your Symfony application.

Usage

Installation

composer require storyblok/php-content-api-client

Setup

useStoryblok\Api\StoryblokClient; $client = newStoryblokClient( baseUri: 'https://api.storyblok.com', token: '***********', timeout: 10// optional ); // you can now request any endpoint which needs authentication$client->request('GET', '/api/something', $options);

Spaces

In your code you should type-hint to Storyblok\Api\SpacesApiInterface

Get the current space

Returns the space associated with the current token.

useStoryblok\Api\SpacesApi; useStoryblok\Api\StoryblokClient; $client = newStoryblokClient(/* ... */); $spacesApi = newSpacesApi($client); $response = $spacesApi->me();

Stories

In your code you should type-hint to Storyblok\Api\StoriesApiInterface

Get all available stories

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Request\StoriesRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->all(newStoriesRequest(language: 'de'));

Fetch by Version (draft, published)

Global

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Dto\Version; useStoryblok\Api\Request\StoryRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client, Version::Draft); $response = $storiesApi->bySlug('/my-story/', newStoryRequest( language: 'de', ));

Method Call

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Dto\Version; useStoryblok\Api\Request\StoryRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client, Version::Published); $response = $storiesApi->bySlug('/my-story/', newStoryRequest( language: 'de', version: Version::Draft, // This overrides the global "version" ));

Pagination

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Dto\Pagination; useStoryblok\Api\Request\StoriesRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->all(newStoriesRequest( language: 'de', pagination: newPagination(page: 1, perPage: 30) ));

Sorting

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Dto\SortBy; useStoryblok\Api\Domain\Value\Dto\Direction; useStoryblok\Api\Request\StoriesRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->all(newStoriesRequest( language: 'de', sortBy: newSortBy(field: 'title', direction: Direction::Desc) ));

Filtering

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Filter\FilterCollection; useStoryblok\Api\Domain\Value\Dto\Direction; useStoryblok\Api\Domain\Value\Filter\Filters\InFilter; useStoryblok\Api\Request\StoriesRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->all(newStoriesRequest( language: 'de', filters: newFilterCollection([ newInFilter(field: 'single_reference_field', value: 'f2fdb571-a265-4d8a-b7c5-7050d23c2383') ]) ));

Available filters

AllInArrayFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\AllInArrayFilter; newAllInArrayFilter(field: 'tags', value: ['foo', 'bar', 'baz']);

AnyInArrayFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\AnyInArrayFilter; newAnyInArrayFilter(field: 'tags', value: ['foo', 'bar', 'baz']);

GreaterThanDateFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\GreaterThanDateFilter; newGreaterThanDateFilter(field: 'created_at', value: new \DateTimeImmutable());

LessThanDateFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\LessThanDateFilter; newLessThanDateFilter(field: 'created_at', value: new \DateTimeImmutable());

GreaterThanFloatFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\GreaterThanFloatFilter; newGreaterThanFloatFilter(field: 'price', value: 39.99);

LessThanFloatFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\LessThanFloatFilter; newLessThanFloatFilter(field: 'price', value: 199.99);

GreaterThanIntFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\GreaterThanIntFilter; newGreaterThanIntFilter(field: 'stock', value: 0);

LessThanIntFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\LessThanIntFilter; newLessThanIntFilter(field: 'stock', value: 100);

InFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\InFilter; newInFilter(field: 'text', value: 'Hello World!'); // ornewInFilter(field: 'text', value: ['Hello Symfony!', 'Hello SensioLabs!']);

NotInFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\NotInFilter; newNotInFilter(field: 'text', value: 'Hello World!'); // ornewNotInFilter(field: 'text', value: ['Bye Symfony!', 'Bye SensioLabs!']);

IsFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\IsFilter; // You can use one of the following constants:// IsFilter::EMPTY_ARRAY// IsFilter::NOT_EMPTY_ARRAY// IsFilter::EMPTY// IsFilter::NOT_EMPTY// IsFilter::TRUE// IsFilter::FALSE// IsFilter::NULL// IsFilter::NOT_NULLnewIsFilter(field: 'text', value: IsFilter::EMPTY);

LikeFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\LikeFilter; newLikeFilter(field: 'description', value: '*I love Symfony*');

NotLikeFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\NotLikeFilter; newNotLikeFilter(field: 'description', value: '*Text*');

OrFilter.php

Example:

useStoryblok\Api\Domain\Value\Filter\Filters\OrFilter; useStoryblok\Api\Domain\Value\Filter\Filters\LikeFilter; useStoryblok\Api\Domain\Value\Filter\Filters\NotLikeFilter; newOrFilter( newLikeFilter(field: 'text', value: 'Yes!*'), newLikeFilter(field: 'text', value: 'Maybe!*'), // ... );

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.

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\QueryParameter\PublishedAtGt; useStoryblok\Api\Request\StoriesRequest; $dateTime = new \DateTimeImmutable('1969-12-28 12:12:12.425', new \DateTimeZone('UTC')); $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->all(newStoriesRequest( language: 'de', publishedAtGt: newPublishedAtGt($dateTime) ));

Get all available stories by Content Type (string)

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Request\StoriesRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->allByContentType('custom_content_type', newStoriesRequest( language: 'de', ));

Get multiple stories by multiple uuid's (array)

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Request\StoriesRequest; useStoryblok\Api\Domain\Value\Uuid; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->allByUuids([newUuid(/** ... */), newUuid(/** ... */)], newStoriesRequest( language: 'de', ));

Get by uuid (Storyblok\Api\Domain\Value\Uuid)

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Uuid; useStoryblok\Api\Request\StoryRequest; $uuid = newUuid(/** ... */); $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->byUuid($uuid, newStoryRequest( language: 'de', ));

Get by slug (string)

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Request\StoryRequest; $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->bySlug('folder/slug', newStoryRequest( language: 'de', ));

Get by id (Storyblok\Api\Domain\Value\Id)

useStoryblok\Api\StoriesApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Id; useStoryblok\Api\Request\StoryRequest; $id = newId(/** ... */); $client = newStoryblokClient(/* ... */); $storiesApi = newStoriesApi($client); $response = $storiesApi->byId($id, newStoryRequest( language: 'de', ));

Links

In your code you should type-hint to Storyblok\Api\LinksApiInterface

Get all available links

useStoryblok\Api\LinksApi; useStoryblok\Api\StoryblokClient; $client = newStoryblokClient(/* ... */); $linksApi = newLinksApi($client); $response = $linksApi->all();

Pagination

useStoryblok\Api\LinksApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Dto\Pagination; useStoryblok\Api\Request\LinksRequest; $client = newStoryblokClient(/* ... */); $linksApi = newLinksApi($client); $response = $linksApi->all(newLinksRequest( pagination: newPagination(page: 1, perPage: 1000) ));

Get by parent (Storyblok\Api\Domain\Value\Id)

useStoryblok\Api\LinksApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Id; $id = newId(/** ... */); $client = newStoryblokClient(/* ... */); $linksApi = newLinksApi($client); $response = $linksApi->byParent($id);

Get all root links

useStoryblok\Api\LinksApi; useStoryblok\Api\StoryblokClient; $client = newStoryblokClient(/* ... */); $linksApi = newLinksApi($client); $response = $linksApi->roots($id);

Datasource

In your code you should type-hint to Storyblok\Api\DatasourceApiInterface

Get by name (string)

useStoryblok\Api\DatasourceApi; useStoryblok\Api\StoryblokClient; $client = newStoryblokClient(/* ... */); $api = newDatasourceApi($client); $response = $api->byName('tags'); // returns Storyblok\Api\Domain\Value\Datasource

If it has more than one dimension, you can get the entries by

useStoryblok\Api\DatasourceApi; useStoryblok\Api\StoryblokClient; useStoryblok\Api\Domain\Value\Datasource\Dimension; $client = newStoryblokClient(/* ... */); $api = newDatasourceApi($client); $response = $api->byName('tags', newDimension('de')); // returns Storyblok\Api\Domain\Value\Datasource

Tags

In your code you should type-hint to Storyblok\Api\TagsApiInterface

Get all available tags

useStoryblok\Api\TagsApi; useStoryblok\Api\StoryblokClient; $client = newStoryblokClient(/* ... */); $api = newTagsApi($client); $response = $api->all(); // returns Storyblok\Api\Response\TagsResponse

Assets

To use the assets API you have to configure the Assets client.

useStoryblok\Api\StoryblokClient; useStoryblok\Api\AssetsApi; $client = newStoryblokClient( baseUri: 'https://api.storyblok.com', token: 'assets-api-token', timeout: 10// optional ); $assetsApi = newAssetsApi($assetsClient); $assetsApi->get('filename.png')

DX Enhancement through Abstract Collections

To improve developer experience (DX), especially when working with content types like stories, the following abstract class is provided to manage collections of specific content types. This class simplifies data handling and ensures type safety while dealing with large amounts of content from Storyblok.

Abstract ContentTypeCollection Class

The ContentTypeCollection class provides a structured way to work with Storyblok content types. It makes managing pagination, filtering, and sorting more intuitive and reusable, saving time and reducing boilerplate code.

<?phpdeclare(strict_types=1); namespaceApp\ContentType; useIteratorAggregate; useStoryblok\Api\Response\StoriesResponse; /** * @template T of ContentTypeInterface * * @implements IteratorAggregate<int, T> */abstractreadonlyclass ContentTypeCollection implements \Countable, \IteratorAggregate{publicint$total; publicint$perPage; publicint$curPage; publicint$lastPage; public ?int$prevPage; public ?int$nextPage; /** * @var list<T> */privatearray$items; finalpublicfunction__construct(StoriesResponse$response){$this->items = array_values(array_map($this->createItem(...), $response->stories)); $this->total = $response->total->value; $this->curPage = $response->pagination->page; $this->perPage = $response->pagination->perPage; $this->lastPage = (int) ceil($this->total / $this->perPage); $this->prevPage = 1 < $this->curPage ? $this->curPage - 1 : null; $this->nextPage = $this->curPage < $this->lastPage ? $this->curPage + 1 : null} /** * @return \Traversable<int, T> */finalpublicfunctiongetIterator(): \Traversable{returnnew \ArrayIterator($this->items)} finalpublicfunctioncount(): int{return\count($this->items)} /** * @param array<string, mixed> $values * * @return T */abstractprotectedfunctioncreateItem(array$values): ContentTypeInterface}

Benefits of Using the Abstract Collection:

  1. Simplified Data Handling: Instead of dealing with raw arrays of stories, this abstract class helps you manage collections of content types, like blog posts or articles, in an organized manner. It abstracts away the repetitive work of pagination and mapping response data to objects.
  2. Enhanced Readability: Using a well-structured collection class makes the code easier to read and maintain. Instead of handling pagination and raw data structures in controllers or services, you simply instantiate the collection and let it handle the data.
  3. Reusability: The class is flexible and reusable across different content types. Once implemented, you can easily create new collections for other Storyblok content types with minimal extra code.
  4. Pagination and Metadata Management: The collection class comes with built-in properties for pagination and metadata (e.g., total items, current page, etc.), making it much easier to manage paginated data efficiently.

Example Usage with a Collection

Here is an example of how to use the ContentTypeCollection to manage blog posts in your Symfony project:

<?phpdeclare(strict_types=1); namespaceApp\ContentType\BlogPost; useApp\ContentType\ContentTypeCollection; useApp\ContentType\ContentTypeFactory; /** * @extends ContentTypeCollection<BlogPost> */finalreadonlyclass BlogPostCollection extends ContentTypeCollection{protectedfunctioncreateItem(array$values): BlogPost{return ContentTypeFactory::create($values, BlogPost::class)} }
newBlogPostCollection( $this->stories->allByContentType( BlogPost::type(), newStoriesRequest( language: $this->localeSwitcher->getLocale(), pagination: newPagination($this->curPage, self::PER_PAGE), sortBy: newSortBy('first_published_at', Direction::Desc), filters: $filters, excludeFields: newFieldCollection([ newField('body'), newField('additional_contents'), ]), ), ), );

Helpers

The Storyblok\Api\Util\ValueObjectTrait provides utility methods for mapping raw Storyblok data arrays into strong PHP value objects, enums, and domain models. These helpers reduce boilerplate code and improve readability in DTO constructors or factory methods.

Use this trait in your value objects or models to simplify the parsing and validation of Storyblok field values.

Available Methods

MethodDescription
one()Expects exactly one item (e.g. from a blocks field). Instantiates one object from it.
list()Maps a list of items to objects. Allows setting $min, $max, or exact $count constraints.
nullOrOne()Same as one(), but allows the field to be optional (returns null if empty).
enum()Maps a string value to a backed enum. Supports default value and whitelisting of allowed values.
DateTimeImmutable()Returns a Safe\DateTimeImmutable object from a given date string.
Uuid()Returns a Storyblok\Api\Domain\Value\Uuid instance from a string.
Asset()Maps an asset array to a Storyblok\Api\Domain\Type\Asset object.
nullOrAsset()Same as Asset(), but allows null or invalid input.
MultiLink()Maps a multilink array to a Storyblok\Api\Domain\Type\MultiLink object.
nullOrMultiLink()Same as MultiLink(), but returns null if url and id are missing or empty.
RichText()Maps rich text content to a Storyblok\Api\Domain\Type\RichText object.
nullOrRichText()Same as RichText(), but returns null if content is empty or only contains whitespace.
boolean()Returns true if the key exists and its value is true, otherwise false.
zeroOrInteger()Returns an integer from the field, or 0 if missing.
zeroOrFloat()Returns a float from the field, or 0.0 if missing.
string()Returns a trimmed non-empty string (using TrimmedNonEmptyString). Optional max length check.
nullOrString()Same as string(), but returns null if missing or invalid.
nullOrEditable()Returns an Editable instance or null.

Management API client

For the Management API PHP Client, see storyblok/php-management-api-client.

License

This project is licensed under the MIT License. Please see License File for more information.

About

Storyblok - SensioLabs PHP Content Delivery API

Resources

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
license-checker.php

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 9