<?php

/**
 * TechDivision\Import\Services\ImportProcessor
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Services;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\RegistryKeys;
use TechDivision\Import\Actions\ActionInterface;
use TechDivision\Import\Connection\ConnectionInterface;
use TechDivision\Import\Assembler\CategoryAssemblerInterface;
use TechDivision\Import\Repositories\StoreRepositoryInterface;
use TechDivision\Import\Repositories\CategoryRepositoryInterface;
use TechDivision\Import\Repositories\TaxClassRepositoryInterface;
use TechDivision\Import\Repositories\LinkTypeRepositoryInterface;
use TechDivision\Import\Repositories\ImageTypeRepositoryInterface;
use TechDivision\Import\Repositories\EavAttributeRepositoryInterface;
use TechDivision\Import\Repositories\StoreWebsiteRepositoryInterface;
use TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface;
use TechDivision\Import\Repositories\LinkAttributeRepositoryInterface;
use TechDivision\Import\Repositories\CoreConfigDataRepositoryInterface;
use TechDivision\Import\Repositories\CategoryVarcharRepositoryInterface;
use TechDivision\Import\Repositories\EavAttributeSetRepositoryInterface;
use TechDivision\Import\Repositories\EavAttributeGroupRepositoryInterface;
use TechDivision\Import\Repositories\CustomerGroupRepositoryInterface;

/**
 * Processor implementation to load global data.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class ImportProcessor implements ImportProcessorInterface
{

    /**
     * A connection to use.
     *
     * @var \TechDivision\Import\Connection\ConnectionInterface
     */
    protected $connection;

    /**
     * The category assembler instance.
     *
     * @var \TechDivision\Import\Assembler\CategoryAssemblerInterface
     */
    protected $categoryAssembler;

    /**
     * The repository to access categories.
     *
     * @var \TechDivision\Import\Repositories\CategoryRepository
     */
    protected $categoryRepository;

    /**
     * The repository to access category varchar values.
     *
     * @var \TechDivision\Import\Repositories\CategoryVarcharRepositoryInterface
     */
    protected $categoryVarcharRepository;

    /**
     * The repository to access EAV attributes.
     *
     * @var \TechDivision\Import\Repositories\EavAttributeRepositoryInterface
     */
    protected $eavAttributeRepository;

    /**
     * The repository to access EAV attribute sets.
     *
     * @var \TechDivision\Import\Repositories\EavAttributeSetRepositoryInterface
     */
    protected $eavAttributeSetRepository;

    /**
     * The repository to access EAV attribute groups.
     *
     * @var \TechDivision\Import\Repositories\EavAttributeGroupRepositoryInterface
     */
    protected $eavAttributeGroupRepository;

    /**
     * The repository to access EAV entity types.
     *
     * @var \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface
     */
    protected $eavEntityTypeRepository;

    /**
     * The repository to access stores.
     *
     * @var \TechDivision\Import\Repositories\StoreRepositoryInterface
     */
    protected $storeRepository;

    /**
     * The repository to access store websites.
     *
     * @var \TechDivision\Import\Repositories\StoreWebsiteRepositoryInterface
     */
    protected $storeWebsiteRepository;

    /**
     * The repository to access tax classes.
     *
     * @var \TechDivision\Import\Repositories\TaxClassRepositoryInterface
     */
    protected $taxClassRepository;

    /**
     * The repository to access link types.
     *
     * @var \TechDivision\Import\Repositories\LinkTypeRepositoryInterface
     */
    protected $linkTypeRepository;

    /**
     * The repository to access image types.
     *
     * @var \TechDivision\Import\Repositories\ImageTypeRepositoryInterface
     */
    protected $imageTypeRepository;

    /**
     * The repository to access link attributes.
     *
     * @var \TechDivision\Import\Repositories\LinkAttributeRepositoryInterface
     */
    protected $linkAttributeRepository;

    /**
     * The repository to access the configuration.
     *
     * @var \TechDivision\Import\Repositories\CoreConfigDataRepositoryInterface
     */
    protected $coreConfigDataRepository;

    /**
     * The repository to access the customer groups.
     *
     * @var \TechDivision\Import\Repositories\CustomerGroupRepositoryInterface
     */
    protected $customerGroupRepository;

    /**
     * The action for store CRUD methods.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $storeAction;

    /**
     * The action for store group CRUD methods.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $storeGroupAction;

    /**
     * The action for store website CRUD methods.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $storeWebsiteAction;

    /**
     * Initialize the processor with the necessary assembler and repository instances.
     *
     * @param \TechDivision\Import\Connection\ConnectionInterface                    $connection                  The connection to use
     * @param \TechDivision\Import\Assembler\CategoryAssemblerInterface              $categoryAssembler           The category assembler instance
     * @param \TechDivision\Import\Repositories\CategoryRepositoryInterface          $categoryRepository          The repository to access categories
     * @param \TechDivision\Import\Repositories\CategoryVarcharRepositoryInterface   $categoryVarcharRepository   The repository to access category varchar values
     * @param \TechDivision\Import\Repositories\EavAttributeRepositoryInterface      $eavAttributeRepository      The repository to access EAV attributes
     * @param \TechDivision\Import\Repositories\EavAttributeSetRepositoryInterface   $eavAttributeSetRepository   The repository to access EAV attribute sets
     * @param \TechDivision\Import\Repositories\EavAttributeGroupRepositoryInterface $eavAttributeGroupRepository The repository to access EAV attribute groups
     * @param \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface     $eavEntityTypeRepository     The repository to access EAV entity types
     * @param \TechDivision\Import\Repositories\StoreRepositoryInterface             $storeRepository             The repository to access stores
     * @param \TechDivision\Import\Repositories\StoreWebsiteRepositoryInterface      $storeWebsiteRepository      The repository to access store websites
     * @param \TechDivision\Import\Repositories\TaxClassRepositoryInterface          $taxClassRepository          The repository to access tax classes
     * @param \TechDivision\Import\Repositories\LinkTypeRepositoryInterface          $linkTypeRepository          The repository to access link types
     * @param \TechDivision\Import\Repositories\LinkAttributeRepositoryInterface     $linkAttributeRepository     The repository to access link attributes
     * @param \TechDivision\Import\Repositories\CoreConfigDataRepositoryInterface    $coreConfigDataRepository    The repository to access the configuration
     * @param \TechDivision\Import\Repositories\CustomerGroupRepositoryInterface     $customerGroupRepository     The repository to access the customer groups
     * @param \TechDivision\Import\Actions\ActionInterface                           $storeAction                 The action with the store CRUD methods
     * @param \TechDivision\Import\Actions\ActionInterface                           $storeGroupAction            The action with the store group CRUD methods
     * @param \TechDivision\Import\Actions\ActionInterface                           $storeWebsiteAction          The action with the store website CRUD methods
     * @param \TechDivision\Import\Repositories\ImageTypeRepositoryInterface         $imageTypeRepository         The repository to access images types
     */
    public function __construct(
        ConnectionInterface $connection,
        CategoryAssemblerInterface $categoryAssembler,
        CategoryRepositoryInterface $categoryRepository,
        CategoryVarcharRepositoryInterface $categoryVarcharRepository,
        EavAttributeRepositoryInterface $eavAttributeRepository,
        EavAttributeSetRepositoryInterface $eavAttributeSetRepository,
        EavAttributeGroupRepositoryInterface $eavAttributeGroupRepository,
        EavEntityTypeRepositoryInterface $eavEntityTypeRepository,
        StoreRepositoryInterface $storeRepository,
        StoreWebsiteRepositoryInterface $storeWebsiteRepository,
        TaxClassRepositoryInterface $taxClassRepository,
        LinkTypeRepositoryInterface $linkTypeRepository,
        LinkAttributeRepositoryInterface $linkAttributeRepository,
        CoreConfigDataRepositoryInterface $coreConfigDataRepository,
        CustomerGroupRepositoryInterface $customerGroupRepository,
        ActionInterface $storeAction,
        ActionInterface $storeGroupAction,
        ActionInterface $storeWebsiteAction,
        ImageTypeRepositoryInterface $imageTypeRepository
    ) {
        $this->setConnection($connection);
        $this->setCategoryAssembler($categoryAssembler);
        $this->setCategoryRepository($categoryRepository);
        $this->setCategoryVarcharRepository($categoryVarcharRepository);
        $this->setEavAttributeRepository($eavAttributeRepository);
        $this->setEavAttributeSetRepository($eavAttributeSetRepository);
        $this->setEavAttributeGroupRepository($eavAttributeGroupRepository);
        $this->setEavEntityTypeRepository($eavEntityTypeRepository);
        $this->setStoreRepository($storeRepository);
        $this->setStoreWebsiteRepository($storeWebsiteRepository);
        $this->setTaxClassRepository($taxClassRepository);
        $this->setLinkTypeRepository($linkTypeRepository);
        $this->setLinkAttributeRepository($linkAttributeRepository);
        $this->setCoreConfigDataRepository($coreConfigDataRepository);
        $this->setStoreAction($storeAction);
        $this->setStoreGroupAction($storeGroupAction);
        $this->setStoreWebsiteAction($storeWebsiteAction);
        $this->setImageTypeRepository($imageTypeRepository);
        $this->setCustomerGroupRepository($customerGroupRepository);
    }

    /**
     * Set's the passed connection.
     *
     * @param \TechDivision\Import\Connection\ConnectionInterface $connection The connection to set
     *
     * @return void
     */
    public function setConnection(ConnectionInterface $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Return's the connection.
     *
     * @return \TechDivision\Import\Connection\ConnectionInterface The connection instance
     */
    public function getConnection()
    {
        return $this->connection;
    }

    /**
     * Turns off autocommit mode. While autocommit mode is turned off, changes made to the database via the PDO
     * object instance are not committed until you end the transaction by calling ProductProcessor::commit().
     * Calling ProductProcessor::rollBack() will roll back all changes to the database and return the connection
     * to autocommit mode.
     *
     * @return boolean Returns TRUE on success or FALSE on failure
     * @link http://php.net/manual/en/pdo.begintransaction.php
     */
    public function beginTransaction()
    {
        return $this->connection->beginTransaction();
    }

    /**
     * Commits a transaction, returning the database connection to autocommit mode until the next call to
     * ProductProcessor::beginTransaction() starts a new transaction.
     *
     * @return boolean Returns TRUE on success or FALSE on failure
     * @link http://php.net/manual/en/pdo.commit.php
     */
    public function commit()
    {
        return $this->connection->commit();
    }

    /**
     * Rolls back the current transaction, as initiated by ProductProcessor::beginTransaction().
     *
     * If the database was set to autocommit mode, this function will restore autocommit mode after it has
     * rolled back the transaction.
     *
     * Some databases, including MySQL, automatically issue an implicit COMMIT when a database definition
     * language (DDL) statement such as DROP TABLE or CREATE TABLE is issued within a transaction. The implicit
     * COMMIT will prevent you from rolling back any other changes within the transaction boundary.
     *
     * @return boolean Returns TRUE on success or FALSE on failure
     * @link http://php.net/manual/en/pdo.rollback.php
     */
    public function rollBack()
    {
        return $this->connection->rollBack();
    }

    /**
     * Set's the category assembler.
     *
     * @param \TechDivision\Import\Assembler\CategoryAssemblerInterface $categoryAssembler The category assembler
     *
     * @return void
     */
    public function setCategoryAssembler($categoryAssembler)
    {
        $this->categoryAssembler = $categoryAssembler;
    }

    /**
     * Return's the category assembler.
     *
     * @return \TechDivision\Import\Assembler\CategoryAssemblerInterface The category assembler instance
     */
    public function getCategoryAssembler()
    {
        return $this->categoryAssembler;
    }

    /**
     * Set's the repository to access categories.
     *
     * @param \TechDivision\Import\Repositories\CategoryRepositoryInterface $categoryRepository The repository to access categories
     *
     * @return void
     */
    public function setCategoryRepository(CategoryRepositoryInterface $categoryRepository)
    {
        $this->categoryRepository = $categoryRepository;
    }

    /**
     * Return's the repository to access categories.
     *
     * @return \TechDivision\Import\Repositories\CategoryRepositoryInterface The repository instance
     */
    public function getCategoryRepository()
    {
        return $this->categoryRepository;
    }

    /**
     * Return's the repository to access category varchar values.
     *
     * @param \TechDivision\Import\Repositories\CategoryVarcharRepositoryInterface $categoryVarcharRepository The repository instance
     *
     * @return void
     */
    public function setCategoryVarcharRepository(CategoryVarcharRepositoryInterface $categoryVarcharRepository)
    {
        $this->categoryVarcharRepository = $categoryVarcharRepository;
    }

    /**
     * Return's the repository to access category varchar values.
     *
     * @return \TechDivision\Import\Repositories\CategoryVarcharRepositoryInterface The repository instance
     */
    public function getCategoryVarcharRepository()
    {
        return $this->categoryVarcharRepository;
    }

    /**
     * Set's the repository to access EAV attributes.
     *
     * @param \TechDivision\Import\Repositories\EavAttributeRepositoryInterface $eavAttributeRepository The repository to access EAV attributes
     *
     * @return void
     */
    public function setEavAttributeRepository(EavAttributeRepositoryInterface $eavAttributeRepository)
    {
        $this->eavAttributeRepository = $eavAttributeRepository;
    }

    /**
     * Return's the repository to access EAV attributes.
     *
     * @return \TechDivision\Import\Repositories\EavAttributeRepositoryInterface The repository instance
     */
    public function getEavAttributeRepository()
    {
        return $this->eavAttributeRepository;
    }

    /**
     * Set's the repository to access EAV attribute sets.
     *
     * @param \TechDivision\Import\Repositories\EavAttributeSetRepositoryInterface $eavAttributeSetRepository The repository the access EAV attribute sets
     *
     * @return void
     */
    public function setEavAttributeSetRepository(EavAttributeSetRepositoryInterface $eavAttributeSetRepository)
    {
        $this->eavAttributeSetRepository = $eavAttributeSetRepository;
    }

    /**
     * Return's the repository to access EAV attribute sets.
     *
     * @return \TechDivision\Import\Repositories\EavAttributeSetRepositoryInterface The repository instance
     */
    public function getEavAttributeSetRepository()
    {
        return $this->eavAttributeSetRepository;
    }

    /**
     * Set's the repository to access EAV attribute groups.
     *
     * @param \TechDivision\Import\Repositories\EavAttributeGroupRepositoryInterface $eavAttributeGroupRepository The repository the access EAV attribute groups
     *
     * @return void
     */
    public function setEavAttributeGroupRepository(EavAttributeGroupRepositoryInterface $eavAttributeGroupRepository)
    {
        $this->eavAttributeGroupRepository = $eavAttributeGroupRepository;
    }

    /**
     * Return's the repository to access EAV attribute groups.
     *
     * @return \TechDivision\Import\Repositories\EavAttributeGroupRepositoryInterface The repository instance
     */
    public function getEavAttributeGroupRepository()
    {
        return $this->eavAttributeGroupRepository;
    }

    /**
     * Return's the repository to access EAV entity types.
     *
     * @return \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface The repository instance
     */
    public function getEavEntityTypeRepository()
    {
        return $this->eavEntityTypeRepository;
    }

    /**
     * Set's the repository to access EAV entity types.
     *
     * @param \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface $eavEntityTypeRepository The repository the access EAV entity types
     *
     * @return void
     */
    public function setEavEntityTypeRepository(EavEntityTypeRepositoryInterface $eavEntityTypeRepository)
    {
        $this->eavEntityTypeRepository = $eavEntityTypeRepository;
    }

    /**
     * Set's the repository to access stores.
     *
     * @param \TechDivision\Import\Repositories\StoreRepositoryInterface $storeRepository The repository the access stores
     *
     * @return void
     */
    public function setStoreRepository(StoreRepositoryInterface $storeRepository)
    {
        $this->storeRepository = $storeRepository;
    }

    /**
     * Return's the repository to access stores.
     *
     * @return \TechDivision\Import\Repositories\StoreRepositoryInterface The repository instance
     */
    public function getStoreRepository()
    {
        return $this->storeRepository;
    }

    /**
     * Set's the repository to access store websites.
     *
     * @param \TechDivision\Import\Repositories\StoreWebsiteRepositoryInterface $storeWebsiteRepository The repository the access store websites
     *
     * @return void
     */
    public function setStoreWebsiteRepository(StoreWebsiteRepositoryInterface $storeWebsiteRepository)
    {
        $this->storeWebsiteRepository = $storeWebsiteRepository;
    }

    /**
     * Return's the repository to access store websites.
     *
     * @return \TechDivision\Import\Repositories\StoreWebsiteRepositoryInterface The repository instance
     */
    public function getStoreWebsiteRepository()
    {
        return $this->storeWebsiteRepository;
    }

    /**
     * Set's the repository to access tax classes.
     *
     * @param \TechDivision\Import\Repositories\TaxClassRepositoryInterface $taxClassRepository The repository the access stores
     *
     * @return void
     */
    public function setTaxClassRepository(TaxClassRepositoryInterface $taxClassRepository)
    {
        $this->taxClassRepository = $taxClassRepository;
    }

    /**
     * Return's the repository to access tax classes.
     *
     * @return \TechDivision\Import\Repositories\TaxClassRepositoryInterface The repository instance
     */
    public function getTaxClassRepository()
    {
        return $this->taxClassRepository;
    }

    /**
     * Set's the repository to access link types.
     *
     * @param \TechDivision\Import\Repositories\LinkTypeRepositoryInterface $linkTypeRepository The repository to access link types
     *
     * @return void
     */
    public function setLinkTypeRepository(LinkTypeRepositoryInterface $linkTypeRepository)
    {
        $this->linkTypeRepository = $linkTypeRepository;
    }

    /**
     * Return's the repository to access link types.
     *
     * @return \TechDivision\Import\Repositories\LinkTypeRepositoryInterface The repository instance
     */
    public function getLinkTypeRepository()
    {
        return $this->linkTypeRepository;
    }

    /**
     * Set's the repository to access link attributes.
     *
     * @param \TechDivision\Import\Repositories\LinkAttributeRepositoryInterface $linkAttributeRepository The repository to access link attributes
     *
     * @return void
     */
    public function setLinkAttributeRepository(LinkAttributeRepositoryInterface $linkAttributeRepository)
    {
        $this->linkAttributeRepository = $linkAttributeRepository;
    }

    /**
     * Return's the repository to access link attributes.
     *
     * @return \TechDivision\Import\Repositories\LinkAttributeRepositoryInterface The repository instance
     */
    public function getLinkAttributeRepository()
    {
        return $this->linkAttributeRepository;
    }

    /**
     * Set's the repository to access link types.
     *
     * @param \TechDivision\Import\Repositories\ImageTypeRepositoryInterface $imageTypeRepository The repository to access image types
     *
     * @return void
     */
    public function setImageTypeRepository(ImageTypeRepositoryInterface $imageTypeRepository)
    {
        $this->imageTypeRepository = $imageTypeRepository;
    }

    /**
     * Return's the repository to access link types.
     *
     * @return \TechDivision\Import\Repositories\ImageTypeRepositoryInterface The repository instance
     */
    public function getImageTypeRepository()
    {
        return $this->imageTypeRepository;
    }

    /**
     * Set's the repository to access the Magento 2 configuration.
     *
     * @param \TechDivision\Import\Repositories\CoreConfigDataRepositoryInterface $coreConfigDataRepository The repository to access the Magento 2 configuration
     *
     * @return void
     */
    public function setCoreConfigDataRepository(CoreConfigDataRepositoryInterface $coreConfigDataRepository)
    {
        $this->coreConfigDataRepository = $coreConfigDataRepository;
    }

    /**
     * Return's the repository to access the Magento 2 configuration.
     *
     * @return \TechDivision\Import\Repositories\CoreConfigDataRepositoryInterface The repository instance
     */
    public function getCoreConfigDataRepository()
    {
        return $this->coreConfigDataRepository;
    }

    /**
     * Set's the repository to access the customer groups.
     *
     * @param \TechDivision\Import\Repositories\CustomerGroupRepositoryInterface $customerGroupRepository The repository to access the customer groups
     *
     * @return void
     */
    public function setCustomerGroupRepository(CustomerGroupRepositoryInterface $customerGroupRepository)
    {
        $this->customerGroupRepository = $customerGroupRepository;
    }

    /**
     * Return's the repository to access the customer groups.
     *
     * @return \TechDivision\Import\Repositories\CustomerGroupRepositoryInterface The repository instance
     */
    public function getCustomerGroupRepository()
    {
        return $this->customerGroupRepository;
    }

    /**
     * Set's the action with the store CRUD methods.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $storeAction The action with the store CRUD methods
     *
     * @return void
     */
    public function setStoreAction(ActionInterface $storeAction)
    {
        $this->storeAction = $storeAction;
    }

    /**
     * Return's the action with the store CRUD methods.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The action instance
     */
    public function getStoreAction()
    {
        return $this->storeAction;
    }

    /**
     * Set's the action with the store group CRUD methods.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $storeGroupAction The action with the store group CRUD methods
     *
     * @return void
     */
    public function setStoreGroupAction(ActionInterface $storeGroupAction)
    {
        $this->storeGroupAction = $storeGroupAction;
    }

    /**
     * Return's the action with the store group CRUD methods.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The action instance
     */
    public function getStoreGroupAction()
    {
        return $this->storeGroupAction;
    }

    /**
     * Set's the action with the store website CRUD methods.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $storeWebsiteAction The action with the store website CRUD methods
     *
     * @return void
     */
    public function setStoreWebsiteAction(ActionInterface $storeWebsiteAction)
    {
        $this->storeWebsiteAction = $storeWebsiteAction;
    }

    /**
     * Return's the action with the store website CRUD methods.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The action instance
     */
    public function getStoreWebsiteAction()
    {
        return $this->storeWebsiteAction;
    }

    /**
     * Return's the EAV attribute set with the passed ID.
     *
     * @param integer $id The ID of the EAV attribute set to load
     *
     * @return array The EAV attribute set
     */
    public function getEavAttributeSet($id)
    {
        return $this->getEavAttributeSetRepository()->load($id);
    }

    /**
     * Return's the attribute sets for the passed entity type ID.
     *
     * @param mixed $entityTypeId The entity type ID to return the attribute sets for
     *
     * @return array|boolean The attribute sets for the passed entity type ID
     */
    public function getEavAttributeSetsByEntityTypeId($entityTypeId)
    {
        return $this->getEavAttributeSetRepository()->findAllByEntityTypeId($entityTypeId);
    }

    /**
     * Return's the attribute groups for the passed attribute set ID, whereas the array
     * is prepared with the attribute group names as keys.
     *
     * @param mixed $attributeSetId The EAV attribute set ID to return the attribute groups for
     *
     * @return array|boolean The EAV attribute groups for the passed attribute ID
     */
    public function getEavAttributeGroupsByAttributeSetId($attributeSetId)
    {
        return $this->getEavAttributeGroupRepository()->findAllByAttributeSetId($attributeSetId);
    }

    /**
     * Return's an array with the EAV attributes for the passed entity type ID and attribute set name.
     *
     * @param integer $entityTypeId     The entity type ID of the EAV attributes to return
     * @param string  $attributeSetName The attribute set name of the EAV attributes to return
     *
     * @return array The
     */
    public function getEavAttributesByEntityTypeIdAndAttributeSetName($entityTypeId, $attributeSetName)
    {
        return $this->getEavAttributeRepository()->findAllByEntityTypeIdAndAttributeSetName($entityTypeId, $attributeSetName);
    }

    /**
     * Return's an array with the available EAV attributes for the passed option value and store ID.
     *
     * @param string $optionValue The option value of the EAV attributes
     * @param string $storeId     The store ID of the EAV attribues
     *
     * @return array The array with all available EAV attributes
     */
    public function getEavAttributesByOptionValueAndStoreId($optionValue, $storeId)
    {
        return $this->getEavAttributeRepository()->findAllByOptionValueAndStoreId($optionValue, $storeId);
    }

    /**
     * Return's the first EAV attribute for the passed option value and store ID.
     *
     * @param string $optionValue The option value of the EAV attributes
     * @param string $storeId     The store ID of the EAV attribues
     *
     * @return array The array with the EAV attribute
     */
    public function getEavAttributeByOptionValueAndStoreId($optionValue, $storeId)
    {
        return $this->getEavAttributeRepository()->findOneByOptionValueAndStoreId($optionValue, $storeId);
    }

    /**
     * Return's an array with the available EAV attributes for the passed is user defined flag.
     *
     * @param integer $isUserDefined The flag itself
     *
     * @return array The array with the EAV attributes matching the passed flag
     */
    public function getEavAttributesByIsUserDefined($isUserDefined = 1)
    {
        return $this->getEavAttributeRepository()->findAllByIsUserDefined($isUserDefined);
    }

    /**
     * Return's an array with the available EAV attributes for the passed is entity type and
     * user defined flag.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attributes to return
     * @param integer $isUserDefined The flag itself
     *
     * @return array The array with the EAV attributes matching the passed entity type and user defined flag
     */
    public function getEavAttributesByEntityTypeIdAndIsUserDefined($entityTypeId, $isUserDefined = 1)
    {
        return $this->getEavAttributeRepository()->findAllByEntityTypeIdAndIsUserDefined($entityTypeId, $isUserDefined);
    }

    /**
     * Return's an array with the availabe EAV attributes for the passed entity type.
     *
     * @param integer $entityTypeId The entity type ID of the EAV attributes to return
     *
     * @return array The array with the EAV attributes matching the passed entity type
     */
    public function getEavAttributesByEntityTypeId($entityTypeId)
    {
        return $this->getEavAttributeRepository()->findAllByEntityTypeId($entityTypeId);
    }

    /**
     * Return's an array with all available EAV entity types with the entity type code as key.
     *
     * @return array The available link types
     */
    public function getEavEntityTypes()
    {
        return $this->getEavEntityTypeRepository()->findAll();
    }

    /**
     * Return's an array with the available stores.
     *
     * @return array The array with the available stores
     */
    public function getStores()
    {
        return $this->getStoreRepository()->findAll();
    }

    /**
     * Return's the default store.
     *
     * @return array The default store
     */
    public function getDefaultStore()
    {
        return $this->getStoreRepository()->findOneByDefault();
    }

    /**
     * Return's an array with the available store websites.
     *
     * @return array The array with the available store websites
     */
    public function getStoreWebsites()
    {
        return $this->getStoreWebsiteRepository()->findAll();
    }

    /**
     * Return's an array with the available tax classes.
     *
     * @return array The array with the available tax classes
     */
    public function getTaxClasses()
    {
        return $this->getTaxClassRepository()->findAll();
    }

    /**
     * Return's an array with all available categories.
     *
     * @return array The available categories
     */
    public function getCategories()
    {
        return $this->getCategoryRepository()->findAll();
    }

    /**
     * Return's an array with the root categories with the store code as key.
     *
     * @return array The root categories
     */
    public function getRootCategories()
    {
        return $this->getCategoryRepository()->findAllRootCategories();
    }

    /**
     * Returns the category varchar values for the categories with
     * the passed with the passed entity IDs.
     *
     * @param array $entityIds The array with the category IDs
     *
     * @return mixed The category varchar values
     */
    public function getCategoryVarcharsByEntityIds(array $entityIds)
    {
        return $this->getCategoryVarcharRepository()->findAllByEntityIds($entityIds);
    }

    /**
     * Return's an array with all available link types.
     *
     * @return array The available link types
     */
    public function getLinkTypes()
    {
        return $this->getLinkTypeRepository()->findAll();
    }

    /**
     * Return's an array with all available link attributes.
     *
     * @return array The available link attributes
     */
    public function getLinkAttributes()
    {
        return $this->getLinkAttributeRepository()->findAll();
    }

    /**
     * Return's an array with all available image types.
     *
     * @return array The available image types
     */
    public function getImageTypes()
    {
        return $this->getImageTypeRepository()->findAll();
    }

    /**
     * Return's an array with the Magento 2 configuration.
     *
     * @return array The Magento 2 configuration
     */
    public function getCoreConfigData()
    {
        return $this->getCoreConfigDataRepository()->findAll();
    }

    /**
     * Returns the customer groups.
     *
     * @return array The customer groups
     */
    public function getCustomerGroups()
    {
        return $this->getCustomerGroupRepository()->findAll();
    }

    /**
     * Persist's the passed store.
     *
     * @param array $store The store to persist
     *
     * @return void
     */
    public function persistStore(array $store)
    {
        return $this->getStoreAction()->persist($store);
    }

    /**
     * Persist's the passed store group.
     *
     * @param array $storeGroup The store group to persist
     *
     * @return void
     */
    public function persistStoreGroup(array $storeGroup)
    {
        return $this->getStoreGroupAction()->persist($storeGroup);
    }

    /**
     * Persist's the passed store website.
     *
     * @param array $storeWebsite The store website to persist
     *
     * @return void
     */
    public function persistStoreWebsite(array $storeWebsite)
    {
        return $this->getStoreWebsiteAction()->persist($storeWebsite);
    }

    /**
     * Returns the array with the global data necessary for the
     * import process.
     *
     * @return array The array with the global data
     */
    public function getGlobalData()
    {

        // initialize the array for the global data
        $globalData = array();

        // initialize the global data
        $globalData[RegistryKeys::STORES] = $this->getStores();
        $globalData[RegistryKeys::LINK_TYPES] = $this->getLinkTypes();
        $globalData[RegistryKeys::TAX_CLASSES] = $this->getTaxClasses();
        $globalData[RegistryKeys::IMAGE_TYPES] = $this->getImageTypes();
        $globalData[RegistryKeys::DEFAULT_STORE] = $this->getDefaultStore();
        $globalData[RegistryKeys::STORE_WEBSITES] = $this->getStoreWebsites();
        $globalData[RegistryKeys::LINK_ATTRIBUTES] = $this->getLinkAttributes();
        $globalData[RegistryKeys::ROOT_CATEGORIES] = $this->getRootCategories();
        $globalData[RegistryKeys::CUSTOMER_GROUPS] = $this->getCustomerGroups();
        $globalData[RegistryKeys::CORE_CONFIG_DATA] = $this->getCoreConfigData();
        $globalData[RegistryKeys::ENTITY_TYPES] = $eavEntityTypes = $this->getEavEntityTypes();

        // prepare the attribute sets
        $eavAttributes = array();
        $eavAttributeSets = array();
        $eavAttributeGroups = array();
        foreach ($eavEntityTypes as $eavEntityTypeCode => $eavEntityType) {
            // load the attribute sets for the entity type
            $attributeSets = $this->getEavAttributeSetsByEntityTypeId($entityTypeId = $eavEntityType[MemberNames::ENTITY_TYPE_ID]);
            // append the attribute sets to the array
            $eavAttributeSets[$eavEntityTypeCode] = $attributeSets;

            // iterate over the attribute sets and initialize the attributes
            foreach ($attributeSets as $attributeSet) {
                // load the attribute set name
                $eavAttributeSetName = $attributeSet[MemberNames::ATTRIBUTE_SET_NAME];

                // load the attributes for the attribute set
                $eavAttributes[$eavEntityTypeCode][$eavAttributeSetName] = $this->getEavAttributesByEntityTypeIdAndAttributeSetName(
                    $entityTypeId,
                    $eavAttributeSetName
                );

                // load the attribute group for the attribute set
                $eavAttributeGroups[$eavEntityTypeCode][$eavAttributeSetName] = $this->getEavAttributeGroupsByAttributeSetId(
                    $attributeSet[MemberNames::ATTRIBUTE_SET_ID]
                );
            }
        }

        // prepare the user defined attributes
        $eavUserDefinedAttributes = array();
        foreach ($eavEntityTypes as $eavEntityTypeCode => $eavEntityType) {
            // load the user defined attributes for the entity type
            $eavUserDefinedAttributes[$eavEntityTypeCode] = $this->getEavAttributesByEntityTypeIdAndIsUserDefined(
                $eavEntityType[MemberNames::ENTITY_TYPE_ID]
            );
        }

        // initialize the arrays with the EAV attributes, EAV user defined attributes and attribute sets/groups
        $globalData[RegistryKeys::EAV_ATTRIBUTES] = $eavAttributes;
        $globalData[RegistryKeys::ATTRIBUTE_SETS] = $eavAttributeSets;
        $globalData[RegistryKeys::ATTRIBUTE_GROUPS] = $eavAttributeGroups;
        $globalData[RegistryKeys::EAV_USER_DEFINED_ATTRIBUTES] = $eavUserDefinedAttributes;

        // initialize categories per store view
        $globalData[RegistryKeys::CATEGORIES] = array();
        foreach ($globalData[RegistryKeys::STORES] as $storeView) {
            $storeViewCode = $storeView[MemberNames::CODE];
            $storeViewId = $storeView[MemberNames::STORE_ID];
            $globalData[RegistryKeys::CATEGORIES][$storeViewCode] = $this->categoryAssembler->getCategoriesWithResolvedPathByStoreView($storeViewId);
        }

        // return the array
        return $globalData;
    }
}


<?php

/**
 * TechDivision\Import\Repositories\AbstractSqlStatementRepository
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Repositories;

/**
 * Abstract repository class for the SQL statements to use.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
abstract class AbstractSqlStatementRepository implements SqlStatementRepositoryInterface
{

    /**
     * The initializes SQL statements.
     *
     * @var array
     */
    protected $preparedStatements = array();

    /**
     * Returns the SQL statement with the passed ID.
     *
     * @param string $id The ID of the SQL statement to return
     *
     * @return string The SQL statement
     * @throws \Exception Is thrown, if the SQL statement with the passed key cannot be found
     */
    public function load($id)
    {

        // try to find the SQL statement with the passed key
        if (isset($this->preparedStatements[$id])) {
            return $this->preparedStatements[$id];
        }

        // throw an exception if NOT available
        throw new \Exception(sprintf('Can\'t find SQL statement with ID %s', $id));
    }
}


<?php

/**
 * TechDivision\Import\Subjects\I18n\DateConverterFactory
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2018 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Subjects\I18n;

use Symfony\Component\DependencyInjection\ContainerInterface;
use TechDivision\Import\Configuration\SubjectConfigurationInterface;

/**
 * Factory for date converter instances.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2018 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class DateConverterFactory implements DateConverterFactoryInterface
{

    /**
     * The DI container instance.
     *
     * @var \Symfony\Component\DependencyInjection\ContainerInterface
     */
    protected $container;

    /**
     * Initialize the factory with the DI container instance.
     *
     * @param \Symfony\Component\DependencyInjection\ContainerInterface $container The DI container instance
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Creates and returns the date converter instance for the subject with the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject The subject to create the date converter for
     *
     * @return \TechDivision\Import\Subjects\I18n\DateConverterInterface The date converter instance
     */
    public function createDateConverter(SubjectConfigurationInterface $subject)
    {

        // create a new date converter instance for the subject with the passed configuration
        $dateConverter = $this->container->get($subject->getDateConverter()->getId());
        $dateConverter->setSubjectConfiguration($subject);

        // return the date converter instance
        return $dateConverter;
    }
}


<?php

/**
 * TechDivision\Import\Subjects\ExportableTrait
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Subjects;

use TechDivision\Import\Utils\ColumnKeys;
use TechDivision\Import\Adapter\ExportAdapterInterface;

/**
 * The trait implementation for the artefact export functionality.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
trait ExportableTrait
{

    /**
     * The array containing the data for product type configuration (configurables, bundles, etc).
     *
     * @var array
     */
    protected $artefacs = array();

    /**
     * The export adapter instance.
     *
     * @var \TechDivision\Import\Adapter\ExportAdapterInterface
     */
    protected $exportAdapter;

    /**
     * The ID of the product that has been created recently.
     *
     * @var string
     */
    protected $lastEntityId;

    /**
     * Return's the artefacts for post-processing.
     *
     * @return array The artefacts
     */
    public function getArtefacts()
    {
        return $this->artefacs;
    }

    /**
     * Add the passed product type artefacts to the product with the
     * last entity ID.
     *
     * @param string  $type      The artefact type, e. g. configurable
     * @param array   $artefacts The product type artefacts
     * @param boolean $override  Whether or not the artefacts for the actual entity ID has to be overwritten
     *
     * @return void
     * @uses \TechDivision\Import\Product\Subjects\BunchSubject::getLastEntityId()
     */
    public function addArtefacts($type, array $artefacts, $override = true)
    {

        // query whether or not, any artefacts are available
        if (sizeof($artefacts) === 0) {
            return;
        }

        // serialize the original data
        array_walk($artefacts, function (&$artefact) {
            if (isset($artefact[ColumnKeys::ORIGINAL_DATA])) {
                $artefact[ColumnKeys::ORIGINAL_DATA] = serialize($artefact[ColumnKeys::ORIGINAL_DATA]);
            }
        });

        // query whether or not, existing artefacts has to be overwritten
        if ($override === true) {
            $this->overrideArtefacts($type, $artefacts);
        } else {
            $this->appendArtefacts($type, $artefacts);
        }
    }

    /**
     * Add the passed product type artefacts to the product with the
     * last entity ID and overrides existing ones with the same key.
     *
     * @param string $type      The artefact type, e. g. configurable
     * @param array  $artefacts The product type artefacts
     *
     * @return void
     */
    protected function overrideArtefacts($type, array $artefacts)
    {
        foreach ($artefacts as $key => $artefact) {
            $this->artefacs[$type][$this->getLastEntityId()][$key] = $artefact;
        }
    }

    /**
     * Append's the passed product type artefacts to the product with the
     * last entity ID.
     *
     * @param string $type      The artefact type, e. g. configurable
     * @param array  $artefacts The product type artefacts
     *
     * @return void
     */
    protected function appendArtefacts($type, array $artefacts)
    {
        foreach ($artefacts as $artefact) {
            $this->artefacs[$type][$this->getLastEntityId()][] = $artefact;
        }
    }

    /**
     * Return the artefacts for the passed type and entity ID.
     *
     * @param string $type     The artefact type, e. g. configurable
     * @param string $entityId The entity ID to return the artefacts for
     *
     * @return array The array with the artefacts
     * @throws \Exception Is thrown, if no artefacts are available
     */
    public function getArtefactsByTypeAndEntityId($type, $entityId)
    {

        // query whether or not, artefacts for the passed params are available
        if (isset($this->artefacs[$type][$entityId])) {
            // load the artefacts
            $artefacts = $this->artefacs[$type][$entityId];

            // unserialize the original data
            array_walk($artefacts, function (&$artefact) {
                if (isset($artefact[ColumnKeys::ORIGINAL_DATA])) {
                    $artefact[ColumnKeys::ORIGINAL_DATA] = unserialize($artefact[ColumnKeys::ORIGINAL_DATA]);
                }
            });

            // return the artefacts
            return $artefacts;
        }

        // throw an exception if not
        throw new \Exception(
            sprintf(
                'Cant\'t load artefacts for type %s and entity ID %d',
                $type,
                $entityId
            )
        );
    }

    /**
     * Queries whether or not artefacts for the passed type and entity ID are available.
     *
     * @param string $type     The artefact type, e. g. configurable
     * @param string $entityId The entity ID to return the artefacts for
     *
     * @return boolean TRUE if artefacts are available, else FALSE
     */
    public function hasArtefactsByTypeAndEntityId($type, $entityId)
    {
        return isset($this->artefacs[$type][$entityId]);
    }

    /**
     * Create's and return's a new empty artefact entity.
     *
     * @param array $columns             The array with the column data
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
     *
     * @return array The new artefact entity
     */
    public function newArtefact(array $columns, array $originalColumnNames = array())
    {

        // initialize the original data and the artefact
        $artefact = array();
        $originalData = array();

        // query whether or not, we've original columns
        if (sizeof($originalColumnNames) > 0) {
            // prepare the original column data
            $originalData[ColumnKeys::ORIGINAL_FILENAME] = $this->getFilename();
            $originalData[ColumnKeys::ORIGINAL_LINE_NUMBER] = $this->getLineNumber();
            $originalData[ColumnKeys::ORIGINAL_COLUMN_NAMES] =  $originalColumnNames;

            // add the original column data to the new artefact
            $artefact = array(ColumnKeys::ORIGINAL_DATA => $originalData);
        }

        // merge the columns into the artefact entity and return it
        return array_merge($artefact, $columns);
    }

    /**
     * Export's the artefacts to CSV files.
     *
     * @param integer $timestamp The timestamp part of the original import file
     * @param string  $counter   The counter part of the origin import file
     *
     * @return void
     */
    public function export($timestamp, $counter)
    {
        $this->getExportAdapter()->export($this->getArtefacts(), $this->getTargetDir(), $timestamp, $counter);
    }

    /**
     * Set's the exporter adapter instance.
     *
     * @param \TechDivision\Import\Adapter\ExportAdapterInterface $exportAdapter The exporter adapter instance
     *
     * @return void
     */
    public function setExportAdapter(ExportAdapterInterface $exportAdapter)
    {
        $this->exportAdapter = $exportAdapter;
    }

    /**
     * Return's the exporter adapter instance.
     *
     * @return \TechDivision\Import\Adapter\ExportAdapterInterface The exporter adapter instance
     */
    public function getExportAdapter()
    {
        return $this->exportAdapter;
    }

    /**
     * Set's the ID of the product that has been created recently.
     *
     * @param string $lastEntityId The entity ID
     *
     * @return void
     */
    public function setLastEntityId($lastEntityId)
    {
        $this->lastEntityId = $lastEntityId;
    }

    /**
     * Return's the ID of the product that has been created recently.
     *
     * @return string The entity Id
     */
    public function getLastEntityId()
    {
        return $this->lastEntityId;
    }
}


<?php

/**
 * TechDivision\Import\Repositories\LinkTypeRepository
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Repositories;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\SqlStatementKeys;

/**
 * Repository implementation to load link type data.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class LinkTypeRepository extends AbstractRepository implements LinkTypeRepositoryInterface
{

    /**
     * The cache for the query results.
     *
     * @var array
     */
    protected $cache = array();

    /**
     * The statement to load the available link types.
     *
     * @var \PDOStatement
     */
    protected $linkTypeStmt;

    /**
     * Initializes the repository's prepared statements.
     *
     * @return void
     */
    public function init()
    {

        // initialize the prepared statements
        $this->linkTypeStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::LINK_TYPES));
    }

    /**
     * Return's an array with all available link types with the link type code as key.
     *
     * @return array The available link types
     */
    public function findAll()
    {

        // query whether or not we've already loaded the value
        if (!isset($this->cache[__METHOD__])) {
            // try to load the link types
            $this->linkTypeStmt->execute();

            // initialize the array for the link types
            $linkTypes = array();

            // load the available link types
            $availableLinkTypes = $this->linkTypeStmt->fetchAll(\PDO::FETCH_ASSOC);

            // prepare the link types => we need the code as key
            foreach ($availableLinkTypes as $linkType) {
                $linkTypes[$linkType[MemberNames::CODE]] = $linkType;
            }

            // append the link types to the cache
            $this->cache[__METHOD__] = $linkTypes;
        }

        // return the link types from the cache
        return $this->cache[__METHOD__];
    }
}


<?php

/**
 * TechDivision\Import\Repositories\ImageTypeRepository
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Reinhard Hampl <r.hampl@techdivision.com>
 * @copyright 2017 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Repositories;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\SqlStatementKeys;

/**
 * Repository implementation to load image type data.
 *
 * @author    Reinhard Hampl <r.hampl@techdivision.com>
 * @copyright 2017 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class ImageTypeRepository extends AbstractRepository implements ImageTypeRepositoryInterface
{

    /**
     * The prepared statement to load the default store.
     *
     * @var \PDOStatement
     */
    protected $imageTypesByEntityTypeCodeAndFrontendInputStmt;

    /**
     * The mapping for the image/thumbnail image types
     *
     * @var array
     */
    protected $defaultMappings = array(
        'image'     => 'base_image',
        'thumbnail' => 'thumbnail_image'
    );

    /**
     * Initializes the repository's prepared statements.
     *
     * @return void
     */
    public function init()
    {

        // initialize the prepared statements
        $this->imageTypesByEntityTypeCodeAndFrontendInputStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::IMAGE_TYPES));
    }

    /**
     * Return's an array with all available image types for the passed entity type code
     * and frontend input type with the link type code as key.
     *
     * @return array The available image types
     */
    public function findAll()
    {

        // initialize the result array
        $result = array();

        // load and the image types from the EAV attribute table
        $this->imageTypesByEntityTypeCodeAndFrontendInputStmt->execute();

        // fetch the image types with the passed parameters
        if ($imageTypes = $this->imageTypesByEntityTypeCodeAndFrontendInputStmt->fetchAll(\PDO::FETCH_ASSOC)) {
            // iterate over the image types found
            foreach ($imageTypes as $imageType) {
                $attributeCode = $imageType[MemberNames::ATTRIBUTE_CODE];
                // map the default image types
                if (isset($this->defaultMappings[$attributeCode])) {
                    $attributeCode = $this->defaultMappings[$attributeCode];
                }

                // add the (mapped) image type
                $result[$attributeCode] = sprintf('%s_label', $attributeCode);
            }
        }

        // return the result
        return $result;
    }
}


<?php

/**
 * TechDivision\Import\Subjects\I18n\SimpleDateConverter
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Subjects\I18n;

use TechDivision\Import\Configuration\SubjectConfigurationInterface;

/**
 * Simple date converter implementation.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class SimpleDateConverter implements DateConverterInterface
{

    /**
     * The subject configuraiton instance.
     *
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
     */
    private $subjectConfiguration;

    /**
     * Sets the subject configuration instance.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subjectConfiguration The subject configuration
     *
     * @return void
     */
    public function setSubjectConfiguration(SubjectConfigurationInterface $subjectConfiguration)
    {
        $this->subjectConfiguration = $subjectConfiguration;
    }

    /**
     * Returns the subject configuration instance.
     *
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
     */
    public function getSubjectConfiguration()
    {
        return $this->subjectConfiguration;
    }

    /**
     * Returns the date converter configuration instance.
     *
     * @return \TechDivision\Import\Configuration\Subject\DateConverterConfigurationInterface The date converter configuration
     */
    protected function getDateConverterConfiguration()
    {
        return $this->getSubjectConfiguration()->getDateConverter();
    }

    /**
     * Converts the passed date into a Magento 2 compatible date format.
     *
     * @param string $date The date to convert
     *
     * @return string The converted date
     */
    public function convert($date)
    {

        // create a DateTime instance from the passed value
        if ($dateTime = \DateTime::createFromFormat($this->getDateConverterConfiguration()->getSourceDateFormat(), $date)) {
            return $dateTime->format('Y-m-d H:i:s');
        }

        // return NULL, if the passed value is NOT a valid date
        return null;
    }
}


<?php

/**
 * TechDivision\Import\Plugins\CacheWarmerPlugin
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Plugins;

use TechDivision\Import\Utils\ConfigurationKeys;
use TechDivision\Import\Utils\DependencyInjectionKeys;

/**
 * Plugin implementation to warm the repository caches.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class CacheWarmerPlugin extends AbstractPlugin
{

    /**
     * Array with the default cache warmers.
     *
     * @var array
     */
    protected $cacheWarmers = array(
        DependencyInjectionKeys::IMPORT_CACHE_WARMER_EAV_ATTRIBUTE_OPTION_VALUE_REPOSITORY
    );

    /**
     * Process the plugin functionality.
     *
     * @return void
     * @throws \Exception Is thrown, if the plugin can not be processed
     */
    public function process()
    {

        // query whether or not additional cache warmers has been configured
        if ($this->getPluginConfiguration()->hasParam(ConfigurationKeys::CACHE_WARMERS)) {
            // try ot load the cache warmers and merge them with the default ones
            $this->cacheWarmers = array_merge(
                $this->cacheWarmers,
                $this->getPluginConfiguration()->getParam(ConfigurationKeys::CACHE_WARMERS)
            );
        }

        // create the instances and warm the repository caches
        foreach ($this->cacheWarmers as $id) {
            $this->getApplication()->getContainer()->get($id)->warm();
        }
    }
}


<?php

/**
 * TechDivision\Import\Subjects\I18n\NumberConverter
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Subjects\I18n;

use TechDivision\Import\Configuration\SubjectConfigurationInterface;

/**
 * Simple number converter implementation.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class SimpleNumberConverter implements NumberConverterInterface
{

    /**
     * The subject configuraiton instance.
     *
     * @var \TechDivision\Import\Configuration\SubjectConfigurationInterface
     */
    private $subjectConfiguration;

    /**
     * The target number formatter.
     *
     * @var \NumberFormatter
     */
    private $formatter;

    /**
     * Initialize the number converter instance.
     */
    public function __construct()
    {
        $this->formatter = \NumberFormatter::create('en_US', \NumberFormatter::DECIMAL);
    }

    /**
     * Sets the subject configuration instance.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subjectConfiguration The subject configuration
     *
     * @return void
     */
    public function setSubjectConfiguration(SubjectConfigurationInterface $subjectConfiguration)
    {
        $this->subjectConfiguration = $subjectConfiguration;
    }

    /**
     * Returns the subject configuration instance.
     *
     * @return \TechDivision\Import\Configuration\SubjectConfigurationInterface The subject configuration
     */
    public function getSubjectConfiguration()
    {
        return $this->subjectConfiguration;
    }

    /**
     * Returns the number converter configuration instance.
     *
     * @return \TechDivision\Import\Configuration\Subject\NumberConverterConfigurationInterface The number converter configuration
     */
    protected function getNumberConverterConfiguration()
    {
        return $this->getSubjectConfiguration()->getNumberConverter();
    }

    /**
     * Returns the target number formatter instance.
     *
     * @return \NumberFormatter The target number formatter instance
     */
    protected function getFormatter()
    {
        return $this->formatter;
    }

    /**
     * Converts the passed number into a float value.
     *
     * @param string $number The number to parse
     *
     * @return float The float value of the number
     */
    public function convert($number)
    {

        // initialize the formatter instance with the source locale
        $formatter = \NumberFormatter::create($this->getNumberConverterConfiguration()->getLocale(), \NumberFormatter::DECIMAL);

        // parse, format and return the value
        return $this->getFormatter()->format($formatter->parse($number));
    }
}


<?php

/**
 * TechDivision\Import\Actions\Processors\StoreWebsiteUpdateProcessor
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Actions\Processors;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\SqlStatementKeys;

/**
 * The store website update processor implementation.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class StoreWebsiteUpdateProcessor extends AbstractUpdateProcessor
{

    /**
     * Return's the array with the SQL statements that has to be prepared.
     *
     * @return array The SQL statements to be prepared
     * @see \TechDivision\Import\Actions\Processors\AbstractBaseProcessor::getStatements()
     */
    protected function getStatements()
    {
        return array(SqlStatementKeys::UPDATE_STORE_WEBSITE => $this->loadStatement(SqlStatementKeys::UPDATE_STORE_WEBSITE));
    }

    /**
     * Update's the passed row.
     *
     * @param array       $row  The row to update
     * @param string|null $name The name of the prepared statement that has to be executed
     *
     * @return string The ID of the updated store website
     */
    public function execute($row, $name = null)
    {
        parent::execute($row, $name);
        return $row[MemberNames::WEBSITE_ID];
    }
}


<?php

/**
 * TechDivision\Import\Plugins\SubjectExecutor
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Plugins;

use TechDivision\Import\Utils\BunchKeys;
use TechDivision\Import\Callbacks\CallbackVisitorInterface;
use TechDivision\Import\Observers\ObserverVisitorInterface;
use TechDivision\Import\Subjects\SubjectFactoryInterface;
use TechDivision\Import\Subjects\ExportableSubjectInterface;
use TechDivision\Import\Configuration\SubjectConfigurationInterface;

/**
 * The subject executor instance.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2017 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class SubjectExecutor implements SubjectExecutorInterface
{

    /**
     * The subject factory instance.
     *
     * @var \TechDivision\Import\Observers\ObserverVisitorInterface
     */
    protected $observerVisitor;

    /**
     * The subject factory instance.
     *
     * @var \TechDivision\Import\Callbacks\CallbackVisitorInterface
     */
    protected $callbackVisitor;

    /**
     * The subject factory instance.
     *
     * @var \TechDivision\Import\Subjects\SubjectFactoryInterface
     */
    protected $subjectFactory;

    /**
     * Initializes the plugin with the application instance.
     *
     * @param \TechDivision\Import\Callbacks\CallbackVisitorInterface $callbackVisitor The callback visitor instance
     * @param \TechDivision\Import\Observers\ObserverVisitorInterface $observerVisitor The observer visitor instance
     * @param \TechDivision\Import\Subjects\SubjectFactoryInterface   $subjectFactory  The subject factory instance
     */
    public function __construct(
        CallbackVisitorInterface $callbackVisitor,
        ObserverVisitorInterface $observerVisitor,
        SubjectFactoryInterface $subjectFactory
    ) {

        // initialize the callback/observer visitors
        $this->callbackVisitor = $callbackVisitor;
        $this->observerVisitor = $observerVisitor;
        $this->subjectFactory = $subjectFactory;
    }

    /**
     * Executes the passed subject.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject  The message with the subject information
     * @param array                                                            $matches  The bunch matches
     * @param string                                                           $serial   The UUID of the actual import
     * @param string                                                           $pathname The path to the file to import
     *
     * @return void
     */
    public function execute(SubjectConfigurationInterface $subject, array $matches, $serial, $pathname)
    {

        // initialize the subject and import the bunch
        $subjectInstance = $this->subjectFactory->createSubject($subject);

        try {
            // setup the subject instance
            $subjectInstance->setUp($serial);

            // initialize the callbacks/observers
            $this->callbackVisitor->visit($subjectInstance);
            $this->observerVisitor->visit($subjectInstance);

            // finally import the CSV file
            $subjectInstance->import($serial, $pathname);

            // query whether or not, we've to export artefacts
            if ($subjectInstance instanceof ExportableSubjectInterface) {
                $subjectInstance->export(
                    $matches[BunchKeys::FILENAME],
                    $matches[BunchKeys::COUNTER]
                );
            }

            // tear down the subject instance
            $subjectInstance->tearDown($serial);
        } catch (\Exception $e) {
            // query whether or not, we've to export artefacts
            if ($subjectInstance instanceof ExportableSubjectInterface) {
                // tear down the subject instance
                $subjectInstance->tearDown($serial);
            }

            // re-throw the exception
            throw $e;
        }
    }
}


<?php

/**
 * TechDivision\Import\Listeners\ClearRegistryListener
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2019 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Listeners;

use League\Event\EventInterface;
use League\Event\AbstractListener;
use TechDivision\Import\ApplicationInterface;
use TechDivision\Import\Services\RegistryProcessorInterface;

/**
 * An listener implementation that initializes the registry.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2019 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class ClearRegistryListener extends AbstractListener
{

    /**
     * The RegistryProcessor instance to handle running threads.
     *
     * @var \TechDivision\Import\Services\RegistryProcessorInterface
     */
    protected $registryProcessor;

    /**
     * Initializes the event.
     *
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry processor instance
     */
    public function __construct(RegistryProcessorInterface $registryProcessor)
    {
        $this->registryProcessor = $registryProcessor;
    }

    /**
     * Handle the event.
     *
     * @param \League\Event\EventInterface              $event       The event that triggered the listener
     * @param \TechDivision\Import\ApplicationInterface $application The application that triggered the listener
     *
     * @return void
     */
    public function handle(EventInterface $event, ApplicationInterface $application = null)
    {
        $this->getRegistryProcessor()->removeAttribute($application->getSerial());
    }

    /**
     * Return's the RegistryProcessor instance to handle the running threads.
     *
     * @return \TechDivision\Import\Services\RegistryProcessor The registry processor instance
     */
    protected function getRegistryProcessor()
    {
        return $this->registryProcessor;
    }
}


<?php

/**
 * TechDivision\Import\Plugins\SubjectPlugin
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Plugins;

use TechDivision\Import\Utils\RegistryKeys;
use TechDivision\Import\ApplicationInterface;
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
use TechDivision\Import\Subjects\FileResolver\FileResolverFactoryInterface;

/**
 * Plugin that processes the subjects.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class SubjectPlugin extends AbstractPlugin
{

    /**
     * The matches for the last processed CSV filename.
     *
     * @var array
     */
    protected $matches = array();

    /**
     * The number of imported bunches.
     *
     * @var integer
     */
    protected $bunches = 0;

    /**
     * The subject executor instance.
     *
     * @var \TechDivision\Import\Plugins\SubjectExecutorInterface
     */
    protected $subjectExecutor;

    /**
     * The file resolver factory instance.
     *
     * @var \TechDivision\Import\Subjects\FileResolver\FileResolverFactoryInterface
     */
    protected $fileResolverFactory;

    /**
     * Initializes the plugin with the application instance.
     *
     * @param \TechDivision\Import\ApplicationInterface                               $application         The application instance
     * @param \TechDivision\Import\Plugins\SubjectExecutorInterface                   $subjectExecutor     The subject executor instance
     * @param \TechDivision\Import\Subjects\FileResolver\FileResolverFactoryInterface $fileResolverFactory The file resolver instance
     */
    public function __construct(
        ApplicationInterface $application,
        SubjectExecutorInterface $subjectExecutor,
        FileResolverFactoryInterface $fileResolverFactory
    ) {

        // call the parent constructor
        parent::__construct($application);

        // set the subject executor and the file resolver factory
        $this->subjectExecutor = $subjectExecutor;
        $this->fileResolverFactory = $fileResolverFactory;
    }

    /**
     * Process the plugin functionality.
     *
     * @return void
     * @throws \Exception Is thrown, if the plugin can not be processed
     */
    public function process()
    {

        try {
            // immediately add the PID to lock this import process
            $this->lock();

            // load the plugin's subjects
            $subjects = $this->getPluginConfiguration()->getSubjects();

            // initialize the array for the status
            $status = array();

            // initialize the status information for the subjects
            /** @var \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject */
            foreach ($subjects as $subject) {
                $status[$subject->getPrefix()] = array();
            }

            // and update it in the registry
            $this->getRegistryProcessor()->mergeAttributesRecursive($this->getSerial(), $status);

            // process all the subjects found in the system configuration
            /** @var \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject */
            foreach ($subjects as $subject) {
                $this->processSubject($subject);
            }

            // update the number of imported bunches
            $this->getRegistryProcessor()->mergeAttributesRecursive(
                $this->getSerial(),
                array(RegistryKeys::BUNCHES => $this->bunches)
            );

            // stop the application if we don't process ANY bunch
            if ($this->bunches === 0) {
                $this->getApplication()->stop(
                    sprintf(
                        'Operation %s has been stopped by %s, because no import files can be found in directory %s',
                        $this->getConfiguration()->getOperationName(),
                        get_class($this),
                        $this->getConfiguration()->getSourceDir()
                    )
                );
            }

            // finally, if a PID has been set (because CSV files has been found),
            // remove it from the PID file to unlock the importer
            $this->unlock();
        } catch (\Exception $e) {
            // finally, if a PID has been set (because CSV files has been found),
            // remove it from the PID file to unlock the importer
            $this->unlock();

            // re-throw the exception
            throw $e;
        }
    }

    /**
     * Process the subject with the passed name/identifier.
     *
     * We create a new, fresh and separate subject for EVERY file here, because this would be
     * the starting point to parallelize the import process in a multithreaded/multiprocessed
     * environment.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject The subject configuration
     *
     * @return void
     */
    protected function processSubject(SubjectConfigurationInterface $subject)
    {

        // initialize the bunch number
        $bunches = 0;

        // load the file resolver for the subject with the passed configuration
        $fileResolver = $this->fileResolverFactory->createFileResolver($subject);

        // load the files
        $files = $fileResolver->loadFiles($serial = $this->getSerial());

        // iterate through all CSV files and process the subjects
        foreach ($files as $filename) {
            // query whether or not that the file is part of the actual bunch
            if ($fileResolver->shouldBeHandled($filename)) {
                // clean-up the OK file, if needed
                $fileResolver->cleanUpOkFile($filename);

                // initialize the subject and import the bunch
                $this->subjectExecutor->execute($subject, $fileResolver->getMatches(), $serial, $filename);

                // raise the number of the imported bunches
                $bunches++;
            }
        }

        // raise the bunch number by the imported bunches
        $this->bunches = $this->bunches + $bunches;

        // reset the file resolver for making it ready parsing the files of the next subject
        $fileResolver->reset();

        // and and log a message that the subject has been processed
        $this->getSystemLogger()->debug(
            sprintf('Successfully processed subject %s with %d bunch(es)!', $subject->getId(), $bunches)
        );
    }
}


<?php

/**
 * TechDivision\Import\Subjects\FileResolver\BunchFileResolver
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Subjects\FileResolver;

use TechDivision\Import\Utils\BunchKeys;
use TechDivision\Import\Exceptions\LineNotFoundException;
use TechDivision\Import\Exceptions\MissingOkFileException;

/**
 * Plugin that processes the subjects.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class SimpleFileResolver extends AbstractFileResolver
{

    /**
     * The regular expression used to load the files with.
     *
     * @var string
     */
    private $regex = '/^.*\/%s\\.%s$/';

    /**
     * The matches for the last processed CSV filename.
     *
     * @var array
     */
    private $matches = array();

    /**
     * Returns the regular expression used to load the files with.
     *
     * @return string The regular expression
     */
    protected function getRegex()
    {
        return $this->regex;
    }

    /**
     * Returns the number of matches found.
     *
     * @return integer The number of matches
     */
    protected function countMatches()
    {
        return sizeof($this->matches);
    }

    /**
     * Adds the passed match to the array with the matches.
     *
     * @param string $name  The name of the match
     * @param string $match The match itself
     *
     * @return void
     */
    protected function addMatch($name, $match)
    {
        $this->matches[strtolower($name)] = $match;
    }

    /**
     * Returns the match with the passed name.
     *
     * @param string $name The name of the match to return
     *
     * @return string|null The match itself
     */
    protected function getMatch($name)
    {
        if (isset($this->matches[$name])) {
            return $this->matches[$name];
        }
    }

    /**
     * Returns the elements the filenames consists of, converted to lowercase.
     *
     * @return array The array with the filename elements
     */
    protected function getPatternKeys()
    {

        // load the pattern keys from the configuration
        $patternKeys = $this->getPatternElements();

        // make sure that they are all lowercase
        array_walk($patternKeys, function (&$value) {
            $value = strtolower($value);
        });

        // return the pattern keys
        return $patternKeys;
    }

    /**
     * Remove's the passed line from the file with the passed name.
     *
     * @param string $line     The line to be removed
     * @param string $filename The name of the file the line has to be removed
     *
     * @return void
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
     */
    protected function removeLineFromFile($line, $filename)
    {
        $this->getApplication()->removeLineFromFile($line, $filename);
    }

    /**
     * Returns the values to create the regex pattern from.
     *
     * @return array The array with the pattern values
     */
    protected function resolvePatternValues()
    {

        // initialize the array
        $elements = array();

        // load the pattern keys
        $patternKeys = $this->getPatternKeys();

        // prepare the pattern values
        foreach ($patternKeys as $element) {
            $elements[] = sprintf('(?<%s>%s)', $element, $this->resolvePatternValue($element));
        }

        // return the pattern values
        return $elements;
    }

    /**
     * Resolves the pattern value for the given element name.
     *
     * @param string $element The element name to resolve the pattern value for
     *
     * @return string|null The resolved pattern value
     */
    protected function resolvePatternValue($element)
    {

        // query whether or not matches has been found OR the counter element has been passed
        if ($this->countMatches() === 0 || BunchKeys::COUNTER === $element) {
            // prepare the method name for the callback to load the pattern value with
            $methodName = sprintf('get%s', ucfirst($element));

            // load the pattern value
            if (in_array($methodName, get_class_methods($this->getFileResolverConfiguration()))) {
                return call_user_func(array($this->getFileResolverConfiguration(), $methodName));
            }

            // stop processing
            return;
        }

        // try to load the pattern value from the matches
        return $this->getMatch($element);
    }

    /**
     * Prepares and returns the pattern for the regex to load the files from the
     * source directory for the passed subject.
     *
     * @return string The prepared regex pattern
     */
    protected function preparePattern()
    {
        return sprintf($this->getRegex(), implode($this->getElementSeparator(), $this->resolvePatternValues()), $this->getSuffix());
    }

    /**
     * Prepares and returns an OK filename from the passed parts.
     *
     * @param array $parts The parts to concatenate the OK filename from
     *
     * @return string The OK filename
     */
    protected function prepareOkFilename(array $parts)
    {
        return sprintf('%s/%s.%s', $this->getSourceDir(), implode($this->getElementSeparator(), $parts), $this->getOkFileSuffix());
    }

    /**
     * Query whether or not the basename, without suffix, of the passed filenames are equal.
     *
     * @param string $filename1 The first filename to compare
     * @param string $filename2 The second filename to compare
     *
     * @return boolean TRUE if the passed files basename are equal, else FALSE
     */
    protected function isEqualFilename($filename1, $filename2)
    {
        return $this->stripSuffix($filename1, $this->getSuffix()) === $this->stripSuffix($filename2, $this->getOkFileSuffix());
    }

    /**
     * Strips the passed suffix, including the (.), from the filename and returns it.
     *
     * @param string $filename The filename to return the suffix from
     * @param string $suffix   The suffix to return
     *
     * @return string The filname without the suffix
     */
    protected function stripSuffix($filename, $suffix)
    {
        return basename($filename, sprintf('.%s', $suffix));
    }

    /**
     * Return's an array with the names of the expected OK files for the actual subject.
     *
     * @return array The array with the expected OK filenames
     */
    protected function getOkFilenames()
    {

        // initialize the array for the available okFilenames
        $okFilenames = array();

        // prepare the OK filenames based on the found CSV file information
        for ($i = 1; $i <= sizeof($patternKeys = $this->getPatternKeys()); $i++) {
            // intialize the array for the parts of the names (prefix, filename + counter)
            $parts = array();
            // load the parts from the matches
            for ($z = 0; $z < $i; $z++) {
                // append the part
                $parts[] = $this->getMatch($patternKeys[$z]);
            }

            // query whether or not, the OK file exists, if yes append it
            if (file_exists($okFilename = $this->prepareOkFilename($parts))) {
                $okFilenames[] = $okFilename;
            }
        }

        // prepare and return the pattern for the OK file
        return $okFilenames;
    }

    /**
     * Resets the file resolver to parse another source directory for new files.
     *
     * @return void
     */
    public function reset()
    {
        $this->matches = array();
    }

    /**
     * Returns the matches.
     *
     * @return array The array with the matches
     */
    public function getMatches()
    {
        return array_merge(array(BunchKeys::FILENAME => date('Ymd-His'), BunchKeys::COUNTER => 1), $this->matches);
    }

    /**
     * Queries whether or not, the passed filename should be handled by the subject.
     *
     * @param string $filename The filename to query for
     *
     * @return boolean TRUE if the file should be handled, else FALSE
     */
    public function shouldBeHandled($filename)
    {

        // initialize the array with the matches
        $matches = array();

        // update the matches, if the pattern matches
        if ($result = preg_match($this->preparePattern(), $filename, $matches)) {
            foreach ($matches as $name => $match) {
                $this->addMatch($name, $match);
            }
        }

        // stop processing, if the filename doesn't match
        return (boolean) $result;
    }

    /**
     * Query whether or not, the passed CSV filename is in the OK file. If the filename was found,
     * the OK file will be cleaned-up.
     *
     * @param string $filename The filename to be cleaned-up
     *
     * @return void
     * @throws \Exception Is thrown, if the passed filename is NOT in the OK file or the OK can not be cleaned-up
     */
    public function cleanUpOkFile($filename)
    {

        // query whether or not the subject needs an OK file, if yes remove the filename from the file
        if ($this->getSubjectConfiguration()->isOkFileNeeded() === false) {
            return;
        }

        try {
            // try to load the expected OK filenames
            if (sizeof($okFilenames = $this->getOkFilenames()) === 0) {
                throw new MissingOkFileException(sprintf('Can\'t find a OK filename for file %s', $filename));
            }

            // iterate over the found OK filenames (should usually be only one, but could be more)
            foreach ($okFilenames as $okFilename) {
                // if the OK filename matches the CSV filename AND the OK file is empty
                if ($this->isEqualFilename($filename, $okFilename) && filesize($okFilename) === 0) {
                    unlink($okFilename);
                    return;
                }

                // else, remove the CSV filename from the OK file
                $this->removeLineFromFile(basename($filename), $fh = fopen($okFilename, 'r+'));
                fclose($fh);

                // if the OK file is empty, delete the file
                if (filesize($okFilename) === 0) {
                    unlink($okFilename);
                }

                // return immediately
                return;
            }

            // throw an exception if either no OK file has been found,
            // or the CSV file is not in one of the OK files
            throw new \Exception(
                sprintf(
                    'Can\'t found filename %s in one of the expected OK files: %s',
                    $filename,
                    implode(', ', $okFilenames)
                )
            );
        } catch (LineNotFoundException $lne) {
            // wrap and re-throw the exception
            throw new \Exception(
                sprintf(
                    'Can\'t remove filename %s from OK file: %s',
                    $filename,
                    $okFilename
                ),
                null,
                $lne
            );
        }
    }
}


<?php

/**
 * TechDivision\Import\Repositories\TaxClassRepository
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Repositories;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\SqlStatementKeys;

/**
 * Repository implementation to load tax class data.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class TaxClassRepository extends AbstractRepository implements TaxClassRepositoryInterface
{

    /**
     * Initializes the repository's prepared statements.
     *
     * @return void
     */
    public function init()
    {

        // initialize the prepared statements
        $this->taxClassesStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::TAX_CLASSES));
    }

    /**
     * Return's an array with the available tax classes and their
     * class names as keys.
     *
     * @return array The array with all available tax classes
     */
    public function findAll()
    {

        // initialize the array with the available tax classes
        $taxClasses = array();

        // execute the prepared statement
        $this->taxClassesStmt->execute();

        // load the available tax classes
        $availableTaxClasses = $this->taxClassesStmt->fetchAll();

        // fetch the tax classes and assemble them as array with the class name as key
        foreach ($availableTaxClasses as $taxClass) {
            $taxClasses[$taxClass[MemberNames::CLASS_NAME]] = $taxClass;
        }

        // return the array with the tax classes
        return $taxClasses;
    }
}


<?php

/**
 * TechDivision\Import\Actions\Processors\StoreGroupUpdateProcessor
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 *
 * PHP version 5
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Actions\Processors;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\SqlStatementKeys;

/**
 * The store group update processor implementation.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import
 * @link      http://www.techdivision.com
 */
class StoreGroupUpdateProcessor extends AbstractUpdateProcessor
{

    /**
     * Return's the array with the SQL statements that has to be prepared.
     *
     * @return array The SQL statements to be prepared
     * @see \TechDivision\Import\Actions\Processors\AbstractBaseProcessor::getStatements()
     */
    protected function getStatements()
    {
        return array(SqlStatementKeys::UPDATE_STORE_GROUP => $this->loadStatement(SqlStatementKeys::UPDATE_STORE_GROUP));
    }

    /**
     * Update's the passed row.
     *
     * @param array       $row  The row to update
     * @param string|null $name The name of the prepared statement that has to be executed
     *
     * @return string The ID of the updated store group
     */
    public function execute($row, $name = null)
    {
        parent::execute($row, $name);
        return $row[MemberNames::GROUP_ID];
    }
}