Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Storyblok Laravel Package

21torr/storyblok

Symfony bundle providing API helpers and infrastructure to work with Storyblok. Simplifies fetching content, integrating Storyblok services, and building Storyblok-powered Symfony apps. Includes documentation for setup and usage.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require 21torr/storyblok
    
  2. Configure in config/packages/storyblok.yaml:

    storyblok:
        adapters:
            default:
                space_id: 'your-space-id'
                token: 'your-preview-token'
                cache: true
                cache_lifetime: 3600
    
  3. First Use Case: Fetch a story in a controller:

    use Storyblok\Bundle\StoryblokBundle\Api\ContentApi;
    
    class PageController extends AbstractController
    {
        public function show(ContentApi $contentApi, string $slug): Response
        {
            $story = $contentApi->fetchStory('page', $slug);
            return $this->render('page/show.html.twig', ['story' => $story]);
        }
    }
    
  4. Twig Integration: Enable the Twig extension in config/packages/twig.yaml:

    twig:
        globals:
            storyblok: '@storyblok.twig.storyblok_extension'
    

    Use in templates:

    {{ storyblok.story('page', 'home')|raw }}
    

Implementation Patterns

Core Workflows

1. Story Fetching & Caching

  • Fetch with caching:
    $story = $contentApi->fetchStory('page', 'home', ['version' => 'draft']);
    
  • Cache invalidation:
    php bin/console storyblok:clear-cache
    

2. Component Rendering

  • Register components in config/packages/storyblok.yaml:
    storyblok:
        components:
            page:
                template: 'page/_page.html.twig'
            hero:
                template: 'components/hero.html.twig'
    
  • Render components dynamically:
    {% for block in story.body %}
        {% include storyblok.component(block._uid) %}
    {% endfor %}
    

3. Asset Handling

  • Generate signed URLs for private assets:
    $assetUrl = $assetProxyUrlGenerator->generateUrl($assetData, ['expires' => '+1 hour']);
    
  • Use in Twig:
    <img src="{{ storyblok.asset_url(asset) }}" alt="{{ asset.name }}">
    

4. Webhooks & Real-Time Updates

  • Configure webhook in config/packages/storyblok.yaml:
    storyblok:
        webhooks:
            default:
                endpoint: '/storyblok/webhook'
                events: ['published', 'unpublished']
    
  • Handle events via Symfony event listeners:
    use Storyblok\Bundle\StoryblokBundle\Event\StoryblokStoryPublishedEvent;
    
    public function onStoryPublished(StoryblokStoryPublishedEvent $event): void
    {
        $this->cache->clear();
    }
    

5. Field-Specific Processing

  • RichText with custom settings:
    $richText = new RichTextField($story->body, [
        'allowLinksOpeningInNewWindow' => true,
        'allowCustomAttributes' => ['data-custom' => true],
    ]);
    
  • Asset with alt text:
    $assetField = new AssetField($story->image, ['useDescriptionAsAlt' => true]);
    

Integration Tips

Twig Extensions

  • Custom filters:
    // src/Twig/StoryblokExtension.php
    public function getFilters(): array
    {
        return [
            new TwigFilter('storyblok_markdown', [$this->markdownConverter, 'convert']),
        ];
    }
    

Form Integration

  • Map Storyblok fields to Symfony forms:
    $builder->add('title', TextType::class, [
        'data' => $story->title,
        'attr' => ['storyblok_field' => 'title'],
    ]);
    

API Clients

  • Extend ContentApi for custom logic:
    class CustomContentApi extends ContentApi
    {
        public function fetchLocalizedStory(string $component, string $slug, array $options = []): StoryData
        {
            $options['lang'] = $this->requestStack->getCurrentRequest()->getLocale();
            return parent::fetchStory($component, $slug, $options);
        }
    }
    

Testing

  • Mock API responses:
    $this->mock(ContentApi::class)
         ->shouldReceive('fetchStory')
         ->once()
         ->andReturn($mockStoryData);
    

Gotchas and Tips

Pitfalls

1. Breaking Changes in v5.x

  • Global services removed: ContentApi and ManagementApi are now adapter-specific.
    // Old (v3/v4):
    $story = $this->get('storyblok.content_api')->fetchStory(...);
    
    // New (v5+):
    $story = $this->get('storyblok.adapter.default.content_api')->fetchStory(...);
    
  • Deprecated methods: Use ContentApi::fetchFoldersInPath() instead of ManagementApi::fetchFoldersInPath().

2. Cache Invalidation

  • Manual cache clearing required after direct Storyblok API updates (not triggered by webhooks).
    php bin/console storyblok:clear-cache
    
  • Cache lifetime: Set cache_lifetime in config to avoid stale data (default: 3600 seconds).

3. Field Validation

  • ChoiceField pitfalls:
    • Ensure allowMissingData is set if Storyblok fields might be empty:
      $choiceField = new ChoiceField($story->tags, ['allowMissingData' => true]);
      
    • Validate min/max options as strings (not integers) for ChoiceField.

4. Asset URLs

  • Private assets require signed URLs:
    $url = $assetProxyUrlGenerator->generateUrl($asset, ['expires' => '+1 hour']);
    
  • Empty assets: Handle cases where filename is empty but the asset structure exists.

5. Webhook Tokens

  • Use adapter key, not space ID, as the webhook token:
    storyblok:
        adapters:
            default:
                adapter_key: 'your-adapter-key'  # <-- This is the token!
    

Debugging Tips

1. Debugging API Responses

  • Enable debug mode in config:
    storyblok:
        debug: true
    
  • Check raw responses:
    $response = $contentApi->sendRequest('GET', '/spaces/me/stories', []);
    $this->logger->debug('Raw response:', ['data' => $response->getData()]);
    

2. Component Discovery

  • Verify component registration:
    php bin/console debug:container storyblok.component_discovery
    
  • Check for missing templates:
    php bin/console storyblok:debug
    

3. Field-Specific Issues

  • RichText parsing errors:

    • Ensure fullData is passed for links:
      $richText = new RichTextField($story->body, ['fullData' => true]);
      
    • Validate toolbar options (e.g., table requires table_styling config).
  • NumberField validation:

    • Use strings for minValue, maxValue, and decimals:
      $numberField = new NumberField($story->price, [
          'minValue' => '0',
          'maxValue' => '1000',
          'decimals' => '2',
      ]);
      

4. Performance

  • Avoid N+1 queries:
    • Fetch related stories/assets in bulk:
      $stories = $contentApi->fetchStories(['component' => 'page'], ['per_page' => 100]);
      
  • Use fetchFoldersInPath() for faster folder traversal (v5.2.0+):
    $folders = $contentApi->fetchFoldersInPath('/blog');
    

Extension Points

1. Custom Field Types

  • Extend AbstractField:
    class CustomField extends AbstractField
    {
        public function __construct(array $data, array $options = [])
        {
            parent::__construct($data, $options);
        }
    
        public function getValue(): mixed
        {
            return $this->data['custom_key'] ?? null;
        }
    }
    
  • Register in config:
    storyblok:
        fields:
            custom:
                class: App\Storyblok\CustomField
    

2. Custom Adapters

  • **
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime