Problem/Motivation
The content_firstmodule currently implements its own entity rendering logic in ContentFirstBuilder::renderContent(), manually handling:
- View builder instantiation and render array construction
- Drupal deprecation compatibility (renderInIsolation vs renderPlain)
- Context management during rendering
The entity_render_contextmodule provides an identical service for rendering entities with configurable context (theme, user, language, view mode), including:
- Built-in request-scoped caching to avoid redundant renders
- Proper context restoration with guaranteed cleanup (via finally blocks)
- Centralized deprecation handling for Drupal 10.3+ compatibility
- Comprehensive error handling with logging
Integration benefits:
- Eliminates code duplication between modules
- Improves performance through shared caching
- Reduces maintenance burden (deprecation logic managed in one place)
- Better separation of concerns (entity_render_context handles rendering complexity)
- Ensures consistent rendering behavior across dependent modules
Steps to reproduce
N/A - This is a refactoring to integrate an external service, not a bug fix.
To verify the integration after applying changes:
- Enable both
entity_render_contextandcontent_firstmodules - Clear cache:
drush cr - Navigate to any node and click the "Content First" tab
- Verify content renders correctly (no visual changes expected)
- Export content as markdown and verify formatting is preserved
Proposed resolution
Refactor content_first to use entity_render_context.renderer service:
-
Add module dependency: Declare
entity_render_contextas a dependency incontent_first.info.yml -
Update service injection: Replace
@rendererwith@entity_render_context.rendererincontent_first.services.yml -
Simplify ContentFirstBuilder class:
- Remove imports:
DeprecationHelper,RendererInterface - Add import:
EntityRenderContextInterface - Update property:
$renderer→$entityRenderContextwith new type - Update constructor parameter and assignment
- Replace
renderContent()method with single service call
- Remove imports:
-
No interface changes required:
ContentFirstBuilderInterface::buildContent()signature remains unchanged - No consumer changes needed: Controllers and plugins depend on the interface, not the implementation
Before (ContentFirstBuilder::renderContent):
protected function renderContent(ContentEntityInterface $entity, string $view_mode) : string {
$view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
$pre_render = $view_builder->view($entity, $view_mode);
$render_output = DeprecationHelper::backwardsCompatibleCall(
currentVersion: \Drupal::VERSION,
deprecatedVersion: '10.3',
currentCallable: fn() => $this->renderer->renderInIsolation($pre_render),
deprecatedCallable: fn() => $this->renderer->renderPlain($pre_render),
);
return $render_output;
}
After (using entity_render_context):
protected function renderContent(ContentEntityInterface $entity, string $view_mode) : string {
// Use entity_render_context service to render the entity.
// Parameters: entity, view_mode, theme (NULL=default), account (NULL=anonymous), langcode (NULL=entity language)
$render_output = $this->entityRenderContext->renderEntity(
$entity,
$view_mode,
NULL, // Use current/default theme
NULL, // Render as anonymous user (uid 0)
NULL, // Use entity's language
);
return $render_output ?? '';
}
Rendering context preserved:
- Theme: NULL parameter uses current/default theme (matches existing behavior)
- User: NULL parameter renders as anonymous user uid 0 (clean exports for content review)
- Language: NULL parameter respects entity's language (matches existing behavior)
Remaining tasks
- Apply changes to content_first module in the repository
- Clear Drupal cache to rebuild service container
- Run PHPCS and PHPStan checks on modified files
- Perform functional testing:
- View content_first tab on various node types
- Export content as markdown ZIP
- Test token replacements
- Verify multilingual content renders correctly
- Regression testing: Compare output before/after changes (should be identical)
- Performance validation: Verify caching benefits on repeated renders
- Prepare patch for drupal.org contribution
User interface changes
None. This is a backend refactoring. The content_first module UI, functionality, and output remain unchanged.
API changes
Public API (no breaking changes):
-
ContentFirstBuilderInterface::buildContent()- Signature and behavior unchanged - All public/protected methods retain same signatures
Internal implementation changes (not part of public API):
- Property
$rendererrenamed to$entityRenderContext(internal property, not part of public API) - Constructor parameter type changed from
RendererInterfacetoEntityRenderContextInterface(service injection, managed by container)
New module dependency:
- content_first now requires
entity_render_contextmodule to function
Data model changes
None. This refactoring affects only the rendering service layer. No database schema, configuration, or entity definitions are changed.
Issue fork content_first-3571484
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
eduardo morales albertiCoding standards stabilized, ready to review
Comment #4
eduardo morales albertiComment #6
eduardo morales albertiFixed! Created new release with the new dependency