Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions blog/2026-03-20-storyblok-adapters.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Storyblok Adapter Setup and Usage
authors: [jannik]
tags: [storyblok, symfony]
---

The Storyblok docs now include a complete guide for the adapter-based setup.

## New Documentation

- [Adapters guide][1] with setup, configuration and usage.
- Dedicated docs for [Content API][5], [Management API][6], [ID Slug Mapper][7], and [Request Validator][8].
- New docs for [Backend Edit URL Helper][9] and [Definitions Synced Event][10].
- New [Asset Proxy][12] section with setup/configuration and usage details.
- New [More Features][13] section for miscellaneous mostly-internal helper features.

## Updated Existing Pages

- [Storyblok installation page][2] now reflects the adapter-based flow.
- [CLI docs][3] now describe adapter-scoped commands and include the post-sync `StoryblokDefinitionsSyncedEvent` hook.
- [Webhook docs][4] now use `AppStoryblokAdapter` examples.
- [Storyblok index][2] now links to the new Asset Proxy section and includes an events overview.
- `More Features` was moved to the bottom of the Storyblok sidebar order.


[1]: /docs/php/symfony/storyblok/adapters
[2]: /docs/php/symfony/storyblok
[3]: /docs/php/symfony/storyblok/cli
[4]: /docs/php/symfony/storyblok/webhook
[5]: /docs/php/symfony/storyblok/api/content-api
[6]: /docs/php/symfony/storyblok/api/management-api
[7]: /docs/php/symfony/storyblok/more-features/id-slug-mapper
[8]: /docs/php/symfony/storyblok/more-features/request-validator
[9]: /docs/php/symfony/storyblok/more-features/backend-edit-url-helper
[10]: /docs/php/symfony/storyblok/definitions-synced-event
[12]: /docs/php/symfony/storyblok/asset-proxy
[13]: /docs/php/symfony/storyblok/more-features
171 changes: 171 additions & 0 deletions docs/php/symfony/storyblok/adapters.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
sidebar_position: -1
---

# Adapters

The Storyblok bundle uses adapters to connect to one or multiple Storyblok spaces.
Each adapter is meant to connect to a single Storyblok space, configured via `StoryblokConfig`.


## Setup

Create one adapter class per Storyblok space:

```php title="src/Storyblok/Adapter/AppStoryblokAdapter.php"
<?php declare(strict_types=1);

namespace App\Storyblok\Adapter;

use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Torr\Storyblok\Adapter\AbstractStoryblokAdapter;
use Torr\Storyblok\Config\StoryblokConfig;

final class AppStoryblokAdapter extends AbstractStoryblokAdapter
{
public function __construct (
ContainerInterface $locator,
#[Autowire(env: "STORYBLOK_APP_SPACE_ID")]
string $spaceId,
#[\SensitiveParameter]
#[Autowire(env: "STORYBLOK_APP_MANAGEMENT_TOKEN")]
string $managementToken,
#[\SensitiveParameter]
#[Autowire(env: "STORYBLOK_APP_CONTENT_TOKEN")]
string $contentToken,
#[\SensitiveParameter]
#[Autowire(env: "default::STORYBLOK_APP_WEBHOOK_SECRET")]
?string $webhookSecret,
)
{
parent::__construct(
$locator,
new StoryblokConfig(
spaceId: $spaceId,
managementToken: $managementToken,
contentToken: $contentToken,
webhookSecret: $webhookSecret,
),
);
}

#[\Override]
public static function getKey () : string
{
return "app";
}

#[\Override]
public function getDisplayName () : string
{
return "App Website";
}

#[\Override]
public function getStandaloneComponentKeys () : array
{
return [
PageComponent::getKey(),
NewsEntryComponent::getKey(),
];
}
}
```

Also define the env vars for this adapter in your `.env` file:

```dotenv title=".env"
STORYBLOK_APP_SPACE_ID=
STORYBLOK_APP_MANAGEMENT_TOKEN=
STORYBLOK_APP_CONTENT_TOKEN=
STORYBLOK_APP_WEBHOOK_SECRET=
```

:::info
Adapter keys must be unique and must be valid ["snail"][snail] strings.
:::


## Configuration

`StoryblokConfig` supports these options:

- `$spaceId` (required): numeric Storyblok space id as string.
- `$managementToken` (required): management API token.
- `$contentToken` (required): content API token.
- `$localeLevel` (optional, default `0`): 0-based slug level where locales are defined.
- `$webhookSecret` (optional): secret for webhook signature validation.
- `$allowUrlWebhookSecret` (optional, default `false`): allow URL-based secret fallback (for free Storyblok plans).
- `$assetToken` (optional): token for signed asset URLs.


## Provided Services

On the adapter, you have access to different services for this specific Storyblok space:


### Content API

[Content API docs](./api/content-api)

Fetch and transform Storyblok content data (for example stories, links and space data).

```php
$appStoryblok->contentApi
```

### Management API

[Management API docs](./api/management-api)

Manage your Storyblok space via the Management API, for example when syncing component definitions.

```php
$appStoryblok->managementApi
```

### ID Slug Mapper

[ID Slug Mapper docs](./more-features/id-slug-mapper)

Resolve between Story IDs and slugs using the content API.

```php
$appStoryblok->idSlugMapper
```

### Request Validator

[Request Validator docs](./more-features/request-validator)

Validate incoming webhook requests against your adapter webhook configuration.

```php
$appStoryblok->requestValidator
```



## Usage

If you know which Storyblok adapter you want to use, you can inject it directly:

```php
function example (AppStoryblokAdapter $storyblok)
{
$storyblok->...
}
```

Sometimes you need to fetch the storyblok depending on a dynamic value, so you need to use the `StoryblokAdapterRegistry` for that:

```php
function example (StoryblokAdapterRegistry $registry)
{
$registry->getByKey("app");
$registry->getBySpaceId("123456");
}
```

[snail]: /docs/php/symfony/snail/
83 changes: 83 additions & 0 deletions docs/php/symfony/storyblok/api/content-api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Content API

The Content API is available on the adapter as:

```php
$appStoryblok->contentApi
```

Use it to fetch and hydrate Storyblok content for the configured adapter space.

## Main Methods

### `fetchSingleStory()`

Fetches a single story by slug, id or uuid.

```php
$appStoryblok->contentApi->fetchSingleStory("home");
```

### `fetchStories()`

Fetches stories for a specific story class.

```php
$appStoryblok->contentApi->fetchStories(PageStory::class, "pages/*");
```

### `fetchAllStories()`

Fetches all stories (optionally filtered by slug pattern).

```php
$appStoryblok->contentApi->fetchAllStories("pages/*");
```

### `getSpaceInfo()`

Returns metadata for the current Storyblok space.

```php
$appStoryblok->contentApi->getSpaceInfo();
```

### `fetchDatasourceEntries()`

Fetches datasource entries from the Content API.

```php
$appStoryblok->contentApi->fetchDatasourceEntries("countries");
```

### `fetchFoldersInPath()`

Fetches folders below a slug prefix.

```php
$appStoryblok->contentApi->fetchFoldersInPath("pages");
```

### `fetchFolderTitleMap()`

Fetches a map of local folder URL to folder title.

```php
$appStoryblok->contentApi->fetchFolderTitleMap("pages");
```

### `fetchAllLinks()`

Fetches all Storyblok links.

```php
$appStoryblok->contentApi->fetchAllLinks();
```

### `fetchSignedAssetUrl()`

Fetches signed asset data for private assets.

```php
$appStoryblok->contentApi->fetchSignedAssetUrl("https://a.storyblok.com/f/...");
```
89 changes: 89 additions & 0 deletions docs/php/symfony/storyblok/api/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# API

Besides the [content api] and [management api], it is recommended to add a storyblok API, which is a wrapper for the content api and helps with purpose-built fetch methods.

This way all your knowledge about the structure and types of your Storyblok space are encapsulated in one place.


```php
use Symfony\Component\DependencyInjection\Attribute\Exclude;

#[Exclude]
class AppStoryblokApi
{
public function __construct (
private readonly ContentApi $contentApi,
) {}

/**
* @return NewsStory[]
*/
public function fetchNews (string $pageTree, string $locale)
{
return $this->contentApi->fetchStories(
storyType: NewsStory::class,
slug: \sprintf("%s/%s/news/*", $pageTree, $locale),
version: $version,
);
}
}
```

Wire it up in your adapter:

```php
class AppStoryblokAdapter
{
public readonly AppStoryblokApi $api;

public function __construct (...)
{
$this->api = new AppStoryblokApi($this->contentApi);
}
```

And use it like this:

```php
$appStoryblok->api->fetchNews("website", "de-de")
```

## Caching

If your API client is caching, you need to reset the cache regularly. As the API is not a service itself but just wrapped inside the adapter, you need to forward the call.

First mark your adapter as resettable:

```php
use Symfony\Contracts\Service\ResetInterface;

class AppStoryblokAdapter extends AbstractStoryblokAdapter implements ResetInterface
{
public readonly AppStoryblokApi $api;

public function reset ()
{
$this->api->reset();
}
}
```

Then implement the cache reset in your API client:

```php
use Symfony\Contracts\Service\ResetInterface;

class AppStoryblokApi implements ResetInterface
{
public function reset ()
{
$this->cache = [];
}
}
```




[content api]: ./content-api
[management api]: ./management-api
Loading
Loading