Drupal多站点中如何以编程方式加载并显示自定义区块
Great question! Let's break down how to solve this step by step since you're dealing with Drupal multisites with separate databases but shared code. The core challenges here are cross-database access and identifying matching blocks across sites (since you don't have machine names or consistent block IDs). Here are two practical approaches:
方案1:基于现有数据特征匹配区块(无需修改现有区块)
If you can't add new fields to your blocks right now, you can use existing attributes like block type (bundle), title, or content hash to group "equivalent" blocks across sites. Here's how to implement this:
Step 1:配置跨数据库连接
首先,你需要能访问每个站点的数据库。可以选择两种方式:
- 在
settings.php中手动添加每个站点的数据库配置(更安全,避免硬编码在模块里),或者 - 扫描
sites/目录自动检测站点配置(更灵活,但实现稍复杂)
示例:在settings.php中添加额外数据库
$databases['site1']['default'] = [ 'database' => 'site1_db', 'username' => 'db_user', 'password' => 'db_pass', 'host' => 'localhost', 'driver' => 'mysql', ]; $databases['site2']['default'] = [ // 第二个站点的数据库凭证 ];
Step 2:跨站点获取区块
在自定义模块的预处理函数或控制器中,遍历每个站点的数据库,查询匹配条件的区块并加载实体:
/** * Implements hook_preprocess_HOOK() for自定义模板. */ function custom_module_preprocess_my_template(&$variables) { $all_blocks = []; // 从settings.php中获取多站点数据库标识 $site_db_keys = ['site1', 'site2']; foreach ($site_db_keys as $site_key) { try { // 切换到当前站点的数据库连接 $original_db = Database::getConnection(); Database::setActiveConnection($site_key); // 查询区块:根据你的需求调整匹配条件,比如区块类型+标题 $block_ids = \Drupal::database()->select('block_content_field_data', 'bcfd') ->fields('bcfd', ['id']) ->condition('bcfd.bundle', 'testimonial') // 你的自定义区块类型 ->condition('bcfd.title', 'Customer Spotlight') // 匹配的区块标题 ->execute() ->fetchCol(); // 加载区块并检查访问权限 foreach ($block_ids as $bid) { $block = \Drupal\block_content\Entity\BlockContent::load($bid); if ($block && $block->access('view')) { $all_blocks[] = [ 'site_label' => ucfirst($site_key), 'block' => $block, ]; } } // 切回原站点的数据库连接 Database::setActiveConnection($original_db->getKey()); } catch (\Exception $e) { \Drupal::logger('custom_module')->error('加载@site站点区块失败:@error', [ '@site' => $site_key, '@error' => $e->getMessage(), ]); } } // 将区块数据传递给模板 $variables['multisite_blocks'] = $all_blocks; }
Step 3:在模板中渲染区块
在Twig模板中,使用view过滤器正确渲染每个区块:
{% for item in multisite_blocks %} <div class="multisite-block-container"> <h4>来自站点:{{ item.site_label }}</h4> {{ item.block|view }} </div> {% endfor %}
方案2:添加统一标识字段(更可靠的长期方案)
如果可以修改区块内容类型,添加一个自定义机器名字段会让跨站点匹配区块的准确性大幅提升:
- 添加自定义字段:进入
/admin/structure/block-content/manage/[你的区块类型]/fields,添加一个文本字段(比如field_block_machine_name),设置为唯一值,可隐藏编辑表单避免误改。 - 批量更新现有区块:使用Drush或自定义脚本,给所有站点的等效区块设置相同的机器名。
- 基于机器名查询:调整方案1中的数据库查询条件,用
field_block_machine_name替代标题/类型,彻底避免重复标题导致的误匹配。
关键注意事项
- 数据库权限:确保Drupal的数据库用户拥有所有多站点数据库的读取权限。
- 性能优化:使用Drupal的
CacheBackendInterface缓存查询结果,设置合理的缓存失效规则(比如区块更新时清除缓存),避免每次页面加载都遍历所有数据库。 - 安全控制:始终检查用户的区块访问权限(
$block->access('view')),防止泄露其他站点的受限内容。 - 错误处理:用try/catch包裹数据库操作,优雅处理连接失败等异常情况。
内容的提问来源于stack exchange,提问作者Prajila V P

