You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Drupal 8实体字段与外部数据源的双向同步?

Absolutely! You can absolutely achieve syncing fields in your content types and taxonomies with external data sources using Drupal hooks, events, and the external_entities module is indeed a powerful tool for this scenario—let me break this down for you in detail:

Using Hooks & Events for Basic Sync

If you want a custom, lightweight solution without relying on a dedicated module, hooks and events work perfectly for both pulling data into Drupal and pushing changes back to your external source.

Pulling External Data into Fields

  • Cron-based sync: Use hook_cron() to periodically fetch data from your external API/database, load the target Drupal entities (content types or taxonomy terms), and update their fields. Example snippet:
    function my_custom_module_cron() {
      // Fetch data from external source
      $external_data = \Drupal::httpClient()->get('https://api.example.com/data')->getBody();
      $items = json_decode($external_data, TRUE);
    
      foreach ($items as $item) {
        // Load existing entity by external ID (store this in a custom field)
        $nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
          'field_external_id' => $item['id']
        ]);
        $node = $nodes ? reset($nodes) : \Drupal::entityTypeManager()->getStorage('node')->create(['type' => 'my_content_type']);
    
        // Update fields
        $node->set('title', $item['title']);
        $node->set('field_description', $item['description']);
        $node->set('field_external_id', $item['id']);
        $node->save();
      }
    }
    
  • Event-driven sync: For real-time updates, use Drupal's event system (Drupal 8+) to subscribe to events like EntityInsertEvent or EntityUpdateEvent, or even custom events triggered when your external source sends a webhook.

Pushing Drupal Changes Back to External Source

Use hooks like hook_entity_presave() or hook_entity_update() to detect when a field changes, then send the new value to your external source. Just make sure to avoid infinite loops (e.g., add a flag to skip sync if the change came from the external source itself):

function my_custom_module_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
  // Only sync content type 'my_content_type'
  if ($entity->getEntityTypeId() !== 'node' || $entity->bundle() !== 'my_content_type') {
    return;
  }

  // Skip if change was triggered by external sync (use a hidden flag field)
  if ($entity->get('field_sync_flag')->value === 'external') {
    $entity->set('field_sync_flag', '');
    return;
  }

  // Push updated data to external API
  try {
    \Drupal::httpClient()->put(
      'https://api.example.com/data/' . $entity->get('field_external_id')->value,
      [
        'json' => [
          'title' => $entity->getTitle(),
          'description' => $entity->get('field_description')->value
        ]
      ]
    );
  } catch (\Exception $e) {
    \Drupal::logger('my_custom_module')->error('Failed to push update to external source: @message', ['@message' => $e->getMessage()]);
  }
}
Deep Dive into the external_entities Module

The external_entities module is built specifically for this use case—it lets you treat external data as native Drupal entities, eliminating the need to write all the sync logic from scratch. Its power comes from two core plugin types:

1. External Entity Storage Plugin

This plugin handles all communication with your external data source. You’ll need to create a custom plugin to implement methods for:

  • load()/loadMultiple(): Fetch data from the external source
  • create()/update()/delete(): Push changes from Drupal back to the source
  • mapFromExternal()/mapToExternal(): Convert data between external and Drupal formats

Here’s a simplified example of a REST API storage plugin:

<?php

namespace Drupal\my_custom_module\Plugin\ExternalEntityStorage;

use Drupal\external_entities\Plugin\ExternalEntityStorageBase;
use GuzzleHttp\ClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @ExternalEntityStorage(
 *   id = "my_rest_storage",
 *   label = @Translation("My REST API Storage"),
 *   description = @Translation("Syncs entities with a remote REST API.")
 * )
 */
class MyRestStorage extends ExternalEntityStorageBase {

  protected $httpClient;

  public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $http_client) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->httpClient = $http_client;
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('http_client')
    );
  }

  public function load($id) {
    try {
      $response = $this->httpClient->get("https://api.example.com/entities/$id");
      $external_data = json_decode($response->getBody(), TRUE);
      return $this->mapFromExternal($external_data);
    } catch (\Exception $e) {
      \Drupal::logger('my_custom_module')->error('Failed to load external entity @id: @message', ['@id' => $id, '@message' => $e->getMessage()]);
      return NULL;
    }
  }

  public function update($entity) {
    try {
      $external_data = $this->mapToExternal($entity);
      $this->httpClient->put(
        "https://api.example.com/entities/{$entity->id()}",
        ['json' => $external_data]
      );
      return TRUE;
    } catch (\Exception $e) {
      \Drupal::logger('my_custom_module')->error('Failed to update external entity @id: @message', ['@id' => $entity->id(), '@message' => $e->getMessage()]);
      return FALSE;
    }
  }

  // Implement other required methods (loadMultiple, create, delete) as needed
}

2. External Entity Mapping Plugin

This plugin defines how external data fields map to Drupal entity fields. You can even add custom callbacks for complex mappings (like linking external category IDs to Drupal taxonomy terms):

<?php

namespace Drupal\my_custom_module\Plugin\ExternalEntityMapping;

use Drupal\external_entities\Plugin\ExternalEntityMappingBase;

/**
 * @ExternalEntityMapping(
 *   id = "my_rest_mapping",
 *   label = @Translation("My REST API Mapping"),
 *   description = @Translation("Maps external API fields to Drupal entity fields.")
 * )
 */
class MyRestMapping extends ExternalEntityMappingBase {

  public function getMapping() {
    return [
      'title' => 'external_title',
      'field_description' => 'external_description',
      'field_category' => [
        'external_field' => 'external_category_id',
        'callback' => [$this, 'mapCategoryTerm'],
      ],
    ];
  }

  // Custom callback to map external category ID to Drupal taxonomy term
  public function mapCategoryTerm($external_value, $entity, $field_name) {
    $term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
      'field_external_category_id' => $external_value
    ]);
    return $term ? ['target_id' => reset($term)->id()] : [];
  }
}

Step-by-Step Setup for external_entities

  1. Install and enable the external_entities module.
  2. Go to Structure > External Entity Types and create a new external entity type (configure its label, entity keys, and link it to your target content type/taxonomy).
  3. Create a custom module and add your storage and mapping plugins (as shown above).
  4. Edit your external entity type configuration and select your custom storage and mapping plugins.
  5. Test the sync: Visit the external entity list page to confirm data loads from the external source, edit an entity and save to verify changes push back.
Best Practices
  • Cache external data: The external_entities module supports caching to reduce API calls—configure cache lifetimes in your external entity type settings.
  • Handle errors gracefully: Always add try/catch blocks in your plugins and log errors to avoid breaking Drupal.
  • Avoid infinite loops: Add a flag field to mark when an entity update comes from the external source, so you don’t push those changes back.
  • Use batch/queue for large datasets: For syncing hundreds/thousands of items, use Drupal’s batch API or queue system to prevent timeouts.

内容的提问来源于stack exchange,提问作者Stephen Winnall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:38:14