alengo/sulu-content-extra-bundle
Extends Sulu CMS 3.x Pages and Articles with an Additional Data tab, built-in entities, configurable mapping for localized/unlocalized fields, auto entity registration, navigation link markers, and sortable template groups. PHP 8.2+, Symfony 7.
Installation:
composer require alengo/sulu-content-extra-bundle
Register the bundle in config/bundles.php:
Alengo\SuluContentExtraBundle\AlengoContentExtraBundle::class => ['all' => true],
Define a Basic Form:
Create config/forms/page_additional_data.xml:
<form xmlns="http://schemas.sulu.io/template/template">
<key>page_additional_data</key>
<properties>
<property name="template_theme" type="select" mandatory="false">
<meta>
<title lang="en">Theme</title>
</meta>
<params>
<param name="values" type="collection">
<param name="default" type="collection">
<param name="title" value="Default"/>
<param name="name" value="default"/>
</param>
</param>
</params>
</property>
</properties>
</form>
Configure Field Mapping (optional, uses defaults if omitted):
# config/packages/alengo_content_extra.yaml
alengo_content_extra:
page:
form_key: page_additional_data
unlocalized_keys: [template_theme]
localized_keys: [notes] # Add this if you create a 'notes' field later
Restart Sulu:
php bin/console cache:clear
Access the New Tab: Edit a Page or Article in the Sulu admin panel. A new "Additional Data" tab will appear with your form fields.
Create a Form Field:
Add a select field for template_theme (as shown above) with options like default, dark, light.
Configure Localization:
In alengo_content_extra.yaml, mark template_theme as unlocalized (shared across all languages):
alengo_content_extra:
page:
unlocalized_keys: [template_theme]
Use the Data in Templates:
Access the value in Twig via the additionalData object:
{% if additionalData.template_theme == 'dark' %}
<body class="dark-theme">
{% endif %}
Unlocalized Fields (e.g., template_theme):
Stored in the base Page/Article entity (shared across all languages).
Configure via unlocalized_keys in YAML.
Localized Fields (e.g., notes):
Stored in the dimension-specific PageDimensionContent/ArticleDimensionContent.
Configure via localized_keys in YAML.
Example Workflow:
notes field to your form:
<property name="notes" type="textarea" mandatory="false"/>
alengo_content_extra:
page:
localized_keys: [notes]
{{ additionalData.notes|default('No notes') }}
Use the sourceLink and sourceUuid markers for link-type pages:
{% if navlink.sourceLink %}
<a href="{{ navlink.sourceUuid }}" class="external-link">View Original</a>
{% endif %}
{% if navlink.sourceUuid == 'some-uuid' %}
<div class="promo-banner">Special Offer!</div>
{% endif %}
Override the default entities in alengo_content_extra.yaml:
alengo_content_extra:
page:
page_class: App\Entity\CustomPage
entity_class: App\Entity\CustomPageDimensionContent
Ensure your custom entities extend the bundle’s base classes:
// src/Entity/CustomPage.php
namespace App\Entity;
use Alengo\SuluContentExtraBundle\Entity\Page as BasePage;
class CustomPage extends BasePage
{
// Add custom fields/methods
}
Leverage translation order to reorder admin tabs:
sulu_admin.template_group.* keys in translations:
# translations/admin+intl-icu.en.yaml
sulu_admin:
template_group:
page_additional_data: 50 # Higher = appears later
seo: 10
SortedGroupProvider will order tabs by these values.config/forms/ and reference them via form_key in YAML.mandatory="true") or add custom validation via Symfony’s constraints in your form XML:
<property name="campaign_id" type="text" mandatory="true">
<meta>
<constraints>
<constraint name="NotBlank" />
<constraint name="Regex" params="{{ pattern: '/^[A-Z0-9_-]+$/' }}" />
</constraints>
</meta>
</property>
{{ additionalData.template_theme }}
{{ additionalData.notes|default }}
{% if navlink.sourceLink %}
{{ navlink.sourceUuid }}
{% endif %}
Access additional data via the Sulu API or Doctrine:
// Get additional data for a Page
$page = $pageRepository->find($uuid);
$dimensionContent = $page->getDimensionContent($locale);
$additionalData = $dimensionContent->getAdditionalData(); // Returns array
Proxy Generation in Development:
auto_generate_proxy_classes: true in dev environments and false in prod (as recommended in the docs). Run cache:warmup after changes:
php bin/console cache:warmup --env=prod
Form Key Conflicts:
page_additional_data or article_additional_data already exists in your project, the bundle’s auto-registration will conflict.form_key in YAML:
alengo_content_extra:
page:
form_key: custom_page_extra_data
Localization Mismatches:
unlocalized in YAML but accessed as localized (or vice versa) will return null.{{ dump(additionalData) }}
Navigation Link Markers Not Appearing:
sourceLink/sourceUuid may not show up if the page isn’t of "link" type.type is set to link in Sulu’s admin panel. Verify the NavigationLinkEnhancer is active (it’s auto-registered by default).Doctrine Schema Updates:
additionalData JSON column to pa_page_dimension_contents and ar_article_dimension_contents. If your database is already customized, migrations may fail.ALTER TABLE pa_page_dimension_contents ADD COLUMN additionalData JSON;
Check Entity Overrides: Verify the bundle’s entities are being used:
php bin/console debug:container Alengo\SuluContentExtraBundle\Entity\Page
Should return the bundle’s Page class, not Sulu’s.
Form Registration: Ensure your form is registered:
php bin/console debug:form page_additional_data
If not found, check for XML syntax errors or missing config/forms/ directory.
Additional Data Storage: Inspect the database directly to verify data is stored correctly:
SELECT additionalData FROM pa_page_dimension_contents WHERE uuid = 'your-page-uuid';
Template Debugging:
Dump the additionalData object in Twig to inspect its structure:
{{ dump(additional
How can I help you explore Laravel packages today?