Managing Structured Data with Sulu Content Types
Stop writing boilerplate entities. Learn how to use Sulu's XML-driven content types to auto-generate database schemas and admin interfaces.
01 May 2026, 08:03 UTC

The Problem: Entity Boilerplate in CMS Development
When adding new structured data to a Symfony-based site, the standard workflow involves creating a Doctrine entity, writing migration files, and building custom forms. For a CMS, this creates a disconnect: developers spend hours writing boilerplate for a data structure that a content editor might want to change next week. This tight coupling between the database schema and the codebase slows down iteration.
The Solution: XML-Driven Schema Generation
Sulu solves this by allowing developers to define content types via XML configuration. Instead of manual entity creation, you declare the desired properties in a config file, and Sulu automatically generates the corresponding database tables and admin UI fields at runtime. This shifts the focus from "how to store the data" to "what data is needed."
Defining a Content Type
To create a new content type, you define an XML file in config/sulu/content-types/. For example, to create an "Article" type with a title, an image, and a custom rating field, you would create article.xml:
<?xml version="1.0"?>
<content-type xmlns="http://schemas.sulu.io/content-type"
name="article"
title="Article"
view="sulu_content.default">
<properties>
<property name="title" type="text_line" mandatory="true">
<tag name="sulu.header" />
</property>
<property name="image" type="media_selection" mandatory="false">
<tag name="sulu.media" />
</property>
<!-- Custom property referencing a Symfony service -->
<property name="rating" type="custom" mandatory="false">
<parameter name="service_id" value="App\\Sulu\\Property\\RatingProperty" />
</property>
</properties>
</content-type>
The type="custom" property allows you to extend Sulu by referencing a Symfony service that implements the necessary property interfaces. This service must be registered in your services.yaml and tagged with sulu.property.
How Sulu Handles the Backend
Once the XML is defined and the cache is warmed, Sulu performs several automated steps:
- Database Mapping: It creates a table named
sulu_content_article. - Column Generation: Each XML property is mapped to a Doctrine column (e.g.,
text_linebecomes aVARCHAR). - Admin Integration: The CMS admin interface automatically renders the corresponding input fields based on the property types.
Verification and Deployment
To activate the new content type, run the following command from your project root as a user with shell access to the application:
php bin/console cache:clear
Verify the result by logging into the Sulu admin and navigating to Content > Content Types. To confirm the database state, you can run a query against your database management tool:
DESCRIBE sulu_content_article;
If you need to evolve the schema—for example, adding a "subtitle" field—simply add the property to the XML and run the update command:
php bin/console sulu:content-type:update
Trade-offs and Performance Constraints
While this system removes boilerplate, it introduces specific engineering considerations:
- Collection Overhead: If a content type uses many large collection properties (repeatable blocks), Sulu may load all related data for every page request. To prevent performance degradation, use lazy loading configurations where possible.
- Service Complexity: Custom properties require a deeper understanding of Sulu's internal property manager and Symfony service tagging, which is more complex than writing a simple Doctrine entity.
To check for performance bottlenecks, use the Symfony Profiler to monitor the number of Doctrine queries triggered when rendering a page with multiple custom content types.
Actionable Summary
Use XML content types when you need flexible, structured data without the overhead of manual entity management. Start with standard types (text_line, media_selection), use sulu:content-type:update for iterations, and monitor your query count in the profiler if you implement large collections.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.