Content Modeling in Symfony
Learn how to handle different content type and nestable blocks, render rich text, and use story references to manage content globally.
Alternatively, in the existing space, create a new content type block article and an “Articles” folder with content. The article content type block should have the following fields:
title: Textcontent: Rich text
Create an article-overview story as a page type content.
Finally, create a featured-articles nestable block with the following field:
articles: References
Add a new featured-articles block to the body field of the home story and select some articles to be featured.
Fetch and list all articles
Section titled “Fetch and list all articles”Create a new controller to get all stories from this new content type.
<?php
declare(strict_types=1);
namespace App\Controller;
use App\ContentType\ArticleOverview;use Storyblok\Api\StoriesApiInterface;use Storyblok\Api\Request\StoriesRequest;use Storyblok\Bundle\ContentType\Attribute\AsContentTypeController;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;
#[AsContentTypeController(contentType: ArticleOverview::class)]final class ArticleOverviewController extends AbstractController{ public function __construct( private readonly StoriesApiInterface $storiesApi ) { }
public function __invoke(Request $request, ArticleOverview $articleOverview): Response { $response = $this->storiesApi->allByContentType('article');
return $this->render('article_overview.html.twig', [ 'articleOverview' => $articleOverview, 'articles' => $response->stories, ]); } }Create a new content type and template file.
<?php
declare(strict_types=1);
namespace App\ContentType;
use Storyblok\Bundle\ContentType\ContentType;
final readonly class ArticleOverview extends ContentType{ public string $uuid; public string $name; public string $slug; public \DateTimeImmutable $publishedAt;
public function __construct(array $values) { $this->uuid = $values['uuid']; $this->name = $values['name']; $this->slug = $values['full_slug']; $this->publishedAt = new \DateTimeImmutable($values['published_at']); }
public function publishedAt(): \DateTimeImmutable { return $this->publishedAt; }
public static function type(): string { return 'article-overview'; }}{% extends 'base.html.twig' %}
{% block title %}Articles - {{ parent() }}{% endblock %}
{% block body %}<h1>Articles</h1><ul> {% for article in articles %} {% if article is iterable and article.full_slug is defined %} <li> <a href="/{{ article.full_slug }}">{{ article.name }}</a> </li> {% endif %} {% endfor %}</ul>{% endblock %}Now, the article overview page shows a list of links to all articles.
Create the article block
Section titled “Create the article block”Add a new content type class and controller to render article stories.
<?php
declare(strict_types=1);
namespace App\Controller;
use App\ContentType\Article;use Storyblok\Bundle\ContentType\Attribute\AsContentTypeController;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;
#[AsContentTypeController(contentType: Article::class)]final class ArticleController extends AbstractController{ public function __invoke(Request $request, Article $article): Response { return $this->render('article.html.twig', [ 'article' => $article, ]); }}<?php
declare(strict_types=1);
namespace App\ContentType;
use Storyblok\Bundle\ContentType\ContentType;
final readonly class Article extends ContentType{ public string $uuid; public string $name; public string $slug; public string $title; public array $content; public \DateTimeImmutable $publishedAt;
public function __construct(array $values) { $this->uuid = $values['uuid']; $this->name = $values['name']; $this->slug = $values['full_slug']; $this->title = $values['content']['title'] ?? ''; $this->content = $values['content']['content'] ?? []; $this->publishedAt = new \DateTimeImmutable($values['published_at']); }
public function publishedAt(): \DateTimeImmutable { return $this->publishedAt; }}Create the article template.
{% extends 'base.html.twig' %}
{% block body %}<article> <h1>{{ article.title }}</h1> <div class="content"> {{ article.content|rich_text }} </div></article>{% endblock %}To render rich text fields, the rich_text Twig filter provided by the Storyblok Symfony bundle is used.
Handle referenced stories
Section titled “Handle referenced stories”In the PageController.php file, inject the StoriesApiInterface and set the resolve_relations parameter to get the full object response of referenced stories.
<?php
declare(strict_types=1);
namespace App\Controller;
use App\ContentType\Page;use Storyblok\Api\StoriesApiInterface;use Storyblok\Api\Request\StoryRequest;use Storyblok\Api\Domain\Value\Resolver\RelationCollection;use Storyblok\Bundle\ContentType\Attribute\AsContentTypeController;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;
#[AsContentTypeController(contentType: Page::class)]final class PageController extends AbstractController{ public function __construct( private readonly StoriesApiInterface $storiesApi ) { }
public function __invoke(Request $request, Page $page): Response { $response = $this->storiesApi->bySlug( $page->slug, new StoryRequest( withRelations: new RelationCollection(['featured-articles.articles']) ) );
$resolvedPage = new Page($response->story);
return $this->render('page.html.twig', [ 'page' => $page, 'page' => $resolvedPage, ]); }}Next, create a block class to handle the featured articles.
<?php
declare(strict_types=1);
namespace App\Block;
use Storyblok\Bundle\Block\Attribute\AsBlock;
#[AsBlock(name: 'featured-articles', template: 'blocks/featured_articles.html.twig')]final readonly class FeaturedArticles{ public array $articles;
public function __construct(array $values) { $this->articles = $values['articles'] ?? []; }}Create the corresponding Twig template.
<section class="featured-articles"> <h2>Featured Articles</h2> <ul> {% for article in block.articles %} {% if article is iterable and article.full_slug is defined %} <li> <a href="/{{ article.full_slug }}">{{ article.name }}</a> </li> {% endif %} {% endfor %} </ul></section>Now, this component will render links to the featured articles in the home page of your project.
Get in touch with the Storyblok community