Extending the module (custom development)

Last updated on
17 June 2025

Extend/alter exporting of an entity?

You can implement a custom event subscriber to subscribe to ExportEvent::class e.g.

Simple example:

<?php

namespace Drupal\my_module\EventSubscriber;

use Drupal\single_content_sync\Event\ExportEvent;
use Drupal\single_content_sync\Event\ImportEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * An event subscriber to extend single_content_sync functionality.
 */
class ContentSyncSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      ExportEvent::class => ['onExport'],
      ImportEvent::class => ['onImport'],
    ];
  }

  /**
   * Subscriber on content export.
   *
   * @param \Drupal\single_content_sync\Event\ExportEvent $event
   *   The content export event.
   */
  public function onExport(ExportEvent $event): void {
    $entity = $event->getEntity();
    $content = $event->getContent();

    switch ($entity->getEntityTypeId()) {
      case 'node':
        $content['base_fields']['node_keeper'] = $entity->get('node_keeper')->value;
        $content['base_fields']['alias_keeper'] = $entity->get('alias_keeper')->value;
        $content['base_fields']['keeper_machine_name'] = $entity->get('keeper_machine_name')->value;
        break;
    }

    $event->setContent($content);
  }

  /**
   * Subscriber on content import.
   *
   * @param \Drupal\single_content_sync\Event\ImportEvent $event
   *   The content import event.
   */
  public function onImport(ImportEvent $event): void {
    $entity = $event->getEntity();
    $content = $event->getContent();

    switch ($entity->getEntityTypeId()) {
      case 'node':
        $entity->set('node_keeper', $content['base_fields']['node_keeper']);
        $entity->set('alias_keeper', $content['base_fields']['alias_keeper']);
        $entity->set('keeper_machine_name', $content['base_fields']['keeper_machine_name']);
        break;
    }
  }

}

Advanced example:

class ContentExportSubscriber implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      ExportEvent::class => ['onExport'],
    ];
  }

  public function onExport(ExportEvent $event) {
    $entity = $event->getEntity();
    $output = $event->getContent();
    $is_translation = $event->isTranslation();


    if ($entity instanceof MediaInterface) {
      switch ($entity->bundle()) {
        case 'gpx':
          // Change bundle for original export.
          if (!$is_translation) {
            $output['bundle'] = 'file'; 
          }
          break;
      }
    }

    if ($entity instanceof BlockContentInterface) {
      switch ($entity->bundle()) {
        case 'cb_focus':
          // Rename field during export.
          $logos = $output['custom_fields']['field_cb_logos'];
          unset($output['custom_fields']['field_cb_logos']);
          $output['custom_fields']['field_cb_media_unlimited'] = $logos;
          break;
      }
    }

    $event->setContent($output);
  }

}

Another option is to implement a custom plugin SingleContentSyncBaseFieldsProcessor where you can isolate your entity export/import logic into a single plugin.

Check out a few examples of existing plugins at src/Plugin/SingleContentSyncBaseFieldsProcessor.

Here is an example of custom SingleContentSyncBaseFieldsProcessor implementation.

class CustomEntityType extends SingleContentSyncBaseFieldsProcessorPluginBase {

  /**
   * {@inheritdoc}
   */
  public function exportBaseValues(FieldableEntityInterface $entity): array {
    return [
      'info' => $entity->label(),
      'reusable' => $entity->isReusable(),
      'langcode' => $entity->language()->getId(),
      'custom_entity_type_revision_id' => $entity->getRevisionId(),
      'enforce_new_revision' => TRUE,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function mapBaseFieldsValues(array $values, FieldableEntityInterface $entity): array {
    return [
      'langcode' => $values['langcode'],
      'info' => $values['info'],
      'reusable' => $values['reusable'],
    ];
  }

}

Extend/alter importing of an entity

Similar to exporting, there's an ImportEvent which could be subscribed to alter the imported entity before it's saved.

For fields, the event is ImportFieldEvent.

Exporting and importing custom field types

You can implement SingleContentSyncFieldProcessor plugin for your custom field type that has some custom logic (not just get and set value).

The plugin should include both import and export logic (and it's pretty much straightforward).

class CustomField extends SingleContentSyncFieldProcessorPluginBase {

  /**
   * {@inheritdoc}
   */
  public function exportFieldValue(FieldItemListInterface $field): array {
    return $field->getValue();
  }

  /**
   * {@inheritdoc}
   */
  public function importFieldValue(FieldableEntityInterface $entity, string $fieldName, array $value): void {
    $entity->set($fieldName, $value);
  }

}

If you want to alter exporting of a field, you can implement a custom event subscriber with the following event:

public static function getSubscribedEvents(): array {
  return [
    ExportFieldEvent::class => ['onFieldExport'],
  ];
}

If you want to alter setting the value of the field into the entity, you can use the following event:

public static function getSubscribedEvents(): array {
  return [
    ImportFieldEvent::class => ['onFieldImport'],
  ];
}

More examples with each event subscriber is coming soon.

Altering an existing plugin

If you need to alter existing plugin implementation of a field processor, you can use the following hook. Simply define your own class and extend the existing one, so you will be able to override the logic of exporting/importing a field.

function hook_single_content_sync_field_processor_info_alter(&$definitions) {
  $definitions['file_asset']['class'] = MyCustomFileAsset::class;
}

The same is applicable for entity type.

function hook_single_content_sync_base_fields_processor_info_alter(&$definitions) {
  $definitions['node']['class'] = NodeOverride::class;
}

Hooks for export (deprecated)

Note: Using hooks is deprecated as of 1.4.x and will be removed in 2.0.x. Please use Plugins as of 1.4.x.

You can export the base fields of your custom entity or alter the base fields of the supported entity using this hook:

hook_content_export_entity_alter(array &$base_fields, FieldableEntityInterface $entity)

You can extend the exporting of your custom field type. Here is the hook to alter the exporting of a field value.

hook_content_export_field_value_alter(&$value, FieldItemListInterface $field)

You can use Drush commands to export and import your content.

This feature is available since 1.4.0 release.

Hooks for import (deprecated)

Here is the hook to alter the importing of an entity:

hook_content_import_entity_alter(array $content, FieldableEntityInterface $entity)

Here is a hook to alter the importing of a field value:

hook_content_import_field_value_alter(FieldableEntityInterface $entity, $field_name, $field_value)

Help improve this page

Page status: No known problems

You can: