<?php

namespace ShopwareAdapter\ResponseParser\Address;

use ShopwareAdapter\ResponseParser\GetAttributeTrait;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Country\Country;
use SystemConnector\TransferObject\Order\Address\Address;
use SystemConnector\TransferObject\Order\Customer\Customer;

class AddressResponseParser implements AddressResponseParserInterface
{
    use GetAttributeTrait;

    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    public function __construct(IdentityServiceInterface $identityService)
    {
        $this->identityService = $identityService;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $entry)
    {
        $entry['salutation'] = strtolower($entry['salutation']);

        $countryIdentitiy = $this->identityService->findOneBy([
            'adapterIdentifier' => $entry['country']['id'],
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => Country::TYPE,
        ]);

        if (null === $countryIdentitiy) {
            throw new NotFoundException('country mapping missing - ' . json_encode($entry));
        }

        if ($entry['salutation'] === 'mr' || $entry['salutation'] === 'herr') {
            $gender = Customer::GENDER_MALE;
        } elseif ($entry['salutation'] === 'ms' || $entry['salutation'] === 'frau') {
            $gender = Customer::GENDER_FEMALE;
        } else {
            $gender = null;
        }

        $params = [
            'gender' => $gender,
            'firstname' => $entry['firstName'],
            'lastname' => $entry['lastName'],
            'street' => $entry['street'],
            'postalCode' => $entry['zipCode'],
            'city' => $entry['city'],
            'countryIdentifier' => $countryIdentitiy->getObjectIdentifier(),
            'vatId' => !empty($entry['vatId']) ? $entry['vatId'] : null,
        ];

        if (!empty($entry['attribute'])) {
            $params['attributes'] = $this->getAttributes($entry['attribute']);
        }

        if (!empty($entry['department'])) {
            $params['department'] = $entry['department'];
        }

        if (!empty($entry['title'])) {
            $params['title'] = $entry['title'];
        }

        if (!empty($entry['company'])) {
            $params['company'] = $entry['company'];
        }

        if (!empty(trim($entry['additionalAddressLine1']))) {
            $params['additional'] = $entry['additionalAddressLine1'];
        }

        if (isset($entry['phone']) && !empty(trim($entry['phone']))) {
            $params['phoneNumber'] = $entry['phone'];
        }

        return Address::fromArray($params);
    }
}


<?php

namespace SystemConnector\DefinitionProvider;

use SystemConnector\DefinitionProvider\Struct\Definition;
use Traversable;

class DefinitionProvider implements DefinitionProviderInterface
{
    /**
     * @var Definition[]
     */
    private $connectorDefinitions;

    /**
     * @var Definition[]
     */
    private $mappingDefinitions;

    /**
     * @var Definition[]
     */
    private $cleanupDefinitions;

    /**
     * @param Definition[]|Traversable $connectorDefinitions
     * @param Definition[]|Traversable $mappingDefinitions
     * @param Definition[]|Traversable $cleanupDefinitions
     */
    public function __construct(Traversable $connectorDefinitions, Traversable $mappingDefinitions, Traversable $cleanupDefinitions)
    {
        $connectorDefinitions = iterator_to_array($connectorDefinitions);
        $mappingDefinitions = iterator_to_array($mappingDefinitions);
        $cleanupDefinitions = iterator_to_array($cleanupDefinitions);

        $this->connectorDefinitions = $this->filterActiveDefinitions($this->sortDefinitions($connectorDefinitions));
        $this->mappingDefinitions = $this->filterActiveDefinitions($this->sortDefinitions($mappingDefinitions));
        $this->cleanupDefinitions = $this->filterActiveDefinitions($this->sortDefinitions($cleanupDefinitions));
    }

    /**
     * @param null|string $objectType
     *
     * @return Definition[]
     */
    public function getConnectorDefinitions($objectType = null)
    {
        return $this->filterMatchingDefinitions($this->connectorDefinitions, $objectType);
    }

    /**
     * @param null|string $objectType
     *
     * @return Definition[]
     */
    public function getMappingDefinitions($objectType = null)
    {
        return $this->filterMatchingDefinitions($this->mappingDefinitions, $objectType);
    }

    /**
     * @return Definition[]
     */
    public function getCleanupDefinitions()
    {
        return $this->cleanupDefinitions;
    }

    /**
     * @param Definition[] $definitions
     *
     * @return Definition[]
     */
    private function filterActiveDefinitions(array $definitions)
    {
        return array_filter($definitions, function (Definition $definition) {
            if (!$definition->isActive()) {
                return false;
            }

            return true;
        });
    }

    /**
     * @param Definition[] $definitions
     * @param null|string  $objectType
     *
     * @return Definition[]
     */
    private function filterMatchingDefinitions(array $definitions, $objectType)
    {
        return array_filter($definitions, function (Definition $definition) use ($objectType) {
            return strtolower($definition->getObjectType()) === strtolower($objectType) || null === $objectType;
        });
    }

    /**
     * @param Definition[] $definitions
     *
     * @return Definition[]
     */
    private function sortDefinitions(array $definitions)
    {
        usort($definitions, function (Definition $definitionLeft, Definition $definitionRight) {
            if ($definitionLeft->getPriority() === $definitionRight->getPriority()) {
                return 0;
            }

            return ($definitionLeft->getPriority() > $definitionRight->getPriority()) ? -1 : 1;
        });

        return $definitions;
    }
}


<?php

namespace SystemConnector\ValidatorService;

use Assert\InvalidArgumentException;
use DateTimeZone;
use SystemConnector\Validator\ValidatorInterface;
use SystemConnector\ValidatorService\Exception\InvalidDataException;
use Traversable;
use function is_array;

class ValidatorService implements ValidatorServiceInterface
{
    /**
     * @var Traversable|ValidatorInterface[]
     */
    public $validators = [];

    /**
     * @param Traversable|ValidatorInterface[] $validators
     */
    public function __construct(Traversable $validators)
    {
        $this->validators = iterator_to_array($validators);
    }

    /**
     * {@inheritdoc}
     */
    public function validate($object, array $parents = [])
    {
        $validators = array_filter($this->validators, function (ValidatorInterface $validator) use ($object) {
            return $validator->supports($object);
        });

        try {
            array_walk($validators, function (ValidatorInterface $validator) use ($object) {
                $validator->validate($object);
            });

            $parents[] = $object;
            $methods = get_class_methods($object);

            if (empty($methods)) {
                return;
            }

            $methods = array_filter($methods, function ($method) {
                return 0 === stripos($method, 'get');
            });

            foreach ($methods as $method) {
                if ($object instanceof DateTimeZone) {
                    continue;
                }

                $result = $object->$method();

                if (is_array($result)) {
                    foreach ($result as $item) {
                        $this->validate($item, $parents);
                    }
                } else {
                    $this->validate($result, $parents);
                }
            }
        } catch (InvalidArgumentException $exception) {
            throw InvalidDataException::fromObject($object, $exception->getMessage(), $exception->getPropertyPath(), $parents);
        }
    }
}


<?php

namespace SystemConnector\TransferObject\Manufacturer;

use SystemConnector\TransferObject\AbstractTransferObject;
use SystemConnector\ValueObject\Attribute\Attribute;

class Manufacturer extends AbstractTransferObject
{
    const TYPE = 'Manufacturer';

    /**
     * @var string
     */
    private $identifier = '';

    /**
     * @var string
     */
    private $name = '';

    /**
     * @var null|string
     */
    private $logoIdentifier;

    /**
     * @var null|string
     */
    private $link;

    /**
     * @var Attribute[]
     */
    private $attributes = [];

    /**
     * {@inheritdoc}
     */
    public function getType()
    {
        return self::TYPE;
    }

    /**
     * {@inheritdoc}
     */
    public function getIdentifier()
    {
        return $this->identifier;
    }

    /**
     * {@inheritdoc}
     */
    public function setIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getLogoIdentifier()
    {
        return $this->logoIdentifier;
    }

    /**
     * @param null|string $logoIdentifier
     */
    public function setLogoIdentifier($logoIdentifier = null)
    {
        $this->logoIdentifier = $logoIdentifier;
    }

    /**
     * @return string
     */
    public function getLink()
    {
        return $this->link;
    }

    /**
     * @param null|string $link
     */
    public function setLink($link = null)
    {
        $this->link = $link;
    }

    /**
     * @return Attribute[]
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * @param array $attributes
     */
    public function setAttributes(array $attributes)
    {
        $this->attributes = $attributes;
    }

    /**
     * {@inheritdoc}
     */
    public function getClassProperties()
    {
        return [
            'identifier' => $this->getIdentifier(),
            'name' => $this->getName(),
            'logoIdentifier' => $this->getLogoIdentifier(),
            'link' => $this->getLink(),
            'attributes' => $this->getAttributes(),
        ];
    }
}


<?php

namespace SystemConnector\TransferObject\Order\OrderItem;

use ReflectionClass;
use SystemConnector\TransferObject\AttributableInterface;
use SystemConnector\ValueObject\AbstractValueObject;
use SystemConnector\ValueObject\Attribute\Attribute;

class OrderItem extends AbstractValueObject implements AttributableInterface
{
    const TYPE_PRODUCT = 'product';
    const TYPE_VOUCHER = 'voucher';
    const TYPE_COUPON = 'coupon';
    const TYPE_DISCOUNT = 'discount';
    const TYPE_PAYMENT_SURCHARGE = 'payment_surcharge';
    const TYPE_SHIPPING_COSTS = 'shipping_costs';

    /**
     * @var int
     */
    private $type = self::TYPE_PRODUCT;

    /**
     * @var float
     */
    private $quantity = 1.0;

    /**
     * @var string
     */
    private $name = '';

    /**
     * @var string
     */
    private $number = '';

    /**
     * @var float
     */
    private $price = 0.0;

    /**
     * @var null|string
     */
    private $vatRateIdentifier;

    /**
     * @var Attribute[]
     */
    private $attributes = [];

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param string $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * @return array
     */
    public function getTypes()
    {
        $reflection = new ReflectionClass(__CLASS__);

        return $reflection->getConstants();
    }

    /**
     * @return float
     */
    public function getQuantity()
    {
        return $this->quantity;
    }

    /**
     * @param float $quantity
     */
    public function setQuantity($quantity)
    {
        $this->quantity = $quantity;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getNumber()
    {
        return $this->number;
    }

    /**
     * @param string $number
     */
    public function setNumber($number)
    {
        $this->number = $number;
    }

    /**
     * @return float
     */
    public function getPrice()
    {
        return $this->price;
    }

    /**
     * @param float $price
     */
    public function setPrice($price)
    {
        $this->price = $price;
    }

    /**
     * @return null|string
     */
    public function getVatRateIdentifier()
    {
        return $this->vatRateIdentifier;
    }

    /**
     * @param null|string $vatRateIdentifier
     */
    public function setVatRateIdentifier($vatRateIdentifier = null)
    {
        $this->vatRateIdentifier = $vatRateIdentifier;
    }

    /**
     * @return Attribute[]
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * @param Attribute[] $attributes
     */
    public function setAttributes(array $attributes)
    {
        $this->attributes = $attributes;
    }

    /**
     * {@inheritdoc}
     */
    public function getClassProperties()
    {
        return [
            'type' => $this->getType(),
            'quantity' => $this->getQuantity(),
            'name' => $this->getName(),
            'number' => $this->getNumber(),
            'price' => $this->getPrice(),
            'vatRateIdentifier' => $this->getVatRateIdentifier(),
            'attributes' => $this->getAttributes(),
        ];
    }
}


<?php

namespace PlentyConnector\Components\Bundle\PlentymarketsAdapter\RequestGenerator\Order\OrderItem;

use Doctrine\ORM\EntityManagerInterface;
use Exception;
use PlentymarketsAdapter\RequestGenerator\Order\OrderItem\OrderItemRequestGeneratorInterface;
use RuntimeException;
use Shopware\Models\Tax\Repository;
use Shopware\Models\Tax\Tax;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\CustomerGroup\CustomerGroup;
use SystemConnector\TransferObject\Order\Order;
use SystemConnector\TransferObject\Order\OrderItem\OrderItem;
use SystemConnector\TransferObject\VatRate\VatRate;

class OrderItemRequestGenerator implements OrderItemRequestGeneratorInterface
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var OrderItemRequestGeneratorInterface
     */
    private $parentOrderItemRequestGenerator;

    public function __construct(
        EntityManagerInterface $entityManager,
        IdentityServiceInterface $identityService,
        OrderItemRequestGeneratorInterface $parentOrderItemRequestGenerator
    ) {
        $this->entityManager = $entityManager;
        $this->identityService = $identityService;
        $this->parentOrderItemRequestGenerator = $parentOrderItemRequestGenerator;
    }

    /**
     * @param OrderItem $orderItem
     * @param Order     $order
     *
     * @return array
     */
    public function generate(OrderItem $orderItem, Order $order)
    {
        $itemParams = $this->parentOrderItemRequestGenerator->generate($orderItem, $order);

        $customerGroupIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $order->getCustomer()->getCustomerGroupIdentifier(),
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => CustomerGroup::TYPE,
        ]);

        if (null === $customerGroupIdentity) {
            throw new RuntimeException('could not find customer group identity of bundle');
        }

        $bundle = $this->getBundle($orderItem->getNumber(), $customerGroupIdentity->getAdapterIdentifier());

        if (empty($bundle)) {
            return $itemParams;
        }

        $vatIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $orderItem->getVatRateIdentifier(),
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => VatRate::TYPE,
        ]);

        if (null === $vatIdentity) {
            throw new RuntimeException('could not find vat identity of bundle');
        }

        /**
         * @var Repository $taxRepository
         */
        $taxRepository = $this->entityManager->getRepository(Tax::class);

        /**
         * @var null|Tax $taxModel
         */
        $taxModel = $taxRepository->find($vatIdentity->getAdapterIdentifier());

        if (null === $taxModel) {
            throw new RuntimeException('could not find shopware vat model of bundle');
        }

        $itemParams['orderItemName'] = $bundle['name'];

        $itemParams['amounts'][0]['priceOriginalGross'] = $bundle['price'] * (1 + ($taxModel->getTax() / 100));

        return $itemParams;
    }

    /**
     * @param int $articleNumber
     * @param int $customerGroupId
     *
     * @return array
     */
    private function getBundle($articleNumber, $customerGroupId)
    {
        try {
            $query = '
                SELECT * FROM s_articles_bundles AS bundle
                LEFT JOIN s_articles_bundles_prices AS bundlePrice
                ON bundle.id = bundlePrice.bundle_id
                WHERE bundle.ordernumber = :articleNumber
                AND bundlePrice.customer_group_id = :customerGroupId
            ';

            return $this->entityManager->getConnection()->fetchAssoc($query, [
                ':articleNumber' => $articleNumber,
                ':customerGroupId' => $customerGroupId,
            ]);
        } catch (Exception $exception) {
            return [];
        }
    }
}


<?php

namespace SystemConnector\TransferObject\Order\Address;

use ReflectionClass;
use SystemConnector\TransferObject\AttributableInterface;
use SystemConnector\ValueObject\AbstractValueObject;
use SystemConnector\ValueObject\Attribute\Attribute;

class Address extends AbstractValueObject implements AttributableInterface
{
    const GENDER_MALE = 'male';
    const GENDER_FEMALE = 'female';

    /**
     * @var null|string
     */
    private $company;

    /**
     * @var null|string
     */
    private $department;

    /**
     * @var string
     */
    private $gender = self::GENDER_MALE;

    /**
     * @var null|string
     */
    private $title;

    /**
     * @var null|string
     */
    private $firstname;

    /**
     * @var null|string
     */
    private $lastname;

    /**
     * @var string
     */
    private $street = '';

    /**
     * @var null|string
     */
    private $additional;

    /**
     * @var string
     */
    private $postalCode = '';

    /**
     * @var string
     */
    private $city = '';

    /**
     * @var string
     */
    private $countryIdentifier = '';

    /**
     * @var null|string
     */
    private $vatId;

    /**
     * @var null|string
     */
    private $phoneNumber;

    /**
     * @var null|string
     */
    private $mobilePhoneNumber;

    /**
     * @var Attribute[]
     */
    private $attributes = [];

    /**
     * @return null|string
     */
    public function getCompany()
    {
        return $this->company;
    }

    /**
     * @param null|string $company
     */
    public function setCompany($company)
    {
        $this->company = $company;
    }

    /**
     * @return null|string
     */
    public function getDepartment()
    {
        return $this->department;
    }

    /**
     * @param null|string $department
     */
    public function setDepartment($department)
    {
        $this->department = $department;
    }

    /**
     * @return array
     */
    public function getGenders()
    {
        $reflection = new ReflectionClass(__CLASS__);

        return $reflection->getConstants();
    }

    /**
     * @return string
     */
    public function getGender()
    {
        return $this->gender;
    }

    /**
     * @param string $gender
     */
    public function setGender($gender)
    {
        $this->gender = $gender;
    }

    /**
     * @return null|string
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @param null|string $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }

    /**
     * @return null|string
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * @param null|string $firstname
     */
    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;
    }

    /**
     * @return null|string
     */
    public function getLastname()
    {
        return $this->lastname;
    }

    /**
     * @param null|string $lastname
     */
    public function setLastname($lastname)
    {
        $this->lastname = $lastname;
    }

    /**
     * @return null|string
     */
    public function getAdditional()
    {
        return $this->additional;
    }

    /**
     * @param null|string $additional
     */
    public function setAdditional($additional = null)
    {
        $this->additional = $additional;
    }

    /**
     * @return null|string
     */
    public function getStreet()
    {
        return $this->street;
    }

    /**
     * @param null|string $street
     */
    public function setStreet($street)
    {
        $this->street = $street;
    }

    /**
     * @return string
     */
    public function getPostalCode()
    {
        return $this->postalCode;
    }

    /**
     * @param string $postalCode
     */
    public function setPostalCode($postalCode)
    {
        $this->postalCode = $postalCode;
    }

    /**
     * @return string
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * @param string $city
     */
    public function setCity($city)
    {
        $this->city = $city;
    }

    /**
     * @return string
     */
    public function getCountryIdentifier()
    {
        return $this->countryIdentifier;
    }

    /**
     * @param string $countryIdentifier
     */
    public function setCountryIdentifier($countryIdentifier)
    {
        $this->countryIdentifier = $countryIdentifier;
    }

    /**
     * @return null|string
     */
    public function getVatId()
    {
        return $this->vatId;
    }

    /**
     * @param null|string $vatId
     */
    public function setVatId($vatId)
    {
        $this->vatId = $vatId;
    }

    /**
     * @return null|string
     */
    public function getPhoneNumber()
    {
        return $this->phoneNumber;
    }

    /**
     * @param null|string $phoneNumber
     */
    public function setPhoneNumber($phoneNumber = null)
    {
        $this->phoneNumber = $phoneNumber;
    }

    /**
     * @return null|string
     */
    public function getMobilePhoneNumber()
    {
        return $this->mobilePhoneNumber;
    }

    /**
     * @param null|string $mobilePhoneNumber
     */
    public function setMobilePhoneNumber($mobilePhoneNumber = null)
    {
        $this->mobilePhoneNumber = $mobilePhoneNumber;
    }

    /**
     * @return Attribute[]
     */
    public function getAttributes()
    {
        return $this->attributes;
    }

    /**
     * @param Attribute[] $attributes
     */
    public function setAttributes(array $attributes)
    {
        $this->attributes = $attributes;
    }

    /**
     * {@inheritdoc}
     */
    public function getClassProperties()
    {
        return [
            'company' => $this->getCompany(),
            'department' => $this->getDepartment(),
            'gender' => $this->getGender(),
            'title' => $this->getTitle(),
            'firstname' => $this->getFirstname(),
            'lastname' => $this->getLastname(),
            'street' => $this->getStreet(),
            'additional' => $this->getAdditional(),
            'postalCode' => $this->getPostalCode(),
            'city' => $this->getCity(),
            'countryIdentifier' => $this->getCountryIdentifier(),
            'vatId' => $this->getVatId(),
            'phoneNumber' => $this->getPhoneNumber(),
            'mobilePhoneNumber' => $this->getMobilePhoneNumber(),
            'attributes' => $this->getAttributes(),
        ];
    }
}


<?php

namespace ShopwareAdapter\ServiceBus\CommandHandler\Category;

use DeepCopy\DeepCopy;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use InvalidArgumentException;
use Shopware\Components\Api\Exception\NotFoundException;
use Shopware\Components\Api\Manager;
use Shopware\Components\Api\Resource\Category as CategoryResource;
use Shopware\Models\Category\Category as CategoryModel;
use Shopware\Models\Category\Repository as CategoryRepository;
use Shopware\Models\Shop\Repository as ShopRepository;
use Shopware\Models\Shop\Shop as ShopModel;
use ShopwareAdapter\DataPersister\Attribute\AttributeDataPersisterInterface;
use ShopwareAdapter\DataPersister\Translation\TranslationDataPersisterInterface;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\Exception\NotFoundException as IdentityNotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\IdentityService\Struct\Identity;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\Category\Category;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Media\Media;
use SystemConnector\TransferObject\Shop\Shop;
use SystemConnector\Translation\TranslationHelperInterface;
use SystemConnector\ValueObject\Attribute\Attribute;

class HandleCategoryCommandHandler implements CommandHandlerInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var TranslationHelperInterface
     */
    private $translationHelper;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    /**
     * @var AttributeDataPersisterInterface
     */
    private $attributePersister;

    /**
     * @var TranslationDataPersisterInterface
     */
    private $translationDataPersister;

    /**
     * @var CategoryRepository
     */
    private $categoryRepository;

    /**
     * @var ShopRepository
     */
    private $shopRepository;

    public function __construct(
        IdentityServiceInterface $identityService,
        TranslationHelperInterface $translationHelper,
        EntityManagerInterface $entityManager,
        AttributeDataPersisterInterface $attributePersister,
        TranslationDataPersisterInterface $translationDataPersister
    ) {
        $this->identityService = $identityService;
        $this->translationHelper = $translationHelper;
        $this->entityManager = $entityManager;
        $this->attributePersister = $attributePersister;
        $this->translationDataPersister = $translationDataPersister;
        $this->categoryRepository = $entityManager->getRepository(CategoryModel::class);
        $this->shopRepository = $entityManager->getRepository(ShopModel::class);
    }

    /**
     * {@inheritdoc}
     */
    public function supports(CommandInterface $command)
    {
        return $command instanceof TransferObjectCommand &&
            $command->getAdapterName() === ShopwareAdapter::NAME &&
            $command->getObjectType() === Category::TYPE &&
            $command->getCommandType() === CommandType::HANDLE;
    }

    /**
     * {@inheritdoc}
     *
     * @var TransferObjectCommand $command
     */
    public function handle(CommandInterface $command)
    {
        /**
         * @var Category $category
         */
        $category = $command->getPayload();

        $validIdentities = [];
        foreach ($category->getShopIdentifiers() as $shopIdentifier) {
            $shopIdentities = $this->identityService->findBy([
                'objectIdentifier' => (string) $shopIdentifier,
                'objectType' => Shop::TYPE,
                'adapterName' => ShopwareAdapter::NAME,
            ]);

            if (empty($shopIdentities)) {
                continue;
            }

            foreach ($shopIdentities as $shopIdentity) {
                $identity = $this->handleCategory($category, $shopIdentity);

                if (null === $identity) {
                    continue;
                }

                $identifier = $identity->getObjectIdentifier();
                $validIdentities[$identifier] = $identifier;
            }
        }

        $this->handleOrphanedCategories($category, $validIdentities);

        return true;
    }

    /**
     * @param Category $category
     */
    private function prepareCategory(Category $category)
    {
        $attributes = $category->getAttributes();

        $attributes[] = Attribute::fromArray([
            'key' => 'metaRobots',
            'value' => $category->getMetaRobots(),
        ]);

        $category->setAttributes($attributes);
    }

    /**
     * @param Category $category
     * @param Identity $shopIdentity
     *
     * @return null|Identity
     */
    private function handleCategory(Category $category, Identity $shopIdentity)
    {
        $deepCopy = new DeepCopy();
        $category = $deepCopy->copy($category);

        /**
         * @var null|ShopModel $shop
         */
        $shop = $this->shopRepository->find($shopIdentity->getAdapterIdentifier());

        if (null === $shop) {
            return null;
        }

        $this->prepareCategory($category);

        $mainCategory = $shop->getCategory();

        if (null === $mainCategory) {
            throw new InvalidArgumentException('shop without main cateogry assignment');
        }

        $shopLocale = $shop->getLocale();

        if (null === $shopLocale) {
            throw new InvalidArgumentException('shop without locale assignment');
        }

        $languageIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $shopLocale->getId(),
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => Language::TYPE,
        ]);

        if (null !== $languageIdentity) {
            /**
             * @var Category $translatedCategory
             */
            $translatedCategory = $this->translationHelper->translate($languageIdentity->getObjectIdentifier(), $category);
            $translatedAttributes = [];

            if (null !== $translatedCategory) {
                foreach ($translatedCategory->getAttributes() as $attribute) {
                    $translatedAttributes[] = $this->translationHelper->translate($languageIdentity->getObjectIdentifier(), $attribute);
                }
                $translatedCategory->setAttributes($translatedAttributes);

                $category = $translatedCategory;
            }
        }

        if (null === $category->getParentIdentifier()) {
            $parentCategory = $mainCategory->getId();
        } else {
            $parentCategoryIdentities = $this->identityService->findBy([
                'objectIdentifier' => (string) $category->getParentIdentifier(),
                'objectType' => Category::TYPE,
                'adapterName' => ShopwareAdapter::NAME,
            ]);

            $possibleIdentities = array_filter($parentCategoryIdentities, function (Identity $identity) use ($mainCategory) {
                return $this->validIdentity($identity, $mainCategory);
            });

            $parentCategoryIdentity = null;
            if (!empty($possibleIdentities)) {
                /**
                 * @var Identity $categoryIdentity
                 */
                $parentCategoryIdentity = array_shift($possibleIdentities);
            }

            if (null === $parentCategoryIdentity) {
                throw new InvalidArgumentException('missing parent category - ' . $category->getParentIdentifier());
            }

            $parentCategory = $parentCategoryIdentity->getAdapterIdentifier();
        }

        $categoryIdentities = $this->identityService->findBy([
            'objectIdentifier' => $category->getIdentifier(),
            'objectType' => Category::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        $possibleIdentities = array_filter($categoryIdentities, function (Identity $identity) use ($mainCategory) {
            return $this->validIdentity($identity, $mainCategory);
        });

        $categoryIdentity = null;
        if (!empty($possibleIdentities)) {
            /**
             * @var Identity $categoryIdentity
             */
            $categoryIdentity = array_shift($possibleIdentities);
        }

        $resource = $this->getCategoryResource();

        if (null !== $categoryIdentity) {
            try {
                $resource->getOne($categoryIdentity->getAdapterIdentifier());
            } catch (NotFoundException $exception) {
                $categoryIdentity = null;
            }
        }

        if (null === $categoryIdentity) {
            $existingCategory = $this->findExistingCategory($category, $parentCategory);

            if (null !== $existingCategory) {
                $categoryIdentity = $this->identityService->insert(
                    (string) $category->getIdentifier(),
                    Category::TYPE,
                    (string) $existingCategory,
                    ShopwareAdapter::NAME
                );
            }
        }

        $params = [
            'active' => $category->getActive(),
            'position' => $category->getPosition(),
            'name' => $category->getName(),
            'parent' => $parentCategory,
            'metaTitle' => $category->getMetaTitle(),
            'metaKeywords' => $category->getMetaKeywords(),
            'metaDescription' => $category->getMetaDescription(),
            'cmsHeadline' => $category->getDescription(),
            'cmsText' => $category->getLongDescription(),
        ];

        if (!empty($category->getImageIdentifiers())) {
            $mediaIdentifiers = $category->getImageIdentifiers();
            $mediaIdentifier = array_shift($mediaIdentifiers);

            $mediaIdentity = $this->identityService->findOneBy([
                'objectIdentifier' => (string) $mediaIdentifier,
                'objectType' => Media::TYPE,
                'adapterName' => ShopwareAdapter::NAME,
            ]);

            if (null === $mediaIdentity) {
                throw new IdentityNotFoundException('media not found - ' . $mediaIdentifier);
            }

            $params['media']['mediaId'] = $mediaIdentity->getAdapterIdentifier();
        }

        if (null !== $categoryIdentity) {
            try {
                $resource->getOne($categoryIdentity->getAdapterIdentifier());
            } catch (NotFoundException $exception) {
                $this->identityService->remove($categoryIdentity);

                $categoryIdentity = null;
            }
        }

        if (null === $categoryIdentity) {
            $categoryModel = $resource->create($params);

            $categoryIdentity = $this->identityService->insert(
                (string) $category->getIdentifier(),
                Category::TYPE,
                (string) $categoryModel->getId(),
                ShopwareAdapter::NAME
            );
        } else {
            $categoryModel = $resource->update($categoryIdentity->getAdapterIdentifier(), $params);
        }

        $this->attributePersister->saveCategoryAttributes($categoryModel, $category->getAttributes());
        $this->translationDataPersister->writeCategoryTranslations($category);

        return $categoryIdentity;
    }

    /**
     * @param Category $category
     * @param int      $parentCategory
     *
     * @return null|int
     */
    private function findExistingCategory(Category $category, $parentCategory)
    {
        $existingCategory = $this->categoryRepository->findOneBy([
            'name' => $category->getName(),
            'parentId' => $parentCategory,
        ]);

        if (null === $existingCategory) {
            return null;
        }

        return $existingCategory->getId();
    }

    /**
     * @param Identity      $categoryIdentity
     * @param CategoryModel $shopMainCategory
     *
     * @return bool
     */
    private function validIdentity(Identity $categoryIdentity, CategoryModel $shopMainCategory)
    {
        try {
            $existingCategory = $this->categoryRepository->find($categoryIdentity->getAdapterIdentifier());

            if (null === $existingCategory) {
                return false;
            }

            $extractedCategoryPath = array_filter(explode('|', $existingCategory->getPath()));

            if (in_array($shopMainCategory->getId(), $extractedCategoryPath)) {
                return true;
            }

            return false;
        } catch (Exception $exception) {
            return false;
        }
    }

    /**
     * @param Category $category
     * @param array    $validIdentities
     */
    private function handleOrphanedCategories(Category $category, array $validIdentities = [])
    {
        $categoryIdentities = $this->identityService->findBy([
            'objectIdentifier' => $category->getIdentifier(),
            'objectType' => Category::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        foreach ($categoryIdentities as $identity) {
            $resource = $this->getCategoryResource();

            if (isset($validIdentities[$identity->getObjectIdentifier()])) {
                continue;
            }

            try {
                $resource->getOne($identity->getAdapterIdentifier());
            } catch (NotFoundException $exception) {
                $this->identityService->remove($identity);

                continue;
            }

            $params = [
                'name' => $category->getName(),
                'active' => false,
            ];

            $resource->update($identity->getAdapterIdentifier(), $params);
        }
    }

    /**
     * @return CategoryResource
     */
    private function getCategoryResource()
    {
        // without this reset the entitymanager sometimes the album is not found correctly.
        Shopware()->Container()->reset('models');

        return Manager::getResource('Category');
    }
}


<?php

namespace ShopwareAdapter\ServiceBus\CommandHandler\Product;

use Doctrine\ORM\EntityManagerInterface;
use Shopware\Components\Api\Exception\NotFoundException;
use Shopware\Components\Api\Manager;
use Shopware\Components\Api\Resource\Article;
use Shopware\Components\Api\Resource\Variant;
use Shopware\Models\Article\Article as ArticleModel;
use Shopware\Models\Article\Detail;
use ShopwareAdapter\DataPersister\Attribute\AttributeDataPersisterInterface;
use ShopwareAdapter\DataPersister\Translation\TranslationDataPersisterInterface;
use ShopwareAdapter\DataProvider\Shop\ShopDataProviderInterface;
use ShopwareAdapter\RequestGenerator\Product\ProductRequestGeneratorInterface;
use ShopwareAdapter\ShopwareAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Command\CommandInterface;
use SystemConnector\ServiceBus\Command\TransferObjectCommand;
use SystemConnector\ServiceBus\CommandHandler\CommandHandlerInterface;
use SystemConnector\ServiceBus\CommandType;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\Translation\TranslationHelperInterface;

class HandleProductCommandHandler implements CommandHandlerInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var TranslationHelperInterface
     */
    private $translationHelper;

    /**
     * @var AttributeDataPersisterInterface
     */
    private $attributeDataPersister;

    /**
     * @var ProductRequestGeneratorInterface
     */
    private $productRequestGenerator;

    /**
     * @var TranslationDataPersisterInterface
     */
    private $translationDataPersister;

    /**
     * @var ShopDataProviderInterface
     */
    private $shopDataProvider;

    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(
        EntityManagerInterface $entityManager,
        IdentityServiceInterface $identityService,
        TranslationHelperInterface $translationHelper,
        AttributeDataPersisterInterface $attributeDataPersister,
        ProductRequestGeneratorInterface $productRequestGenerator,
        TranslationDataPersisterInterface $translationDataPersister,
        ShopDataProviderInterface $shopDataProvider
    ) {
        $this->identityService = $identityService;
        $this->translationHelper = $translationHelper;
        $this->attributeDataPersister = $attributeDataPersister;
        $this->productRequestGenerator = $productRequestGenerator;
        $this->translationDataPersister = $translationDataPersister;
        $this->entityManager = $entityManager;
        $this->shopDataProvider = $shopDataProvider;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(CommandInterface $command)
    {
        return $command instanceof TransferObjectCommand &&
            $command->getAdapterName() === ShopwareAdapter::NAME &&
            $command->getObjectType() === Product::TYPE &&
            $command->getCommandType() === CommandType::HANDLE;
    }

    /**
     * {@inheritdoc}
     *
     * @param TransferObjectCommand $command
     */
    public function handle(CommandInterface $command)
    {
        $shopLocaleId = $this->shopDataProvider->getDefaultShop()->getLocale()->getId();

        $languageIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => (string) $shopLocaleId,
            'adapterName' => ShopwareAdapter::NAME,
            'objectType' => Language::TYPE,
        ]);

        /**
         * @var Product $product
         */
        $product = $command->getPayload();

        if (null !== $languageIdentity) {
            /**
             * @var Product $translated
             */
            $translated = $this->translationHelper->translate($languageIdentity->getObjectIdentifier(), $product);

            if (null !== $translated) {
                $product = $translated;
            }
        }

        $params = $this->productRequestGenerator->generate($product);

        if (empty($params)) {
            return false;
        }

        $articleResource = $this->getArticleResource();

        $productIdentity = $this->identityService->findOneBy([
            'objectIdentifier' => $product->getIdentifier(),
            'objectType' => Product::TYPE,
            'adapterName' => ShopwareAdapter::NAME,
        ]);

        $variationRepository = $this->entityManager->getRepository(Detail::class);

        /**
         * @var null|Detail $mainVariation
         */
        $mainVariation = $variationRepository->findOneBy(['number' => $product->getNumber()]);

        if (null === $mainVariation && null !== $productIdentity) {
            $mainVariation = $variationRepository->findOneBy(['articleId' => $productIdentity->getAdapterIdentifier(), 'kind' => 1]);
        }

        if (null === $productIdentity) {
            if (null === $mainVariation) {
                $productModel = $articleResource->create($params);
            } else {
                $this->correctMainDetailAssignment($mainVariation);

                foreach ($mainVariation->getImages() as $image) {
                    if (null !== $image) {
                        $this->translationDataPersister->removeMediaTranslation($image);
                    }
                }

                $productModel = $articleResource->update($mainVariation->getArticleId(), $params);
            }

            $this->identityService->insert(
                $product->getIdentifier(),
                Product::TYPE,
                (string) $productModel->getId(),
                ShopwareAdapter::NAME
            );
        } else {
            try {
                $articleRepository = $this->entityManager->getRepository(ArticleModel::class);

                $productModel = $articleRepository->find($productIdentity->getAdapterIdentifier());

                if (null === $mainVariation) {
                    $variationResource = $this->getVariationResource();

                    $mainVariation = $variationResource->create([
                        'articleId' => $productIdentity->getAdapterIdentifier(),
                        'number' => $product->getNumber(),
                        'active' => true,
                    ]);
                }

                foreach ($productModel->getImages() as $image) {
                    if (null !== $image) {
                        $this->translationDataPersister->removeMediaTranslation($image);
                    }
                }

                $this->correctMainDetailAssignment($mainVariation);
                $productModel = $articleResource->update($productModel->getId(), $params);
            } catch (NotFoundException $exception) {
                $productModel = $articleResource->create($params);

                $this->identityService->update(
                    $productIdentity,
                    [
                        'adapterIdentifier' => (string) $productModel->getId(),
                    ]
                );
            }

            $this->attributeDataPersister->saveProductDetailAttributes(
                $productModel->getMainDetail(),
                $product->getAttributes()
            );

            $this->translationDataPersister->writeProductTranslations($product);
        }

        return true;
    }

    /**
     * @param Detail $mainVariation
     */
    private function correctMainDetailAssignment(Detail $mainVariation)
    {
        $this->entityManager->getConnection()->update(
            's_articles',
            ['main_detail_id' => $mainVariation->getId()],
            ['id' => $mainVariation->getArticle()->getId()]
        );

        $this->entityManager->getConnection()->update(
            's_articles_details',
            ['kind' => 2],
            ['articleID' => $mainVariation->getArticle()->getId()]
        );

        $this->entityManager->getConnection()->update(
            's_articles_details',
            ['kind' => 1],
            ['id' => $mainVariation->getId()]
        );
    }

    /**
     * @return Article
     */
    private function getArticleResource()
    {
        // without this reset the entitymanager will write the models in the wrong order, leading
        // to an s_articles_details.articleID cannot be null exception from the dbal driver.
        Shopware()->Container()->reset('models');

        return Manager::getResource('Article');
    }

    /**
     * @return Variant
     */
    private function getVariationResource()
    {
        return Manager::getResource('Variant');
    }
}


<?php

namespace PlentymarketsAdapter\ServiceBus\QueryHandler\Unit;

use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Item\Unit as UnitApi;
use PlentymarketsAdapter\ResponseParser\Unit\UnitResponseParserInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\Query\QueryInterface;
use SystemConnector\ServiceBus\QueryHandler\QueryHandlerInterface;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\TransferObject\Unit\Unit;

class FetchAllUnitsQueryHandler implements QueryHandlerInterface
{
    /**
     * @var UnitApi
     */
    private $unitApi;

    /**
     * @var UnitResponseParserInterface
     */
    private $responseParser;

    public function __construct(UnitApi $unitApi, UnitResponseParserInterface $responseParser)
    {
        $this->unitApi = $unitApi;
        $this->responseParser = $responseParser;
    }

    /**
     * {@inheritdoc}
     */
    public function supports(QueryInterface $query)
    {
        return $query instanceof FetchTransferObjectQuery &&
            $query->getAdapterName() === PlentymarketsAdapter::NAME &&
            $query->getObjectType() === Unit::TYPE &&
            $query->getQueryType() === QueryType::ALL;
    }

    /**
     * {@inheritdoc}
     */
    public function handle(QueryInterface $query)
    {
        $elements = $this->unitApi->findAll();

        foreach ($elements as $element) {
            $result = $this->responseParser->parse($element);

            if (null === $result) {
                continue;
            }

            yield $result;
        }
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Unit;

use PlentymarketsAdapter\PlentymarketsAdapter;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Unit\Unit;

class UnitResponseParser implements UnitResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    public function __construct(IdentityServiceInterface $identityService)
    {
        $this->identityService = $identityService;
    }

    /**
     * {@inheritdoc}
     */
    public function parse(array $entry)
    {
        $identity = $this->identityService->findOneOrCreate(
            (string) $entry['id'],
            PlentymarketsAdapter::NAME,
            Unit::TYPE
        );

        // use first unit name as name
        $name = implode(' / ', array_column($entry['names'], 'name'));

        return Unit::fromArray([
            'identifier' => $identity->getObjectIdentifier(),
            'name' => $name,
        ]);
    }
}


<?php

namespace PlentyConnector\Components\PayPal\PaymentData;

use DateTimeImmutable;
use SystemConnector\TransferObject\Payment\PaymentData\PaymentDataInterface;
use SystemConnector\ValueObject\AbstractValueObject;

class PayPalUnifiedPaymentData extends AbstractValueObject implements PaymentDataInterface
{
    /**
     * @var string
     */
    private $reference;

    /**
     * @var string
     */
    private $bank_name;

    /**
     * @var string
     */
    private $account_holder;

    /**
     * @var string
     */
    private $iban;

    /**
     * @var string
     */
    private $bic;

    /**
     * @var float
     */
    private $amount;

    /**
     * @var DateTimeImmutable
     */
    private $due_date;

    /**
     * @return string
     */
    public function getReference()
    {
        return $this->reference;
    }

    /**
     * @param string $reference
     */
    public function setReference($reference)
    {
        $this->reference = $reference;
    }

    /**
     * @return string
     */
    public function getBankName()
    {
        return $this->bank_name;
    }

    /**
     * @param string $bank_name
     */
    public function setBankName($bank_name)
    {
        $this->bank_name = $bank_name;
    }

    /**
     * @return string
     */
    public function getAccountHolder()
    {
        return $this->account_holder;
    }

    /**
     * @param string $account_holder
     */
    public function setAccountHolder($account_holder)
    {
        $this->account_holder = $account_holder;
    }

    /**
     * @return string
     */
    public function getIban()
    {
        return $this->iban;
    }

    /**
     * @param string $iban
     */
    public function setIban($iban)
    {
        $this->iban = $iban;
    }

    /**
     * @return string
     */
    public function getBic()
    {
        return $this->bic;
    }

    /**
     * @param string $bic
     */
    public function setBic($bic)
    {
        $this->bic = $bic;
    }

    /**
     * @return float
     */
    public function getAmount()
    {
        return $this->amount;
    }

    /**
     * @param float $amount
     */
    public function setAmount($amount)
    {
        $this->amount = $amount;
    }

    /**
     * @return DateTimeImmutable
     */
    public function getDueDate()
    {
        return $this->due_date;
    }

    /**
     * @param DateTimeImmutable $due_date
     */
    public function setDueDate($due_date)
    {
        $this->due_date = $due_date;
    }

    /**
     * {@inheritdoc}
     */
    public function getClassProperties()
    {
        return [
            'reference' => $this->getReference(),
            'bank_name' => $this->getBankName(),
            'account_holder' => $this->getAccountHolder(),
            'iban' => $this->getIban(),
            'bic' => $this->getBic(),
            'amount' => $this->getAmount(),
            'due_date' => $this->getDueDate(),
        ];
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Product;

use DateTimeImmutable;
use PlentymarketsAdapter\Client\ClientInterface;
use PlentymarketsAdapter\Helper\VariationHelperInterface;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ReadApi\Item\Property\Group as PropertyGroupApi;
use PlentymarketsAdapter\ReadApi\Item\Property\Name as PropertyNameApi;
use PlentymarketsAdapter\ResponseParser\Product\Image\ImageResponseParserInterface;
use PlentymarketsAdapter\ResponseParser\Product\Variation\VariationResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\ConfigService\ConfigServiceInterface;
use SystemConnector\IdentityService\Exception\NotFoundException;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Category\Category;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Manufacturer\Manufacturer;
use SystemConnector\TransferObject\Product\Badge\Badge;
use SystemConnector\TransferObject\Product\Image\Image;
use SystemConnector\TransferObject\Product\LinkedProduct\LinkedProduct;
use SystemConnector\TransferObject\Product\Product;
use SystemConnector\TransferObject\Product\Property\Property;
use SystemConnector\TransferObject\Product\Property\Value\Value;
use SystemConnector\TransferObject\Product\Variation\Variation;
use SystemConnector\TransferObject\ShippingProfile\ShippingProfile;
use SystemConnector\TransferObject\TransferObjectInterface;
use SystemConnector\TransferObject\VatRate\VatRate;
use SystemConnector\ValueObject\Attribute\Attribute;
use SystemConnector\ValueObject\Translation\Translation;

class ProductResponseParser implements ProductResponseParserInterface
{
    /**
     * @var ConfigServiceInterface
     */
    private $configService;

    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var ImageResponseParserInterface
     */
    private $imageResponseParser;

    /**
     * @var VariationResponseParserInterface
     */
    private $variationResponseParser;

    /**
     * @var VariationHelperInterface
     */
    private $variationHelper;

    /**
     * @var PropertyGroupApi
     */
    private $itemsPropertiesGroupsNamesApi;

    /**
     * @var PropertyNameApi
     */
    private $itemsPropertiesNamesApi;

    public function __construct(
        ConfigServiceInterface $configService,
        IdentityServiceInterface $identityService,
        LoggerInterface $logger,
        ImageResponseParserInterface $imageResponseParser,
        VariationResponseParserInterface $variationResponseParser,
        VariationHelperInterface $variationHelper,
        ClientInterface $client
    ) {
        $this->configService = $configService;
        $this->identityService = $identityService;
        $this->logger = $logger;
        $this->imageResponseParser = $imageResponseParser;
        $this->variationResponseParser = $variationResponseParser;
        $this->variationHelper = $variationHelper;

        $this->itemsPropertiesGroupsNamesApi = new PropertyGroupApi($client);
        $this->itemsPropertiesNamesApi = new PropertyNameApi($client);
    }

    /**
     * @param array $product
     *
     * @return TransferObjectInterface[]
     */
    public function parse(array $product)
    {
        $result = [];

        if (empty($product['texts'])) {
            $this->logger->notice('the product has no text fieds and will be skipped', [
                'product id' => $product['id'],
            ]);

            return [];
        }

        $mainVariation = $this->variationHelper->getMainVariation($product['variations']);

        if (empty($mainVariation)) {
            return [];
        }

        $identity = $this->identityService->findOneOrCreate(
            (string) $product['id'],
            PlentymarketsAdapter::NAME,
            Product::TYPE
        );

        $candidatesForProcessing = $this->variationResponseParser->parse($product);

        if (empty($candidatesForProcessing)) {
            return [];
        }

        $variations = array_filter($candidatesForProcessing, function (TransferObjectInterface $object) {
            return $object instanceof Variation;
        });

        if (empty($variations)) {
            return [];
        }

        $productObject = new Product();
        $productObject->setIdentifier($identity->getObjectIdentifier());
        $productObject->setName((string) $product['texts'][0]['name1']);
        $productObject->setActive($this->getActive($variations, $mainVariation));
        $productObject->setNumber($this->variationHelper->getMainVariationNumber($mainVariation, $variations));
        $productObject->setBadges($this->getBadges($product));
        $productObject->setShopIdentifiers($this->variationHelper->getShopIdentifiers($mainVariation));
        $productObject->setManufacturerIdentifier($this->getManufacturerIdentifier($product));
        $productObject->setCategoryIdentifiers($this->getCategories($mainVariation));
        $productObject->setDefaultCategoryIdentifiers($this->getDafaultCategories($mainVariation));
        $productObject->setShippingProfileIdentifiers($this->getShippingProfiles($product));
        $productObject->setImages($this->getImages($product, $product['texts'], $result));
        $productObject->setVatRateIdentifier($this->getVatRateIdentifier($mainVariation));
        $productObject->setDescription((string) $product['texts'][0]['shortDescription']);
        $productObject->setLongDescription((string) $product['texts'][0]['description']);
        $productObject->setMetaTitle((string) $product['texts'][0]['name1']);
        $productObject->setMetaDescription((string) $product['texts'][0]['metaDescription']);
        $productObject->setMetaKeywords((string) $product['texts'][0]['keywords']);
        $productObject->setMetaRobots('INDEX, FOLLOW');
        $productObject->setLinkedProducts($this->getLinkedProducts($product));
        $productObject->setProperties($this->getProperties($mainVariation));
        $productObject->setTranslations($this->getProductTranslations($product['texts']));
        $productObject->setAvailableFrom($this->getAvailableFrom($mainVariation));
        $productObject->setAvailableTo($this->getAvailableTo($mainVariation));
        $productObject->setCreatedAt($this->getCreatedAt($mainVariation));
        $productObject->setAttributes($this->getAttributes($product));
        $productObject->setVariantConfiguration($this->getVariantConfiguration($variations));

        $result[$productObject->getIdentifier()] = $productObject;

        $candidatesForProcessing = $this->addProductAttributesToVariation($productObject, $candidatesForProcessing);

        return array_merge($result, $candidatesForProcessing);
    }

    /**
     * @param Product                   $product
     * @param TransferObjectInterface[] $candidatesForProcessing
     *
     * @return TransferObjectInterface[]
     */
    private function addProductAttributesToVariation(Product $product, array $candidatesForProcessing = [])
    {
        return array_map(function (TransferObjectInterface $object) use ($product) {
            if (!($object instanceof Variation)) {
                return $object;
            }

            $object->setAttributes(array_merge($object->getAttributes(), $product->getAttributes()));

            return $object;
        }, $candidatesForProcessing);
    }

    /**
     * @param array $variation
     *
     * @return string
     */
    private function getVatRateIdentifier(array $variation)
    {
        $vatRateIdentity = $this->identityService->findOneBy([
            'adapterIdentifier' => $variation['vatId'],
            'adapterName' => PlentymarketsAdapter::NAME,
            'objectType' => VatRate::TYPE,
        ]);

        if (null === $vatRateIdentity) {
            throw new NotFoundException('missing mapping for vat rate');
        }

        return $vatRateIdentity->getObjectIdentifier();
    }

    /**
     * @param array $product
     *
     * @return string
     */
    private function getManufacturerIdentifier(array $product)
    {
        $manufacturerIdentity = $this->identityService->findOneOrCreate(
            (string) $product['manufacturerId'],
            PlentymarketsAdapter::NAME,
            Manufacturer::TYPE
        );

        if (null === $manufacturerIdentity) {
            throw new NotFoundException('missing mapping for manufacturer');
        }

        return $manufacturerIdentity->getObjectIdentifier();
    }

    /**
     * @param array $product
     *
     * @return array
     */
    private function getShippingProfiles(array $product)
    {
        $shippingProfiles = [];
        foreach ($product['itemShippingProfiles'] as $profile) {
            $profileIdentity = $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $profile['profileId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => ShippingProfile::TYPE,
            ]);

            if (null === $profileIdentity) {
                continue;
            }

            $shippingProfiles[] = $profileIdentity->getObjectIdentifier();
        }

        return $shippingProfiles;
    }

    /**
     * @param array $product
     * @param array $texts
     * @param array $result
     *
     * @return Image[]
     */
    private function getImages(array $product, array $texts, array &$result)
    {
        $images = [];
        foreach ($product['itemImages'] as $entry) {
            $images[] = $this->imageResponseParser->parseImage($entry, $texts, $result);
        }

        return array_filter($images);
    }

    /**
     * @param array $mainVariation
     *
     * @return array
     */
    private function getDafaultCategories(array $mainVariation)
    {
        $defaultCategories = [];

        foreach ($mainVariation['variationDefaultCategory'] as $category) {
            $categoryIdentity = $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $category['branchId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Category::TYPE,
            ]);

            if (null === $categoryIdentity) {
                $this->logger->notice('missing mapping for category', ['category' => $category]);

                continue;
            }

            $defaultCategories[] = $categoryIdentity->getObjectIdentifier();
        }

        return $defaultCategories;
    }

    /**
     * @param array $texts
     *
     * @return Translation[]
     */
    private function getProductTranslations(array $texts)
    {
        $translations = [];

        foreach ($texts as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'name',
                'value' => $text['name1'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'description',
                'value' => $text['metaDescription'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'longDescription',
                'value' => $text['description'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaTitle',
                'value' => $text['name1'],
            ]);

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'metaKeywords',
                'value' => $text['keywords'],
            ]);
        }

        return $translations;
    }

    /**
     * @param array $mainVariation
     *
     * @return array
     */
    private function getCategories(array $mainVariation)
    {
        $categories = [];
        foreach ($mainVariation['variationCategories'] as $category) {
            $categoryIdentity = $this->identityService->findOneBy([
                'adapterIdentifier' => (string) $category['categoryId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Category::TYPE,
            ]);

            if (null === $categoryIdentity) {
                $this->logger->notice('missing mapping for category', ['category' => $category]);

                continue;
            }

            $categories[] = $categoryIdentity->getObjectIdentifier();
        }

        return $categories;
    }

    /**
     * @param array $product
     *
     * @return LinkedProduct[]
     */
    private function getLinkedProducts(array $product)
    {
        $result = [];

        foreach ($product['itemCrossSelling'] as $linkedProduct) {
            if ($linkedProduct['relationship'] === 'Similar') {
                $type = LinkedProduct::TYPE_SIMILAR;
            } elseif ($linkedProduct['relationship'] === 'Accessory') {
                $type = LinkedProduct::TYPE_ACCESSORY;
            } elseif ($linkedProduct['relationship'] === 'ReplacementPart') {
                $type = LinkedProduct::TYPE_REPLACEMENT;
            } else {
                continue;
            }

            $productIdentity = $this->identityService->findOneBy([
                'adapterIdentifier' => $linkedProduct['crossItemId'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Product::TYPE,
            ]);

            if (null === $productIdentity) {
                $this->logger->notice('linked product not found', ['linkedProduct' => $linkedProduct]);

                continue;
            }

            $result[] = LinkedProduct::fromArray([
                'type' => $type,
                'productIdentifier' => $productIdentity->getObjectIdentifier(),
            ]);
        }

        return $result;
    }

    /**
     * @param array $mainVariation
     *
     * @return Property[]
     */
    private function getProperties(array $mainVariation)
    {
        $result = [];

        $properties = $mainVariation['variationProperties'];

        foreach ($properties as $property) {
            if (!$property['property']['isSearchable']) {
                continue;
            }

            $backendName = $property['property']['backendName'];

            $values = [];
            $translations = [];

            if ($property['property']['valueType'] === 'empty' && null !== $property['property']['propertyGroupId']) {
                $propertyGroupNames = $this->itemsPropertiesGroupsNamesApi->findOne($property['property']['propertyGroupId']);

                if (empty($propertyGroupNames[0]['name'])) {
                    continue;
                }

                $backendName = $propertyGroupNames[0]['name'];

                foreach ($propertyGroupNames as $name) {
                    $languageIdentifier = $this->identityService->findOneBy([
                        'adapterIdentifier' => $name['lang'],
                        'adapterName' => PlentymarketsAdapter::NAME,
                        'objectType' => Language::TYPE,
                    ]);

                    if (null === $languageIdentifier) {
                        continue;
                    }

                    $translations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'name',
                        'value' => $name['name'],
                    ]);
                }

                $propertyNames = $this->itemsPropertiesNamesApi->findOne($property['property']['id']);
                $valueTranslations = [];

                foreach ($propertyNames as $name) {
                    $languageIdentifier = $this->identityService->findOneBy([
                        'adapterIdentifier' => $name['lang'],
                        'adapterName' => PlentymarketsAdapter::NAME,
                        'objectType' => Language::TYPE,
                    ]);

                    if (null === $languageIdentifier) {
                        continue;
                    }

                    $valueTranslations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'value',
                        'value' => $name['name'],
                    ]);
                }

                $values[] = Value::fromArray([
                    'value' => (string) $property['property']['backendName'],
                    'translations' => $valueTranslations,
                ]);
            } elseif ($property['property']['valueType'] === 'text') {
                if (empty($property['names'][0]['value'])) {
                    continue;
                }

                $valueTranslations = [];

                foreach ($property['names'] as $name) {
                    $languageIdentifier = $this->identityService->findOneBy([
                        'adapterIdentifier' => $name['lang'],
                        'adapterName' => PlentymarketsAdapter::NAME,
                        'objectType' => Language::TYPE,
                    ]);

                    if (null === $languageIdentifier) {
                        continue;
                    }

                    $translations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'name',
                        'value' => $property['property']['backendName'],
                    ]);

                    $valueTranslations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'value',
                        'value' => $name['value'],
                    ]);
                }

                $values[] = Value::fromArray([
                    'value' => (string) $property['names'][0]['value'],
                    'translations' => $valueTranslations,
                ]);
            } elseif ($property['property']['valueType'] === 'selection') {
                if (null === $property['propertySelectionId']) {
                    continue;
                }

                $valueTranslations = [];

                foreach ($property['propertySelection'] as $selection) {
                    $languageIdentifier = $this->identityService->findOneBy([
                        'adapterIdentifier' => $selection['lang'],
                        'adapterName' => PlentymarketsAdapter::NAME,
                        'objectType' => Language::TYPE,
                    ]);

                    if (null === $languageIdentifier) {
                        continue;
                    }

                    $translations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'name',
                        'value' => $property['property']['backendName'],
                    ]);

                    $valueTranslations[] = Translation::fromArray([
                        'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                        'property' => 'value',
                        'value' => $selection['name'],
                    ]);
                }

                $values[] = Value::fromArray([
                    'value' => (string) $property['propertySelection'][0]['name'],
                    'translations' => $valueTranslations,
                ]);
            } elseif ($property['property']['valueType'] === 'int') {
                if (null === $property['valueInt']) {
                    continue;
                }

                $values[] = Value::fromArray([
                    'value' => (string) $property['valueInt'],
                ]);
            } elseif ($property['property']['valueType'] === 'float') {
                if (null === $property['valueFloat']) {
                    continue;
                }

                $values[] = Value::fromArray([
                    'value' => (string) $property['valueFloat'],
                ]);
            } elseif ($property['property']['valueType'] === 'file') {
                $this->logger->notice('file properties are not supported', ['variation', $mainVariation['id']]);

                continue;
            }

            $result[] = Property::fromArray([
                'name' => $backendName,
                'values' => $values,
                'translations' => $translations,
            ]);
        }

        return $result;
    }

    /**
     * @param array $mainVariation
     *
     * @return null|DateTimeImmutable
     */
    private function getAvailableFrom(array $mainVariation)
    {
        if (!empty($mainVariation['availableUntil'])) {
            return new DateTimeImmutable('now');
        }

        return null;
    }

    /**
     * @param array $mainVariation
     *
     * @return null|DateTimeImmutable
     */
    private function getAvailableTo(array $mainVariation)
    {
        if (!empty($mainVariation['availableUntil'])) {
            return new DateTimeImmutable($mainVariation['availableUntil']);
        }

        return null;
    }

    /**
     * @param array $mainVariation
     *
     * @return null|DateTimeImmutable
     */
    private function getCreatedAt(array $mainVariation)
    {
        if (!empty($mainVariation['createdAt'])) {
            return new DateTimeImmutable($mainVariation['createdAt']);
        }

        return null;
    }

    /**
     * @param Variation[] $variations
     * @param array       $mainVariation
     *
     * @return bool
     */
    private function getActive(array $variations, array $mainVariation)
    {
        $checkActiveMainVariation = json_decode($this->configService->get('check_active_main_variation'));

        if ($checkActiveMainVariation && !$mainVariation['isActive']) {
            return false;
        }

        foreach ($variations as $variation) {
            if ($variation->getActive()) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param Variation[] $variations
     *
     * @return Property[]
     */
    private function getVariantConfiguration(array $variations = [])
    {
        $properties = [[]];

        foreach ($variations as $variation) {
            $properties[] = $variation->getProperties();
        }

        return array_merge(...$properties);
    }

    /**
     * @param array $product
     *
     * @return Attribute[]
     */
    private function getAttributes(array $product)
    {
        $attributes = [];

        for ($i = 0; $i < 20; ++$i) {
            $key = 'free' . ($i + 1);

            if (!array_key_exists($key, $product)) {
                continue;
            }

            $attributes[] = Attribute::fromArray([
                'key' => $key,
                'value' => (string) $product[$key],
            ]);
        }

        $attributes[] = $this->getShortDescriptionAsAttribute($product);
        $attributes[] = $this->getTechnicalDataAsAttribute($product);
        $attributes[] = $this->getAgeRestrictionAsAttribute($product);
        $attributes[] = $this->getSecondProductNameAsAttribute($product);
        $attributes[] = $this->getThirdProductNameAsAttribute($product);
        $attributes[] = $this->getItemIdAsAttribute($product);

        return $attributes;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getShortDescriptionAsAttribute(array $product)
    {
        $translations = [];

        foreach ($product['texts'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'value',
                'value' => $text['shortDescription'],
            ]);
        }

        $attribute = new Attribute();
        $attribute->setKey('shortDescription');
        $attribute->setValue((string) $product['texts'][0]['shortDescription']);
        $attribute->setTranslations($translations);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getTechnicalDataAsAttribute(array $product)
    {
        $translations = [];

        foreach ($product['texts'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'value',
                'value' => $text['technicalData'],
            ]);
        }

        $attribute = new Attribute();
        $attribute->setKey('technicalDescription');
        $attribute->setValue((string) $product['texts'][0]['technicalData']);
        $attribute->setTranslations($translations);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getSecondProductNameAsAttribute(array $product)
    {
        $translations = [];

        foreach ($product['texts'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'value',
                'value' => $text['name2'],
            ]);
        }

        $attribute = new Attribute();
        $attribute->setKey('secondProductName');
        $attribute->setValue((string) $product['texts'][0]['name2']);
        $attribute->setTranslations($translations);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getThirdProductNameAsAttribute(array $product)
    {
        $translations = [];

        foreach ($product['texts'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'value',
                'value' => $text['name3'],
            ]);
        }

        $attribute = new Attribute();
        $attribute->setKey('thirdProductName');
        $attribute->setValue((string) $product['texts'][0]['name3']);
        $attribute->setTranslations($translations);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getAgeRestrictionAsAttribute(array $product)
    {
        $attribute = new Attribute();
        $attribute->setKey('ageRestriction');
        $attribute->setValue((string) $product['ageRestriction']);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Attribute
     */
    private function getItemIdAsAttribute(array $product)
    {
        $attribute = new Attribute();
        $attribute->setKey('itemId');
        $attribute->setValue((string) $product['id']);

        return $attribute;
    }

    /**
     * @param array $product
     *
     * @return Badge[]
     */
    private function getBadges(array $product)
    {
        if ($product['storeSpecial'] === 3) {
            $badge = new Badge();
            $badge->setType(Badge::TYPE_HIGHLIGHT);

            return [$badge];
        }

        return [];
    }
}


<?php

namespace SystemConnector\Console\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use SystemConnector\Console\OutputHandler\OutputHandlerInterface;
use SystemConnector\DefinitionProvider\DefinitionProviderInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\ServiceBus\Query\FetchTransferObjectQuery;
use SystemConnector\ServiceBus\QueryType;
use SystemConnector\ServiceBus\ServiceBus;

class DumpCommand extends Command
{
    /**
     * @var ServiceBus
     */
    private $serviceBus;

    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var DefinitionProviderInterface
     */
    private $definitionProvider;

    /**
     * @var OutputHandlerInterface
     */
    private $outputHandler;

    public function __construct(
        ServiceBus $serviceBus,
        IdentityServiceInterface $identityService,
        DefinitionProviderInterface $definitionProvider,
        OutputHandlerInterface $outputHandler
    ) {
        $this->serviceBus = $serviceBus;
        $this->identityService = $identityService;
        $this->definitionProvider = $definitionProvider;
        $this->outputHandler = $outputHandler;

        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName('plentyconnector:dump');
        $this->setDescription('retrieves and dumps transfer objects');
        $this->addArgument(
            'identifier',
            InputArgument::REQUIRED,
            'Object Identifier'
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $this->outputHandler->initialize($input, $output);

        $identity = $this->identityService->findOneBy([
            'objectIdentifier' => $input->getArgument('identifier'),
        ]);

        if (null === $identity) {
            $this->outputHandler->writeLine('could not find identity for identifier');

            return;
        }

        $i = 1;
        $definitions = $this->definitionProvider->getConnectorDefinitions($identity->getObjectType());

        foreach ($definitions as $definition) {
            $result = $this->serviceBus->handle(new FetchTransferObjectQuery(
                $definition->getOriginAdapterName(),
                $definition->getObjectType(),
                QueryType::ONE,
                $input->getArgument('identifier')
            ));

            if (!$result) {
                $this->outputHandler->writeLine('could not find TransferObject');

                continue;
            }

            foreach ($result as $item) {
                $this->outputHandler->writeLine();
                $this->outputHandler->writeLine(sprintf('TransferObject %s:', $i++));
                $this->outputHandler->writeLine(json_encode($item, JSON_PRETTY_PRINT));
            }
        }
    }
}


<?php

namespace SystemConnector\ConfigService;

use SystemConnector\ConfigService\Storage\ConfigServiceStorageInterface;
use Traversable;

class ConfigService implements ConfigServiceInterface
{
    /**
     * @var ConfigServiceStorageInterface[]
     */
    private $storages;

    public function __construct(Traversable $storage)
    {
        $this->storages = iterator_to_array($storage);
    }

    /**
     * {@inheritdoc}
     */
    public function getAll()
    {
        $storage = reset($this->storages);

        return $storage->getAll();
    }

    /**
     * {@inheritdoc}
     */
    public function get($key, $default = null)
    {
        $storage = reset($this->storages);

        $result = $storage->get($key);

        if ($result !== null) {
            return $result;
        }

        return $default;
    }

    /**
     * {@inheritdoc}
     */
    public function set($key, $value)
    {
        $storage = reset($this->storages);

        $storage->set($key, $value);
    }
}


<?php

namespace SystemConnector\IdentityService\Storage;

use Doctrine\DBAL\Connection;
use SystemConnector\IdentityService\Struct\Identity;

class DatabaseIdentityServiceStorage implements IdentityServiceStorageInterface
{
    /**
     * @var Connection
     */
    private $connection;

    /**
     * @var string
     */
    private $table;

    public function __construct(
        Connection $connection,
        $table
    ) {
        $this->connection = $connection;
        $this->table = $table;
    }

    /**
     * {@inheritdoc}
     */
    public function findOneBy(array $criteria = [])
    {
        $queryBuilder = $this->connection->createQueryBuilder();
        $queryBuilder->from($this->table, 'identity');
        $queryBuilder->select([
            'identity.objectIdentifier',
            'identity.objectType',
            'identity.adapterIdentifier',
            'identity.adapterName',
        ]);
        $queryBuilder->setMaxResults(1);

        foreach ($criteria as $key => $value) {
            $queryBuilder->andWhere($queryBuilder->expr()->eq($key, ':' . $key));
            $queryBuilder->setParameter(':' . $key, $value);
        }

        $result = $queryBuilder->execute()->fetch();

        if (!empty($result)) {
            return Identity::fromArray([
                'objectIdentifier' => $result['objectIdentifier'],
                'objectType' => $result['objectType'],
                'adapterIdentifier' => $result['adapterIdentifier'],
                'adapterName' => $result['adapterName'],
            ]);
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function findBy(array $criteria = [])
    {
        $queryBuilder = $this->connection->createQueryBuilder();
        $queryBuilder->from($this->table, 'identity');
        $queryBuilder->select([
            'identity.objectIdentifier',
            'identity.objectType',
            'identity.adapterIdentifier',
            'identity.adapterName',
        ]);

        foreach ($criteria as $key => $value) {
            $queryBuilder->andWhere($queryBuilder->expr()->eq($key, ':' . $key));
            $queryBuilder->setParameter(':' . $key, $value);
        }

        $result = $queryBuilder->execute()->fetchAll();

        return array_map(function (array $result) {
            return Identity::fromArray([
                'objectIdentifier' => $result['objectIdentifier'],
                'objectType' => $result['objectType'],
                'adapterIdentifier' => $result['adapterIdentifier'],
                'adapterName' => $result['adapterName'],
            ]);
        }, $result);
    }

    /**
     * {@inheritdoc}
     */
    public function insert(Identity $identity)
    {
        $this->connection->insert($this->table, [
            'adapterIdentifier' => $identity->getAdapterIdentifier(),
            'adapterName' => $identity->getAdapterName(),
            'objectIdentifier' => $identity->getObjectIdentifier(),
            'objectType' => $identity->getObjectType(),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function update(Identity $identity, array $data = [])
    {
        $this->connection->update($this->table, $data, [
            'adapterIdentifier' => $identity->getAdapterIdentifier(),
            'adapterName' => $identity->getAdapterName(),
            'objectIdentifier' => $identity->getObjectIdentifier(),
            'objectType' => $identity->getObjectType(),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function remove(Identity $identity)
    {
        $this->connection->delete($this->table, [
            'adapterIdentifier' => $identity->getAdapterIdentifier(),
            'adapterName' => $identity->getAdapterName(),
            'objectIdentifier' => $identity->getObjectIdentifier(),
            'objectType' => $identity->getObjectType(),
        ]);
    }
}


<?php

namespace PlentymarketsAdapter\ResponseParser\Product\Image;

use Exception;
use PlentymarketsAdapter\Helper\MediaCategoryHelper;
use PlentymarketsAdapter\PlentymarketsAdapter;
use PlentymarketsAdapter\ResponseParser\Media\MediaResponseParserInterface;
use Psr\Log\LoggerInterface;
use SystemConnector\IdentityService\IdentityServiceInterface;
use SystemConnector\TransferObject\Language\Language;
use SystemConnector\TransferObject\Product\Image\Image;
use SystemConnector\TransferObject\Shop\Shop;
use SystemConnector\ValueObject\Translation\Translation;

class ImageResponseParser implements ImageResponseParserInterface
{
    /**
     * @var IdentityServiceInterface
     */
    private $identityService;

    /**
     * @var MediaResponseParserInterface
     */
    private $mediaResponseParser;

    /**
     * @var LoggerInterface
     */
    private $logger;

    public function __construct(
        IdentityServiceInterface $identityService,
        MediaResponseParserInterface $mediaResponseParser,
        LoggerInterface $logger
    ) {
        $this->identityService = $identityService;
        $this->mediaResponseParser = $mediaResponseParser;
        $this->logger = $logger;
    }

    /**
     * @param array $entry
     * @param array $texts
     * @param array $result
     *
     * @return null|Image
     */
    public function parseImage(array $entry, array $texts, array &$result)
    {
        try {
            if (!empty($entry['names'][0]['name'])) {
                $name = $entry['names'][0]['name'];
            } else {
                $name = $texts[0]['name1'];
            }

            $alternate = $name;
            if (!empty($entry['names'][0]['alternate'])) {
                $alternate = $entry['names'][0]['alternate'];
            }

            $media = $this->mediaResponseParser->parse([
                'mediaCategory' => MediaCategoryHelper::PRODUCT,
                'id' => $entry['id'],
                'link' => $entry['url'],
                'filename' => $entry['cleanImageName'],
                'name' => $name,
                'hash' => $entry['md5Checksum'],
                'alternateName' => $alternate,
                'translations' => $this->getMediaTranslations($entry, $texts),
            ]);

            $result[$media->getIdentifier()] = $media;

            $linkedShops = array_filter($entry['availabilities'], function (array $availabilitiy) {
                return $availabilitiy['type'] === 'mandant';
            });

            $shopIdentifiers = array_map(function ($shop) {
                $shopIdentity = $this->identityService->findOneBy([
                    'adapterIdentifier' => (string) $shop['value'],
                    'adapterName' => PlentymarketsAdapter::NAME,
                    'objectType' => Shop::TYPE,
                ]);

                if (null === $shopIdentity) {
                    return null;
                }

                return $shopIdentity->getObjectIdentifier();
            }, $linkedShops);

            $image = new Image();
            $image->setMediaIdentifier($media->getIdentifier());
            $image->setShopIdentifiers(array_filter($shopIdentifiers));
            $image->setPosition((int) $entry['position']);
            $image->setName($name);
            $image->setTranslations($media->getTranslations());

            return $image;
        } catch (Exception $exception) {
            $this->logger->notice('error when parsing product image', [
                'id' => $entry['id'],
                'url' => $entry['url'],
            ]);
        }

        return null;
    }

    /**
     * @param array $image
     * @param array $productTexts
     *
     * @return array
     */
    private function getMediaTranslations(array $image, array $productTexts)
    {
        $translations = [];

        foreach ($image['names'] as $text) {
            $languageIdentifier = $this->identityService->findOneBy([
                'adapterIdentifier' => $text['lang'],
                'adapterName' => PlentymarketsAdapter::NAME,
                'objectType' => Language::TYPE,
            ]);

            if (null === $languageIdentifier) {
                continue;
            }

            if (!empty($text['name'])) {
                $name = $text['name'];
            } else {
                $name = '';

                foreach ($productTexts as $productText) {
                    if ($text['lang'] === $productText['lang']) {
                        $name = $productText['name1'];
                    }
                }
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'name',
                'value' => $name,
            ]);

            if (!empty($text['alternate'])) {
                $alternate = $text['alternate'];
            } else {
                $alternate = '';

                foreach ($productTexts as $productText) {
                    if ($text['lang'] === $productText['lang']) {
                        $alternate = $productText['name1'];
                    }
                }
            }

            $translations[] = Translation::fromArray([
                'languageIdentifier' => $languageIdentifier->getObjectIdentifier(),
                'property' => 'alternateName',
                'value' => $alternate,
            ]);
        }

        return $translations;
    }
}


<?php

namespace ShopwareAdapter\Helper;

use SystemConnector\TransferObject\AttributableInterface;
use SystemConnector\TransferObject\TranslateableInterface;
use SystemConnector\ValidatorService\ValidatorServiceInterface;
use SystemConnector\ValueObject\Attribute\Attribute;

class AttributeHelper implements AttributeHelperInterface
{
    /**
     * @var ValidatorServiceInterface
     */
    private $validator;

    public function __construct(ValidatorServiceInterface $validator)
    {
        $this->validator = $validator;
    }

    /**
     * {@inheritdoc}
     */
    public function addFieldAsAttribute(AttributableInterface $object, $fieldName)
    {
        if (!method_exists($object, 'getAttributes')) {
            return;
        }

        $method = 'get' . ucfirst($fieldName);

        if (!method_exists($object, $method)) {
            return;
        }

        $fieldValue = $object->$method();

        if (null === $fieldValue) {
            return;
        }

        $attribute = new Attribute();
        $attribute->setKey($fieldName);
        $attribute->setValue($fieldValue);

        if ($object instanceof TranslateableInterface) {
            $translations = $object->getTranslations();
            $newTranslations = [];

            foreach ($translations as $translation) {
                if ($fieldName === $translation->getProperty()) {
                    $newTranslation = clone $translation;
                    $newTranslation->setProperty('value');

                    $newTranslations[] = $newTranslation;
                }
            }

            $attribute->setTranslations($newTranslations);
        }

        $this->validator->validate($attribute);

        $object->setAttributes(array_merge($object->getAttributes(), [$attribute]));
    }
}