A Drupal module that provides a block plugin that can render any other block plugin the system. This allows for dynamic block selection and configuration through an administrative interface.
The proxy block is likely only useful for the A/B Blocks sub-module in the A/B Tests project.
- Dynamic Block Selection: Choose any available block plugin from a dropdown
- AJAX Configuration: Real-time configuration forms that update based on block selection
- Context Mapping: Pass page contexts to target blocks that require them
- Access Control: Respects target block access permissions
- Cache Integration: Properly handles cache metadata from target blocks
- Layout Builder Compatible: Works seamlessly in Layout Builder and traditional Block UI
- A/B Testing: Switch between different blocks for testing
- Conditional Block Display: Show different blocks based on configuration
- Block Reusability: Use the same block configuration in multiple places
- Dynamic Content: Change block content without rebuilding layouts
-
Target Block Selection
- Dropdown list of all available block plugins (excluding the proxy block itself)
- Option to select "Do not render any block" to hide the block completely
- AJAX-powered selection that immediately updates the configuration form
-
Target Block Configuration
- Dynamic configuration form that appears after selecting a target block
- Shows the same configuration options as the target block would normally have
- Updated in real-time via AJAX when changing target block selection
- Displays informational message for blocks without configuration options
-
Context Mapping (when applicable)
- Appears for blocks that require contexts (e.g., node, user, term contexts)
- Dropdown for each required context to map to available page contexts
- Required context mappings are marked as mandatory
- Validates that all required contexts are properly mapped
The administrative interface follows Drupal's standard block configuration patterns:
- Block Library: Available through standard block placement UI
- Layout Builder: Can be added as any other block in Layout Builder
- Configuration: Accessed through standard block configuration forms
- Validation: Real-time validation of target block configuration and context mapping
The main block plugin that implements the proxy functionality:
- Extends:
BlockBase
- Implements:
ContainerFactoryPluginInterface
,ContextAwarePluginInterface
- Pattern: Uses final class with constructor promotion and dependency injection
- Block Creation: Proxy block is instantiated by Drupal's block system
- Service Injection: Required services (BlockManager, Logger, CurrentUser) are injected
- Configuration Loading: Saved configuration is loaded from storage
- Form Building: Administrative configuration form is built
- Target Selection: Available block plugins are enumerated and presented
- AJAX Handling: Target block selection triggers AJAX callback
- Dynamic Form: Target block configuration form is dynamically loaded
- Context Discovery: Required contexts for target block are identified
- Validation: Form submission validates target block configuration and context mapping
-
Target Block Creation:
$target_block = $this->blockManager->createInstance($plugin_id, $block_config);
-
Context Mapping:
if ($target_block instanceof ContextAwarePluginInterface) { $this->passContextsToTargetBlock($target_block); }
-
Access Control:
$access_result = $target_block->access($this->currentUser, TRUE);
-
Content Generation:
$build = $target_block->build();
-
Cache Metadata:
$this->bubbleTargetBlockCacheMetadata($build, $target_block);
The module handles context passing through a sophisticated mapping system:
- Inspects target block's context definitions using
getContextDefinitions()
- Identifies required vs optional contexts
- Builds mapping form for each context requirement
- Maps proxy block's available contexts to target block's required contexts
- Supports both automatic mapping (same context name) and manual mapping
- Validates that all required contexts are mapped before allowing form submission
protected function passContextsToTargetBlock(ContextAwarePluginInterface
$target_block): void {
$proxy_contexts = $this->getContexts();
$context_mapping = $this->getConfiguration()['context_mapping'] ?? [];
// Map contexts based on configuration
foreach ($target_context_definitions as $name => $definition) {
$source_name = $context_mapping[$name] ?? $name;
if (isset($proxy_contexts[$source_name])) {
$target_block->setContext($name, $proxy_contexts[$source_name]);
}
}
}
The module properly handles cache metadata to ensure optimal performance:
- Merges proxy block cache contexts with target block cache contexts
- Ensures cache varies on all relevant parameters
- Bubbles target block cache tags to proxy block
- Adds proxy-specific cache tags for invalidation
- Uses most restrictive max-age between proxy and target blocks
- Ensures proper cache invalidation timing
The module implements comprehensive error handling:
- Catches
PluginException
during target block instantiation - Logs errors with context information
- Gracefully degrades to empty render array
- Catches
ContextException
during context mapping - Logs context-related errors
- Continues execution with available contexts
- Validates target block configuration
- Validates required context mappings
- Provides user-friendly error messages
This module follows several advanced Drupal development patterns:
- Uses
array_map
,array_filter
,array_reduce
instead of foreach loops - Implements functional composition for data transformation
- Uses arrow functions for simple transformations
- Uses strategy pattern for different block types
- Leverages PHP 8 match expressions for clean branching
- Implements interface-based behavior detection
- Constructor promotion for clean dependency injection
- Auto-wiring of services through ContainerFactoryPluginInterface
- Minimal service coupling
- PHP 8.1+ features including constructor promotion
- Strict typing with
declare(strict_types=1)
- Final classes for performance and encapsulation
- Union types for intersection constraints
- Place module in
modules/contrib/proxy_block
or install via Composer - Enable the module:
drush en proxy_block
- Clear caches:
drush cr
- The "Proxy Block" will be available in the block library
No global configuration is required. Each proxy block instance is configured individually through the standard Drupal block configuration interface.
- Drupal: 10.x, 11.x
- PHP: 8.1+
- Layout Builder: Full compatibility
- Block UI: Full compatibility
- Context System: Full integration
- Only supports block plugins, not content blocks from the Block Library
- Context mapping requires manual configuration for complex scenarios
- Performance depends on target block performance characteristics
- Respects all target block access permissions
- Does not bypass Drupal's security layer
- Validates all user input through Drupal's form API
- Logs security-relevant events for audit trails
- Lazy-loads target blocks only when needed
- Properly caches target block output
- Minimizes database queries through proper caching
- Uses AJAX for responsive administrative interface
This module follows Drupal coding standards and modern PHP practices. When contributing:
- Use final classes where possible
- Implement constructor promotion
- Favor functional programming patterns
- Use polymorphism over conditional logic
- Include comprehensive error handling
- Write tests for all new functionality
For issues, feature requests, or contributions, please use the project's issue queue on Drupal.org.