{"id":29970615,"url":"https://github.com/lullabot/proxy_block","last_synced_at":"2026-05-26T22:01:37.555Z","repository":{"id":306791071,"uuid":"1026622450","full_name":"Lullabot/proxy_block","owner":"Lullabot","description":"Renders a different block instead","archived":false,"fork":false,"pushed_at":"2026-05-20T16:16:20.000Z","size":8471,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T21:44:18.437Z","etag":null,"topics":["drupal","drupal-module"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lullabot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-26T09:04:30.000Z","updated_at":"2026-05-20T16:16:10.000Z","dependencies_parsed_at":"2025-08-12T00:13:17.628Z","dependency_job_id":"55e3ab3a-e801-4a8c-aa04-af9002651cdf","html_url":"https://github.com/Lullabot/proxy_block","commit_stats":null,"previous_names":["lullabot/proxy_block"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/Lullabot/proxy_block","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fproxy_block","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fproxy_block/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fproxy_block/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fproxy_block/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lullabot","download_url":"https://codeload.github.com/Lullabot/proxy_block/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fproxy_block/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33540617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"ssl_error","status_checked_at":"2026-05-26T15:22:15.568Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["drupal","drupal-module"],"created_at":"2025-08-04T05:39:38.041Z","updated_at":"2026-05-26T22:01:37.517Z","avatar_url":"https://github.com/Lullabot.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Proxy Block\n\nA 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\nadministrative interface.\n\n## Overview\n\nThe 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.\n\n## Features\n\n- **Dynamic Block Selection**: Choose any available block plugin from a dropdown\n- **AJAX Configuration**: Real-time configuration forms that update based on block\n  selection\n- **Context Mapping**: Pass page contexts to target blocks that require them\n- **Access Control**: Respects target block access permissions\n- **Cache Integration**: Properly handles cache metadata from target blocks\n- **Layout Builder Compatible**: Works seamlessly in Layout Builder and traditional\n  Block UI\n\n## Use Cases\n\n- **A/B Testing**: Switch between different blocks for testing\n- **Conditional Block Display**: Show different blocks based on configuration\n- **Block Reusability**: Use the same block configuration in multiple places\n- **Dynamic Content**: Change block content without rebuilding layouts\n\n## User Interface\n\n### Block Configuration\n\n1.  **Target Block Selection**\n    - Dropdown list of all available block plugins (excluding the proxy block itself)\n    - Option to select \"Do not render any block\" to hide the block completely\n    - AJAX-powered selection that immediately updates the configuration form\n\n2.  **Target Block Configuration**\n    - Dynamic configuration form that appears after selecting a target block\n    - Shows the same configuration options as the target block would normally have\n    - Updated in real-time via AJAX when changing target block selection\n    - Displays informational message for blocks without configuration options\n\n3.  **Context Mapping** (when applicable)\n    - Appears for blocks that require contexts (e.g., node, user, term contexts)\n    - Dropdown for each required context to map to available page contexts\n    - Required context mappings are marked as mandatory\n    - Validates that all required contexts are properly mapped\n\n### Administrative Experience\n\nThe administrative interface follows Drupal's standard block configuration patterns:\n\n- **Block Library**: Available through standard block placement UI\n- **Layout Builder**: Can be added as any other block in Layout Builder\n- **Configuration**: Accessed through standard block configuration forms\n- **Validation**: Real-time validation of target block configuration and context\n  mapping\n\n## Technical Architecture\n\n### Core Components\n\n#### ProxyBlock Plugin (`src/Plugin/Block/ProxyBlock.php`)\n\nThe main block plugin that implements the proxy functionality:\n\n- **Extends**: `BlockBase`\n- **Implements**: `ContainerFactoryPluginInterface`, `ContextAwarePluginInterface`\n- **Pattern**: Uses final class with constructor promotion and dependency injection\n\n### Render Logic\n\n#### Initialization Phase\n\n1.  **Block Creation**: Proxy block is instantiated by Drupal's block system\n2.  **Service Injection**: Required services (BlockManager, Logger, CurrentUser) are\n    injected\n3.  **Configuration Loading**: Saved configuration is loaded from storage\n\n#### Configuration Phase\n\n1.  **Form Building**: Administrative configuration form is built\n2.  **Target Selection**: Available block plugins are enumerated and presented\n3.  **AJAX Handling**: Target block selection triggers AJAX callback\n4.  **Dynamic Form**: Target block configuration form is dynamically loaded\n5.  **Context Discovery**: Required contexts for target block are identified\n6.  **Validation**: Form submission validates target block configuration and context\n    mapping\n\n#### Render Phase\n\n1.  **Target Block Creation**:\n\n    ```php\n    $target_block = $this-\u003eblockManager-\u003ecreateInstance($plugin_id, $block_config);\n    ```\n\n2.  **Context Mapping**:\n\n    ```php\n    if ($target_block instanceof ContextAwarePluginInterface) {\n      $this-\u003epassContextsToTargetBlock($target_block);\n    }\n    ```\n\n3.  **Access Control**:\n\n    ```php\n    $access_result = $target_block-\u003eaccess($this-\u003ecurrentUser, TRUE);\n    ```\n\n4.  **Content Generation**:\n\n    ```php\n    $build = $target_block-\u003ebuild();\n    ```\n\n5.  **Cache Metadata**:\n    ```php\n    $this-\u003ebubbleTargetBlockCacheMetadata($build, $target_block);\n    ```\n\n### Context Handling\n\nThe module handles context passing through a sophisticated mapping system:\n\n#### Context Discovery\n\n- Inspects target block's context definitions using `getContextDefinitions()`\n- Identifies required vs optional contexts\n- Builds mapping form for each context requirement\n\n#### Context Mapping\n\n- Maps proxy block's available contexts to target block's required contexts\n- Supports both automatic mapping (same context name) and manual mapping\n- Validates that all required contexts are mapped before allowing form submission\n\n#### Context Application\n\n```php\nprotected function passContextsToTargetBlock(ContextAwarePluginInterface\n$target_block): void {\n  $proxy_contexts = $this-\u003egetContexts();\n  $context_mapping = $this-\u003egetConfiguration()['context_mapping'] ?? [];\n\n  // Map contexts based on configuration\n  foreach ($target_context_definitions as $name =\u003e $definition) {\n    $source_name = $context_mapping[$name] ?? $name;\n    if (isset($proxy_contexts[$source_name])) {\n      $target_block-\u003esetContext($name, $proxy_contexts[$source_name]);\n    }\n  }\n}\n```\n\n### Cache Integration\n\nThe module properly handles cache metadata to ensure optimal performance:\n\n#### Cache Contexts\n\n- Merges proxy block cache contexts with target block cache contexts\n- Ensures cache varies on all relevant parameters\n\n#### Cache Tags\n\n- Bubbles target block cache tags to proxy block\n- Adds proxy-specific cache tags for invalidation\n\n#### Cache Max Age\n\n- Uses most restrictive max-age between proxy and target blocks\n- Ensures proper cache invalidation timing\n\n### Error Handling\n\nThe module implements comprehensive error handling:\n\n#### Plugin Creation Errors\n\n- Catches `PluginException` during target block instantiation\n- Logs errors with context information\n- Gracefully degrades to empty render array\n\n#### Context Errors\n\n- Catches `ContextException` during context mapping\n- Logs context-related errors\n- Continues execution with available contexts\n\n#### Form Errors\n\n- Validates target block configuration\n- Validates required context mappings\n- Provides user-friendly error messages\n\n## Development Patterns\n\nThis module follows several advanced Drupal development patterns:\n\n### Functional Programming\n\n- Uses `array_map`, `array_filter`, `array_reduce` instead of foreach loops\n- Implements functional composition for data transformation\n- Uses arrow functions for simple transformations\n\n### Polymorphism Over Conditionals\n\n- Uses strategy pattern for different block types\n- Leverages PHP 8 match expressions for clean branching\n- Implements interface-based behavior detection\n\n### Dependency Injection\n\n- Constructor promotion for clean dependency injection\n- Auto-wiring of services through ContainerFactoryPluginInterface\n- Minimal service coupling\n\n### Modern PHP Features\n\n- PHP 8.1+ features including constructor promotion\n- Strict typing with `declare(strict_types=1)`\n- Final classes for performance and encapsulation\n- Union types for intersection constraints\n\n## Installation\n\n1.  Place module in `modules/contrib/proxy_block` or install via Composer\n2.  Enable the module: `drush en proxy_block`\n3.  Clear caches: `drush cr`\n4.  The \"Proxy Block\" will be available in the block library\n\n## Configuration\n\nNo global configuration is required. Each proxy block instance is configured\nindividually through the standard Drupal block configuration interface.\n\n## Compatibility\n\n- **Drupal**: 10.x, 11.x\n- **PHP**: 8.1+\n- **Layout Builder**: Full compatibility\n- **Block UI**: Full compatibility\n- **Context System**: Full integration\n\n## Limitations\n\n- Only supports block plugins, not content blocks from the Block Library\n- Context mapping requires manual configuration for complex scenarios\n- Performance depends on target block performance characteristics\n\n## Security Considerations\n\n- Respects all target block access permissions\n- Does not bypass Drupal's security layer\n- Validates all user input through Drupal's form API\n- Logs security-relevant events for audit trails\n\n## Performance\n\n- Lazy-loads target blocks only when needed\n- Properly caches target block output\n- Minimizes database queries through proper caching\n- Uses AJAX for responsive administrative interface\n\n## Contributing\n\nThis module is not open to public contributions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flullabot%2Fproxy_block","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flullabot%2Fproxy_block","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flullabot%2Fproxy_block/lists"}