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. Install the package:

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

    storyblok:
        adapters:
            default:
                space_id: 'your_space_id'
                token: 'your_preview_token'
                adapter_key: 'your_adapter_key'
                cache: 'cache.adapter.redis'
                cache_prefix: 'storyblok'
    
  3. Fetch a story in a controller:

    use Storyblok\Bundle\StoryblokBundle\Service\ContentApi;
    
    public function show(ContentApi $contentApi)
    {
        $story = $contentApi->fetch('home', ['version' => 'published']);
        return $this->render('story.html.twig', ['story' => $story]);
    }
    
  4. Render components in Twig:

    {% for block in story.content %}
        {% include 'components/' ~ block._component ~ '.html.twig' with {
            'block': block,
            'contentApi': contentApi
        } %}
    {% endfor %}
    

First Use Case

Fetch and render a homepage with dynamic components:

// src/Controller/HomeController.php
public function index(ContentApi $contentApi)
{
    $story = $contentApi->fetch('home', ['version' => 'published']);
    return $this->render('home/index.html.twig', ['story' => $story]);
}

Implementation Patterns

Adapter-Based Architecture

  • Create adapters for different environments (e.g., preview, production):

    storyblok:
        adapters:
            preview:
                space_id: '%env(STORYBLOK_SPACE_ID)%'
                token: '%env(STORYBLOK_PREVIEW_TOKEN)%'
                adapter_key: 'preview'
            production:
                space_id: '%env(STORYBLOK_SPACE_ID)%'
                token: '%env(STORYBLOK_TOKEN)%'
                adapter_key: 'production'
    
  • Inject the adapter into services:

    public function __construct(
        private StoryblokAdapterInterface $storyblokAdapter
    ) {}
    

Component Rendering

  • Use component discovery to auto-load components:

    // src/Storyblok/Component/ComponentResolver.php
    public function resolve(string $componentName): string
    {
        return sprintf('components/%s.html.twig', $componentName);
    }
    
  • Pass context to components:

    {% include 'components/' ~ block._component ~ '.html.twig' with {
        'block': block,
        'contentApi': contentApi,
        'assetProxyUrlGenerator': assetProxyUrlGenerator
    } %}
    

Asset Handling

  • Generate proxy URLs for assets:

    public function generateUrl(AssetProxyUrlGenerator $assetProxyUrlGenerator, string $assetId)
    {
        return $assetProxyUrlGenerator->generate($assetId);
    }
    
  • Use AssetData DTO for type safety:

    /** @var AssetData $asset */
    $imageUrl = $asset->getUrl();
    $altText = $asset->getAlt();
    

Webhooks and Sync

  • Set up a webhook endpoint:

    // src/EventListener/StoryblokWebhookListener.php
    public function __invoke(StoryblokWebhookEvent $event)
    {
        $this->storyblokAdapter->sync();
    }
    
  • Trigger sync manually:

    php bin/console storyblok:sync
    

Field-Specific Patterns

  • RichText fields:

    {{ block.richtext|storyblokRichtext(contentApi) }}
    
  • Link fields:

    <a href="{{ block.link.url }}">{{ block.link.title }}</a>
    
  • Choice fields:

    {{ block.choice|storyblokChoice }}
    

Gotchas and Tips

Common Pitfalls

  1. Caching:

    • Always clear cache after sync:
      php bin/console cache:clear
      php bin/console storyblok:sync
      
    • Use cache_prefix to avoid conflicts in multi-environment setups.
  2. Adapter Keys:

    • Must be valid "snails" (lowercase, hyphen-separated, no spaces).
    • Used as webhook tokens, not space IDs.
  3. Pagination:

    • Use sendPaginatedRequest() for large datasets:
      $assets = $managementApi->sendPaginatedRequest('/assets', []);
      
  4. Asset URLs:

    • Private assets require signed URLs. Configure in asset_proxy:
      storyblok:
          asset_proxy:
              signed_urls: true
              token: '%env(STORYBLOK_ASSET_TOKEN)%'
      
  5. Field Validation:

    • ChoiceField requires allowMissingData for optional selections:
      fields:
          my_choice:
              type: choice
              options:
                  - value: 'option1'
                    label: 'Option 1'
              allowMissingData: true
      

Debugging Tips

  • Enable debug mode:

    storyblok:
        debug: true
    
    • Logs API requests/responses to var/log/storyblok.log.
  • Use the debug command:

    php bin/console debug:storyblok
    
  • Validate Storyblok config:

    php bin/console debug:config storyblok
    

Extension Points

  1. Custom Field Types:

    • Extend AbstractField and register in storyblok.yaml:
      storyblok:
          field_types:
              my_custom_field: App\Storyblok\Field\MyCustomField
      
  2. Event Subscribers:

    • Listen to StoryblokDefinitionsSyncedEvent for post-sync logic:
      public static function getSubscribedEvents(): array
      {
          return [
              StoryblokDefinitionsSyncedEvent::class => 'onDefinitionsSynced',
          ];
      }
      
  3. Asset Proxy Customization:

    • Override AssetProxyUrlGenerator for custom URL logic:
      services:
          App\Storyblok\AssetProxy\CustomAssetProxyUrlGenerator:
              decorates: 'storyblok.asset_proxy_url_generator'
              arguments: ['@.inner']
      

Performance Optimizations

  • Fetch folders efficiently:

    $folders = $contentApi->fetchFoldersInPath('/path/to/folder');
    
  • Use fetchFolderTitleMap() for large folder structures:

    $titleMap = $contentApi->fetchFolderTitleMap('/');
    
  • Lazy-load components with ComponentResolver:

    $resolver->resolve($componentName); // Only loads if needed
    

Migration Notes

  • From v3 to v5:

    • Replace global services with adapter-based injection.
    • Update deprecated methods (e.g., ManagementApi::fetchFoldersInPath()ContentApi::fetchFoldersInPath()).
    • Configure adapters explicitly in storyblok.yaml.
  • Private Assets:

    • Requires signed_urls: true and a token in asset_proxy config.
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor