Skip to main content

Register a new module

The twocream Shopware Connector Bundle offers two ways to implement an import module.

AbstractModule is suitable for simple entities and is used, for example, for attribute imports. It is also used when you want to import flat data structures from the MDM area into your own Shopware entities.

AbstractSyncModule is suitable for more complex entities, such as the Shopware product entity. It provides the option to create, update, or delete multiple entities during processing. For example, relational records like Shopware units of measure.

Structure of a simple module

info

Simple modules must inherit from this abstract class: Twocream\JsonImporter\Core\System\Import\Module\AbstractModule

<?php

namespace MyCustomExtension\Core\System\Import\Module;

use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Twocream\JsonImporter\Core\System\Import\Module\AbstractModule;
use Twocream\JsonImporter\Core\System\Import\Validation\Decision;
use Twocream\JsonImporter\Core\System\Import\Validation\Result;
use MyCustomExtension\Core\Content\Pimcore\MasterDataManagement\Video\VideoDefinition;

class VideoModule extends AbstractModule
{
public function getHandledPackage(): string
{
return 'videos';
}

protected function getEntityName(): string
{
return VideoDefinition::ENTITY_NAME;
}

protected function validateEntry(array $entry): array
{
$problems = [];

if (empty($entry['uuid'])) {
$problems[] = 'uuid';
}

if (empty($entry['id'])) {
$problems[] = 'id';
}

return $problems;
}

protected function store(Result $result): ?EntityWrittenContainerEvent
{
$records = [];
$videoRepository = $this->registry->getRepository('app_pimcore_video');

foreach ($result as $entry) {
if ($entry->getDecision() === Decision::DECISION_SKIP_RECORD) {
continue;
}

$translations = $this->service->prepareTranslation($entry['texts']['translations']);
$videoTranslations = $this->service->prepareTranslation($entry['video']['translations'], [
'youtube-link' => 'youtubeLink'
]);
$thumbnailTranslations = $this->service->prepareTranslation($entry['thumbnail']['translations'], [
'image' => 'thumbnailImage',
'title' => 'thumbnailTitle',
'alt-text' => 'thumbnailAltText'
]);

foreach ($translations as $key => $values) {
if (isset($videoTranslations[$key])) {
$translations[$key] = array_merge($values, $videoTranslations[$key]);
} else {
$translations[$key] = array_merge($values, ['youtubeLink' => null]);
}

if (isset($thumbnailTranslations[$key])) {
$translations[$key] = array_merge($translations[$key], $thumbnailTranslations[$key]);
} else {
$translations[$key] = array_merge(
$translations[$key],
['thumbnailImage' => null, 'thumbnailTitle' => null, 'thumbnailAltText' => null]
);
}
}

$records[] = [
'id' => $entry['uuid'],
'pimcoreId' => $entry['id'],
'key' => $entry['key'],
'type' => 'youtube',
'defaultLink' => $entry['video']['default-value'],
'translations' => $translations
];
}

if (!empty($records)) {
return $videoRepository->upsert($records, $this->context);
}

return null;
}
}

Structure of a more complex module

info

Complex modules must inherit from this abstract class: Twocream\JsonImporter\Core\System\Import\Module\AbstractSyncModule

namespace MyCustomExtension\Core\System\Import\Module;

use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
use Twocream\JsonImporter\Core\System\Import\Module\AbstractSyncModule;
use Twocream\JsonImporter\Core\System\Import\Validation\Decision;
use Twocream\JsonImporter\Core\System\Import\Validation\Result;

class ExampleModule extends AbstractSyncModule
{
public function getHandledPackage(): string
{
return 'example';
}

protected function getEntityName(): string
{
return ExampleDefinition::ENTITY_NAME;
}

protected function validateEntry(array $entry): array
{
$problems = [];

if (empty($entry['translations']['de_DE']['name'])) {
$problems[] = 'name';
}

return $problems;
}

protected function store(Result $result): ?EntityWrittenContainerEvent
{
foreach ($result as $entry) {
if ($entry->getDecision() === Decision::DECISION_SKIP_RECORD) {
continue;
}

if (!empty($entry['measurement-unit'])) {
$unitId = md5('unit-' . $entry['measurement-unit']);

if (!$this->isIdExist($unitId, UnitDefinition::ENTITY_NAME)) {
$this->syncs[UnitDefinition::ENTITY_NAME][SyncOperation::ACTION_UPSERT][] = [
'id' => $unitId,
'translations' => $this->service->prepareTranslation([
'de' => [
'name' => $entry['measurement-unit'],
'shortCode' => $entry['measurement-unit']
]
])
];
}
}


$translations = $this->service->prepareTranslation(
$entry['texts'],
[
'description' => 'description',
'name' => 'name',
]
);

$this->syncs[ProductDefinition::ENTITY_NAME][SyncOperation::ACTION_UPSERT][] = [
'id' => $entry['uuid'],
'pimcoreId' => $entry['id'],
'unitId' => $unitId,
'translations' => $translations,
'customFields' => [
'position' => $entry['custom-fields']['position'],
],
];
}

return $this->sync();
}
}

Explanation

The complex module builds upon the simple module, therefore the following definitions apply to both types.

NameDescription
getHandledPackage(): stringUnique package name, e.g., "product"
getEntityName(): stringThe name of the entity the module is intended for, e.g., "product"
store(Result $result): ?EntityWrittenContainerEventHere, the content-related processing of data for import occurs
canBeDeleted(): boolOptionally, it can prevent data deletion
isIdExist(string $id, string $entityName): boolHelper method to check if a record with the given ID & entity already exists
validateEntry(array $entry): arrayOptionally, you can implement validation logic for imported data

Register the module

New modules must be registered in a service configuration file. The service tag twocream_json_importer.module must be used.

Best Practice

For structured configuration, it is recommended to create the file src/Resources/config/services/import.xml.
This can then be included via an import into the central services.xml.

<service id="MyCustomExtension\Core\System\Import\Module\ProductModule">
<tag name="twocream_json_importer.module"/>
</service>