https://github.com/lullabot/proxy_block
Renders a different block instead
https://github.com/lullabot/proxy_block
drupal drupal-module
Last synced: 2 months ago
JSON representation
Renders a different block instead
- Host: GitHub
- URL: https://github.com/lullabot/proxy_block
- Owner: Lullabot
- Created: 2025-07-26T09:04:30.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-08-19T16:35:14.000Z (7 months ago)
- Last Synced: 2025-08-19T17:37:53.722Z (7 months ago)
- Topics: drupal, drupal-module
- Language: PHP
- Homepage:
- Size: 2.38 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Proxy Block
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.
## Overview
The proxy block is likely only useful for the A/B Blocks sub-module in the [A/B Tests](https://www.github.com/Lullabot/ab_tests) project.
## Features
- **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
## Use Cases
- **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
## User Interface
### Block Configuration
1. **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
2. **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
3. **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
### Administrative Experience
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
## Technical Architecture
### Core Components
#### ProxyBlock Plugin (`src/Plugin/Block/ProxyBlock.php`)
The main block plugin that implements the proxy functionality:
- **Extends**: `BlockBase`
- **Implements**: `ContainerFactoryPluginInterface`, `ContextAwarePluginInterface`
- **Pattern**: Uses final class with constructor promotion and dependency injection
### Render Logic
#### Initialization Phase
1. **Block Creation**: Proxy block is instantiated by Drupal's block system
2. **Service Injection**: Required services (BlockManager, Logger, CurrentUser) are
injected
3. **Configuration Loading**: Saved configuration is loaded from storage
#### Configuration Phase
1. **Form Building**: Administrative configuration form is built
2. **Target Selection**: Available block plugins are enumerated and presented
3. **AJAX Handling**: Target block selection triggers AJAX callback
4. **Dynamic Form**: Target block configuration form is dynamically loaded
5. **Context Discovery**: Required contexts for target block are identified
6. **Validation**: Form submission validates target block configuration and context
mapping
#### Render Phase
1. **Target Block Creation**:
```php
$target_block = $this->blockManager->createInstance($plugin_id, $block_config);
```
2. **Context Mapping**:
```php
if ($target_block instanceof ContextAwarePluginInterface) {
$this->passContextsToTargetBlock($target_block);
}
```
3. **Access Control**:
```php
$access_result = $target_block->access($this->currentUser, TRUE);
```
4. **Content Generation**:
```php
$build = $target_block->build();
```
5. **Cache Metadata**:
```php
$this->bubbleTargetBlockCacheMetadata($build, $target_block);
```
### Context Handling
The module handles context passing through a sophisticated mapping system:
#### Context Discovery
- Inspects target block's context definitions using `getContextDefinitions()`
- Identifies required vs optional contexts
- Builds mapping form for each context requirement
#### Context Mapping
- 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
#### Context Application
```php
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]);
}
}
}
```
### Cache Integration
The module properly handles cache metadata to ensure optimal performance:
#### Cache Contexts
- Merges proxy block cache contexts with target block cache contexts
- Ensures cache varies on all relevant parameters
#### Cache Tags
- Bubbles target block cache tags to proxy block
- Adds proxy-specific cache tags for invalidation
#### Cache Max Age
- Uses most restrictive max-age between proxy and target blocks
- Ensures proper cache invalidation timing
### Error Handling
The module implements comprehensive error handling:
#### Plugin Creation Errors
- Catches `PluginException` during target block instantiation
- Logs errors with context information
- Gracefully degrades to empty render array
#### Context Errors
- Catches `ContextException` during context mapping
- Logs context-related errors
- Continues execution with available contexts
#### Form Errors
- Validates target block configuration
- Validates required context mappings
- Provides user-friendly error messages
## Development Patterns
This module follows several advanced Drupal development patterns:
### Functional Programming
- Uses `array_map`, `array_filter`, `array_reduce` instead of foreach loops
- Implements functional composition for data transformation
- Uses arrow functions for simple transformations
### Polymorphism Over Conditionals
- Uses strategy pattern for different block types
- Leverages PHP 8 match expressions for clean branching
- Implements interface-based behavior detection
### Dependency Injection
- Constructor promotion for clean dependency injection
- Auto-wiring of services through ContainerFactoryPluginInterface
- Minimal service coupling
### Modern PHP Features
- 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
## Installation
1. Place module in `modules/contrib/proxy_block` or install via Composer
2. Enable the module: `drush en proxy_block`
3. Clear caches: `drush cr`
4. The "Proxy Block" will be available in the block library
## Configuration
No global configuration is required. Each proxy block instance is configured
individually through the standard Drupal block configuration interface.
## Compatibility
- **Drupal**: 10.x, 11.x
- **PHP**: 8.1+
- **Layout Builder**: Full compatibility
- **Block UI**: Full compatibility
- **Context System**: Full integration
## Limitations
- 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
## Security Considerations
- 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
## Performance
- Lazy-loads target blocks only when needed
- Properly caches target block output
- Minimizes database queries through proper caching
- Uses AJAX for responsive administrative interface
## Contributing
This module is not open to public contributions.