<?php

/**
 * TechDivision\Import\Plugins\MissingOptionValuesPlugin
 *
 * 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\ColumnKeys;
use TechDivision\Import\Utils\RegistryKeys;
use TechDivision\Import\Utils\SwiftMailerKeys;
use TechDivision\Import\ApplicationInterface;
use TechDivision\Import\Subjects\ExportableTrait;
use TechDivision\Import\Adapter\ExportAdapterInterface;

/**
 * Plugin that exports the missing option values to a CSV file.
 *
 * @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 MissingOptionValuesPlugin extends AbstractPlugin
{

    /**
     * The artefact type.
     *
     * @var string
     */
    const ARTEFACT_TYPE = 'missing-option-values';

    /**
     * The trait providing the export functionality.
     *
     * @var \TechDivision\Import\Subjects\ExportableTrait
     */
    use ExportableTrait;

    /**
     * Initializes the plugin with the application instance.
     *
     * @param \TechDivision\Import\ApplicationInterface           $application   The application instance
     * @param \TechDivision\Import\Adapter\ExportAdapterInterface $exportAdapter The export adapter instance
     */
    public function __construct(ApplicationInterface $application, ExportAdapterInterface $exportAdapter)
    {

        // pass the application to the parent constructor
        parent::__construct($application);

        // set the export adapter
        $this->exportAdapter = $exportAdapter;
    }

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

        // query whether or not, the debug mode has been enabled
        if (!$this->getConfiguration()->isDebugMode()) {
            $this->getSystemLogger()->info('Debug mode is not enabled, missing option values will not be exported');
            return;
        }

        // clear the filecache
        clearstatcache();

        // load the actual status
        $status = $this->getRegistryProcessor()->getAttribute($this->getSerial());

        // query whether or not the configured source directory is available
        if (!is_dir($sourceDir = $status[RegistryKeys::SOURCE_DIRECTORY])) {
            throw new \Exception(sprintf('Configured source directory %s is not available!', $sourceDir));
        }

        // load the array with the missing option values from the status
        $missingOptions = $status[RegistryKeys::MISSING_OPTION_VALUES];

        // if missing option values are found, return immediately
        if (sizeof($missingOptions) === 0) {
            $this->getSystemLogger()->info('Found no missing option values');
            return;
        }

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

        // prepare the artefacts
        foreach ($missingOptions as $attributeCode => $options) {
            foreach ($options as $value => $data) {
                list($counter, $skus) = $data;
                $artefacts[] = $this->newArtefact(
                    array(
                        ColumnKeys::STORE_VIEW_CODE   => null,
                        ColumnKeys::ATTRIBUTE_CODE    => $attributeCode,
                        ColumnKeys::ADMIN_STORE_VALUE => $value,
                        ColumnKeys::VALUE             => $value,
                        ColumnKeys::COUNTER           => $counter,
                        ColumnKeys::UNIQUE_IDENTIFIER => implode(',', array_keys($skus)),
                        ColumnKeys::SORT_ORDER        => null
                    )
                );
            }
        }

        // initialize a dummy last entity ID
        $this->setLastEntityId(0);

        // add the artefacts (missing option values) and export them as CSV file
        $this->addArtefacts(MissingOptionValuesPlugin::ARTEFACT_TYPE, $artefacts);
        $this->export(date('Ymd-His'), $counter = '01');

        // query whether or not a swift mailer has been registered
        if ($swiftMailer = $this->getSwiftMailer()) {
            // the swift mailer configuration
            $swiftMailerConfiguration = $this->getPluginConfiguration()->getSwiftMailer();

            // create the message with the CSV with the missing option values
            $message = $swiftMailer->createMessage()
                                   ->setSubject(sprintf('[%s] %s', $this->getSystemName(), $swiftMailerConfiguration->getParam(SwiftMailerKeys::SUBJECT)))
                                   ->setFrom($swiftMailerConfiguration->getParam(SwiftMailerKeys::FROM))
                                   ->setTo($to = $swiftMailerConfiguration->getParam(SwiftMailerKeys::TO))
                                   ->setBody('The attached CSV file(s) contains the missing attribute option values');

            // load the exported filenames
            $exportedFilenames = $this->getExportAdapter()->getExportedFilenames();

            // attach the CSV files with the missing option values
            foreach ($exportedFilenames as $filename) {
                $message->attach(\Swift_Attachment::fromPath($filename));
            }

            // initialize the array with the failed recipients
            $failedRecipients = array();
            $recipientsAccepted = 0;

            // send the mail
            $recipientsAccepted = $swiftMailer->send($message, $failedRecipients);

            // query whether or not all recipients have been accepted
            if (sizeof($failedRecipients) > 0) {
                $this->getSystemLogger()->error(sprintf('Can\'t send mail to %s', implode(', ', $failedRecipients)));
            }

            // if at least one recipient has been accepted
            if ($recipientsAccepted > 0) {
                // cast 'to' into an array if not already
                is_array($to) ? : $to = (array) $to;
                // remove the NOT accepted recipients
                $acceptedRecipients = array_diff($to, $failedRecipients);

                // log a message with the successfull receivers
                $this->getSystemLogger()->info(
                    sprintf(
                        'Mail successfully sent to %d recipient(s) (%s)',
                        $recipientsAccepted,
                        implode(', ', $acceptedRecipients)
                    )
                );
            }
        }

        // load the system loggers
        $systemLoggers = $this->getSystemLoggers();

        // and and log a message that the missing option values has been exported
        foreach ($systemLoggers as $systemLogger) {
            $systemLogger->error(
                sprintf(
                    'Exported missing option values to file %s!',
                    $filename
                )
            );
        }
    }

    /**
     * Return's the systemm name to be used.
     *
     * @return string The system name to be used
     */
    protected function getSystemName()
    {
        return $this->getConfiguration()->getSystemName();
    }

    /**
     * Return's the target directory the CSV files has to be exported to.
     *
     * @return string The name of the target directory
     */
    protected function getTargetDir()
    {

        // load the actual status
        $status = $this->getRegistryProcessor()->getAttribute($this->getSerial());

        // query whether or not the configured source directory is available
        if (!is_dir($sourceDir = $status[RegistryKeys::SOURCE_DIRECTORY])) {
            throw new \Exception(sprintf('Configured source directory %s is not available!', $sourceDir));
        }

        // return the source directory where we want to export to
        return $sourceDir;
    }
}


<?php

/**
 * TechDivision\Import\Adapter\Csv\ExportConfigFactory
 *
 * 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\Adapter\Csv;

use Goodby\CSV\Export\Standard\ExporterConfig;
use TechDivision\Import\Configuration\CsvConfigurationInterface;

/**
 * Factory implementation for a CSV export configuration.
 *
 * @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 ExportConfigFactory implements ExportConfigFactoryInterface
{

    /**
     * The configuration instance.
     *
     * @var \TechDivision\Import\Configuration\CsvConfigurationInterface
     */
    protected $configuration;

    /**
     * Initialize the adapter with the configuration.
     *
     * @param \TechDivision\Import\Configuration\CsvConfigurationInterface $configuration The configuration instance
     */
    public function __construct(CsvConfigurationInterface $configuration)
    {
        $this->configuration = $configuration;
    }

    /**
     * Factory method to create a new export configuration instance.
     *
     * @return \Goodby\CSV\Export\Standard\ExporterConfig The export configuration
     */
    public function createExportConfig()
    {

        // initialize the lexer configuration
        $config = new ExporterConfig();

        // query whether or not a delimiter character has been configured
        if ($delimiter = $this->configuration->getDelimiter()) {
            $config->setDelimiter($delimiter);
        }

        // query whether or not a custom escape character has been configured
        if ($escape = $this->configuration->getEscape()) {
            $config->setEscape($escape);
        }

        // query whether or not a custom enclosure character has been configured
        if ($enclosure = $this->configuration->getEnclosure()) {
            $config->setEnclosure($enclosure);
        }

        // query whether or not a custom source charset has been configured
        if ($fromCharset = $this->configuration->getFromCharset()) {
            $config->setFromCharset($fromCharset);
        }

        // query whether or not a custom target charset has been configured
        if ($toCharset = $this->configuration->getToCharset()) {
            $config->setToCharset($toCharset);
        }

        // query whether or not a custom file mode has been configured
        if ($fileMode = $this->configuration->getFileMode()) {
            $config->setFileMode($fileMode);
        }

        // return the lexer configuration
        return $config;
    }
}


<?php

/**
 * TechDivision\Import\Callbacks\AbstractBooleanCallback
 *
 * 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-category
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Callbacks;

use TechDivision\Import\Utils\RegistryKeys;
use TechDivision\Import\Observers\AttributeCodeAndValueAwareObserverInterface;

/**
 * A callback implementation that converts the passed boolean value.
 *
 * @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-category
 * @link      http://www.techdivision.com
 */
abstract class AbstractBooleanCallback extends AbstractCallback
{

    /**
     * Array with the string => boolean mapping.
     *
     * @var array
     */
    protected $booleanValues = array(
        'true'  => 1,
        'yes'   => 1,
        '1'     => 1,
        'false' => 0,
        'no'    => 0,
        '0'     => 0
    );

    /**
     * Will be invoked by a observer it has been registered for.
     *
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer
     *
     * @return mixed The modified value
     */
    public function handle(AttributeCodeAndValueAwareObserverInterface $observer)
    {

        // set the observer
        $this->setObserver($observer);

        // load the attribute code and value
        $attributeCode = $observer->getAttributeCode();
        $attributeValue = $observer->getAttributeValue();

        // query whether or not, the passed value can be mapped to a boolean representation
        if (isset($this->booleanValues[strtolower($attributeValue)])) {
            return (boolean) $this->booleanValues[strtolower($attributeValue)];
        }

        // query whether or not we're in debug mode
        if ($this->isDebugMode()) {
            // log a warning and continue with the next value
            $this->getSystemLogger()->warning(
                $this->appendExceptionSuffix(
                    sprintf(
                        'Can\'t map option value "%s" for attribute %s to a boolean representation',
                        $attributeValue,
                        $attributeCode
                    )
                )
            );

            // add the missing option value to the registry
            $this->mergeAttributesRecursive(
                array(
                    RegistryKeys::MISSING_OPTION_VALUES => array(
                        $attributeCode => array(
                            $attributeValue => array(
                                $this->raiseCounter($attributeValue),
                                array($this->getUniqueIdentifier() => true)
                            )
                        )
                    )
                )
            );

            // return NULL, if NO value can be mapped to a boolean representation
            return;
        }

        // throw an exception if the attribute is not available
        throw new \Exception(
            $this->appendExceptionSuffix(
                sprintf(
                    'Can\'t map option value "%s" for attribute %s to a boolean representation',
                    $attributeValue,
                    $attributeCode
                )
            )
        );
    }
}


<?php

/**
 * TechDivision\Import\Actions\Processors\UrlRewriteUpdateProcessor
 *
 * 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 URL rewrite 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 UrlRewriteUpdateProcessor 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_URL_REWRITE => $this->loadStatement(SqlStatementKeys::UPDATE_URL_REWRITE));
    }

    /**
     * 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 product
     */
    public function execute($row, $name = null)
    {
        parent::execute($row, $name);
        return $row[MemberNames::URL_REWRITE_ID];
    }
}


<?php

/**
 * TechDivision\Import\Serializers\AdditionalAttributeCsvSerializer
 *
 * 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\Serializers;

use TechDivision\Import\Configuration\CsvConfigurationInterface;

/**
 * Serializer implementation that un-/serializes the additional product attribues found in the CSV file
 * in the row 'additional_attributes'.
 *
 * @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 AdditionalAttributeCsvSerializer extends AbstractCsvSerializer
{

    /**
     * The factory instance for the CSV value serializer.
     *
     * @var \TechDivision\Import\Serializers\ConfigurationAwareSerializerFactoryInterface
     */
    private $valueCsvSerializerFactory;

    /**
     * The CSV value serializer instance.
     *
     * @var \TechDivision\Import\Serializers\SerializerInterface
     */
    private $valueCsvSerializer;

    /**
     * Initialize the serializer with the passed CSV value serializer factory.
     *
     * @param \TechDivision\Import\Serializers\ConfigurationAwareSerializerFactoryInterface $valueCsvSerializerFactory The CSV value serializer factory
     */
    public function __construct(ConfigurationAwareSerializerFactoryInterface $valueCsvSerializerFactory)
    {
        $this->valueCsvSerializerFactory = $valueCsvSerializerFactory;
    }

    /**
     * Returns the factory instance for the CSV value serializer.
     *
     * @return \TechDivision\Import\Serializers\ConfigurationAwareSerializerFactoryInterface The CSV value serializer factory instance
     */
    protected function getValueCsvSerializerFactory()
    {
        return $this->valueCsvSerializerFactory;
    }

    /**
     * Returns the CSV value serializer instance.
     *
     * @param \TechDivision\Import\Serializers\SerializerInterface $valueCsvSerializer The CSV value serializer instance
     *
     * @return void
     */
    protected function setValueCsvSerializer(SerializerInterface $valueCsvSerializer)
    {
        $this->valueCsvSerializer = $valueCsvSerializer;
    }

    /**
     * Returns the CSV value serializer instance.
     *
     * @return \TechDivision\Import\Serializers\SerializerInterface The CSV value serializer instance
     */
    protected function getValueCsvSerializer()
    {
        return $this->valueCsvSerializer;
    }

    /**
     * Passes the configuration and initializes the serializer.
     *
     * @param \TechDivision\Import\Configuration\CsvConfigurationInterface $configuration The CSV configuration
     *
     * @return void
     */
    public function init(CsvConfigurationInterface $configuration)
    {

        // pass the configuration to the parent instance
        parent::init($configuration);

        // create the CSV value serializer instance
        $this->setValueCsvSerializer($this->getValueCsvSerializerFactory()->createSerializer($configuration));
    }

    /**
     * Unserializes the elements of the passed string.
     *
     * @param string|null $serialized The value to unserialize
     *
     * @return array The unserialized values
     * @see \TechDivision\Import\Serializers\SerializerInterface::unserialize()
     */
    public function unserialize($serialized = null)
    {

        // initialize the array for the unserialized additional attributes
        $attributes = array();

        // explode the additional attributes
        if ($additionalAttributes = $this->explode($serialized)) {
            // iterate over the attributes and append them to the row
            foreach ($additionalAttributes as $additionalAttribute) {
                // explode attribute code/option value from the attribute
                list ($attributeCode, $optionValue) = explode('=', $additionalAttribute);

                // extract the key/value pairs into an array
                $attributes[$attributeCode] = $optionValue;
            }
        }

        // return the array with the unserialized additional attributes
        return $attributes;
    }

    /**
     * Serializes the elements of the passed array.
     *
     * @param array|null $unserialized The serialized data
     *
     * @return string The serialized array
     * @see \TechDivision\Import\Serializers\SerializerInterface::serialize()
     */
    public function serialize(array $unserialized = null)
    {

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

        if (is_array($unserialized)) {
            // serialize the key/value pairs into the array
            foreach ($unserialized as $attributeCode => $attributeValue) {
                $attributes[] = implode('=', array($attributeCode, $attributeValue));
            }
        }

        // serialize the array itself
        return $this->implode($attributes);
    }

    /**
     * Extracts the elements of the passed value by exploding them
     * with the also passed delimiter.
     *
     * @param string|null $value     The value to extract
     * @param string|null $delimiter The delimiter used to extrace the elements
     *
     * @return array|null The exploded values
     * @see \TechDivision\Import\Serializers\SerializerInterface::unserialize()
     */
    public function explode($value = null, $delimiter = null)
    {
        return $this->getValueCsvSerializer()->explode($value, $delimiter);
    }

    /**
     * Compacts the elements of the passed value by imploding them
     * with the also passed delimiter.
     *
     * @param array|null  $value     The values to compact
     * @param string|null $delimiter The delimiter use to implode the values
     *
     * @return string|null The compatected value
     * @see \TechDivision\Import\Serializers\SerializerInterface::serialize()
     */
    public function implode(array $value = null, $delimiter = null)
    {
        return $this->getValueCsvSerializer()->implode($value, $delimiter);
    }
}


<?php

/**
 * TechDivision\Import\Actions\Processors\AbstractBaseProcessor
 *
 * 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\EntityStatus;

/**
 * An abstract processor implementation provide basic CRUD 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
 */
abstract class AbstractBaseProcessor extends AbstractProcessor
{

    /**
     * The array with the statements to be prepared.
     *
     * @var array
     */
    protected $statements = array();

    /**
     * The array with the prepared statements.
     *
     * @var array
     */
    protected $preparedStatements = array();

    /**
     * Return's the array with the SQL statements that has to be prepared.
     *
     * @return array The SQL statements to be prepared
     */
    protected function getStatements()
    {
        return $this->statements;
    }

    /**
     * Add's the prepared statement.
     *
     * @param string        $name              The unique name of the prepared statement
     * @param \PDOStatement $preparedStatement The prepared statement
     *
     * @return void
     */
    protected function addPreparedStatement($name, \PDOStatement $preparedStatement)
    {
        $this->preparedStatements[$name] = $preparedStatement;
    }

    /**
     * Return's the prepared statement.
     *
     * @param string $name The name of the prepared statement to return
     *
     * @return \PDOStatement The prepared statement
     */
    protected function getPreparedStatement($name = null)
    {

        // try to load the prepared statement, or use the default one
        if (isset($this->preparedStatements[$name])) {
            return $this->preparedStatements[$name];
        }

        // return the first (default) prepared statement
        return reset($this->preparedStatements);
    }

    /**
     * Qeuery whether or not the prepared statement is available or not.
     *
     * @param string $name The nqme of the prepared statement
     *
     * @return boolean TRUE if the prepared statement is available, else FALSE
     */
    protected function hasPreparedStatement($name)
    {
        return isset($this->preparedStatements[$name]);
    }

    /**
     * The array with the prepared statements.
     *
     * @return array The prepared statments
     */
    protected function getPreparedStatements()
    {
        return $this->preparedStatements;
    }

    /**
     * Prepare's and return's the passed row by removing the
     * entity status.
     *
     * @param array $row The row to prepare
     *
     * @return array The prepared row
     */
    protected function prepareRow(array $row)
    {

        // remove the entity status
        unset($row[EntityStatus::MEMBER_NAME]);

        // return the prepared row
        return $row;
    }

    /**
     * Implements the CRUD functionality the processor is responsible for,
     * can be one of CREATE, READ, UPDATE or DELETE a entity.
     *
     * @param array       $row  The data to handle
     * @param string|null $name The name of the prepared statement to execute
     *
     * @return void
     */
    public function execute($row, $name = null)
    {
        try {
            // finally execute the prepared statement
            $this->getPreparedStatement($name)->execute($this->prepareRow($row));
        } catch (\PDOException $pdoe) {
            // initialize the SQL statement with the placeholders
            $sql = $this->getPreparedStatement($name)->queryString;

            // replace the placeholders with the values
            foreach ($row as $key => $value) {
                $sql = str_replace(sprintf(':%s', $key), $value, $sql);
            }

            // prepare the error message itself
            $message = sprintf('%s when executing SQL "%s"', $pdoe->getMessage(), preg_replace('/\r\n\s\s+/', ' ', $sql));

            // re-throw the exception with a more detailed error message
            throw new \PDOException($message, null, $pdoe);
        }
    }

    /**
     * Initializes the proceessor with the prepared statements.
     *
     * @return void
     */
    public function init()
    {

        // load the statements
        $statements = $this->getStatements();

        foreach ($statements as $name => $statement) {
            $this->addPreparedStatement($name, $this->getConnection()->prepare($statement));
        }
    }
}


<?php

/**
 * TechDivision\Import\Observers\AdditionalAttributeObserver
 *
 * 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\Observers;

use TechDivision\Import\Utils\ColumnKeys;
use TechDivision\Import\Subjects\SubjectInterface;

/**
 * Observer that prepares the additional product attribues found in the CSV file
 * in the row 'additional_attributes'.
 *
 * @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 AdditionalAttributeObserver extends AbstractObserver
{

    /**
     * Will be invoked by the action on the events the listener has been registered for.
     *
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance
     *
     * @return array The modified row
     * @see \TechDivision\Import\Observers\ObserverInterface::handle()
     */
    public function handle(SubjectInterface $subject)
    {

        // initialize the row
        $this->setSubject($subject);
        $this->setRow($subject->getRow());

        // process the functionality and return the row
        $this->process();

        // return the processed row
        return $this->getRow();
    }

    /**
     * Process the observer's business logic.
     *
     * @return array The processed row
     */
    protected function process()
    {

        // query whether or not the row has additional attributes
        if ($additionalAttributes = $this->getValue(ColumnKeys::ADDITIONAL_ATTRIBUTES)) {
            // explode the additional attributes
            $additionalAttributes = $this->parseAdditionaAttributes($additionalAttributes);
            // load the subject instance
            $subject = $this->getSubject();
            // iterate over the attributes and append them to the row
            foreach ($additionalAttributes as $additionalAttribute) {
                // explode attribute code/option value from the attribute
                list ($attributeCode, $optionValue) = $subject->explode($additionalAttribute, '=');

                // try to load the appropriate key for the value
                if (!$subject->hasHeader($attributeCode)) {
                    $subject->addHeader($attributeCode);
                }

                // append/replace the attribute value
                $this->setValue($attributeCode, $optionValue);

                // add a log message in debug mod
                if ($subject->isDebugMode()) {
                    $subject->getSystemLogger()->debug(
                        sprintf(
                            'Extract new column "%s" with value "%s" from column "%s" in file %s on line %d',
                            $attributeCode,
                            $optionValue,
                            ColumnKeys::ADDITIONAL_ATTRIBUTES,
                            $subject->getFilename(),
                            $subject->getLineNumber()
                        )
                    );
                }
            }
        }
    }

    /**
     * Parses the string with the additional attributes as CSV and returns an array.
     *
     * @param string $additionalAttributes The string with the additional attributes
     *
     * @return array The array with the parsed
     * @link http://php.net/manual/de/function.str-getcsv.php
     */
    protected function parseAdditionaAttributes($additionalAttributes)
    {

        // load the global configuration
        $configuration = $this->getSubject()->getConfiguration()->getConfiguration();

        // initializet delimiter, enclosure and escape char
        $delimiter = $configuration->getDelimiter();
        $enclosure = $configuration->getEnclosure();
        $escape = $configuration->getEscape();

        // parse and return the found data as array
        return str_getcsv($additionalAttributes, $delimiter, $enclosure, $escape);
    }
}


<?php

/**
 * TechDivision\Import\Callbacks\AbstractEavAwareCallback
 *
 * 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\Callbacks;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\ConfigurationInterface;
use TechDivision\Import\Services\EavAwareProcessorInterface;

/**
 * An abstract EAV aware callback implementation.
 *
 * @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
 */
abstract class AbstractEavAwareCallback extends AbstractCallback
{

    /**
     * The EAV aware processor.
     *
     * @var \TechDivision\Import\Services\EavAwareProcessorInterface
     */
    protected $eavAwareProcessor;

    /**
     * The configuration instance.
     *
     * @var \TechDivision\Import\ConfigurationInterface
     */
    protected $configuration;

    /**
     * Initialize the callback with the passed processor instance.
     *
     * @param \TechDivision\Import\ConfigurationInterface              $configuration     The configuration instance
     * @param \TechDivision\Import\Services\EavAwareProcessorInterface $eavAwareProcessor The processor instance
     */
    public function __construct(ConfigurationInterface $configuration, EavAwareProcessorInterface $eavAwareProcessor)
    {
        $this->configuration = $configuration;
        $this->eavAwareProcessor = $eavAwareProcessor;
    }

    /**
     * Return's the EAV aware processor instance.
     *
     * @return \TechDivision\Import\Services\EavAwareProcessorInterface The processor instance
     */
    protected function getEavAwareProcessor()
    {
        return $this->eavAwareProcessor;
    }

    /**
     * Returns the configuration instance.
     *
     * @return \TechDivision\Import\ConfigurationInterface The configuration instance
     */
    protected function getConfiguration()
    {
        return $this->configuration;
    }

    /**
     * Returns the configured entity type code.
     *
     * @return string The entity type code from the configuration
     */
    protected function getEntityTypeCode()
    {
        return $this->getConfiguration()->getEntityTypeCode();
    }

    /**
     * Returns the entity type ID for the configured entity type code.
     *
     * @return integer The entity type ID of the configured entity type
     */
    protected function getEntityTypeId()
    {

        // load the entity type for the configured entity type code
        $entityType = $this->getEavAwareProcessor()->loadEavEntityTypeByEntityTypeCode($this->getEntityTypeCode());

        // return the entity type ID
        return $entityType[MemberNames::ENTITY_TYPE_ID];
    }

    /**
     * Load's and return's the EAV attribute option value with the passed entity type ID, code, store ID and value.
     *
     * @param string  $entityTypeId  The entity type ID of the EAV attribute to load the option value for
     * @param string  $attributeCode The code of the EAV attribute option to load
     * @param integer $storeId       The store ID of the attribute option to load
     * @param string  $value         The value of the attribute option to load
     *
     * @return array The EAV attribute option value
     */
    protected function loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {
        return $this->getEavAwareProcessor()->loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
    }
}


<?php

/**
 * TechDivision\Import\Utils\Generators\CoreConfigDataUidGenerator
 *
 * 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\Utils\Generators;

use TechDivision\Import\Utils\MemberNames;

/**
 * Generator implementation for the 'core_config_data' entity.
 *
 * @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 CoreConfigDataUidGenerator implements GeneratorInterface
{

    /**
     * Create a unique identifier for the passed 'core_config_data' entity.
     *
     * @param array $entity The entity to generate the UID for
     *
     * @return string The unique identifier
     * @see \TechDivision\Import\Utils\Generators\GeneratorInterface::generate()
     */
    public function generate(array $entity)
    {

        // load the data to generate the entity with
        $path = $entity[MemberNames::PATH];
        $scope = $entity[MemberNames::SCOPE];
        $scopeId = $entity[MemberNames::SCOPE_ID];

        // generate and return the entity
        return sprintf('%s/%s/%s', $scope, $scopeId, $path);
    }
}


<?php

/**
 * TechDivision\Import\Repositories\EavAttributeOptionValueRepository
 *
 * 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;

/**
 * Repository implementation to load EAV attribute option value 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
 */
abstract class AbstractCachedRepository extends AbstractRepository implements CachedRepositoryInterface
{

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

    /**
     * References that links to another cache entry.
     *
     * @var array
     */
    protected $references = array();

    /**
     * Prepares a unique cache key for the passed query name and params.
     *
     * @param string $queryName The query name to prepare the cache key for
     * @param array  $params    The query params
     *
     * @return string The prepared cache key
     */
    public function cacheKey($queryName, array $params)
    {
        return sprintf('%s-%s', $queryName, implode('-', $params));
    }

    /**
     * Query whether or not a cache value for the passed cache key is available.
     *
     * @param string $cacheKey The cache key to query for
     *
     * @return boolean TRUE if the a value is available, else FALSE
     */
    public function isCached($cacheKey)
    {
        return isset($this->cache[$this->resolveReference($cacheKey)]);
    }

    /**
     * Inversion of the isCached() method.
     *
     * @param string $cacheKey The cache key to query for
     *
     * @return boolean TRUE if the value is not available, else FALSE
     */
    public function notCached($cacheKey)
    {
        return !$this->isCached($cacheKey);
    }

    /**
     * Add's a cache reference from one key to another.
     *
     * @param string $from The key to reference from
     * @param string $to   The key to reference to
     *
     * @return void
     */
    public function addReference($from, $to)
    {
        $this->references[$from] = $to;
    }

    /**
     * Resolve's the cache key.
     *
     * @param string $from The cache key to resolve
     *
     * @return string The resolved reference
     */
    protected function resolveReference($from)
    {

        // query whether or not a reference exists
        if (isset($this->references[$from])) {
            return $this->references[$from];
        }

        // return the passed reference
        return $from;
    }

    /**
     * Add the passed value to the cache.
     *
     * @param string $cacheKey   The cache key
     * @param mixed  $value      The value to cache
     * @param array  $references An array with references to add
     *
     * @return void
     */
    public function toCache($cacheKey, $value, array $references = array())
    {

        // add the value to the cache
        $this->cache[$cacheKey] = $value;

        // also register the references if given
        foreach ($references as $from => $to) {
            $this->references[$from] = $to;
        }
    }

    /**
     * Query whether or not a value for the passed cache key exists. If yes, the value
     * will be returned, else an exception will be thrown.
     *
     * @param string $cacheKey The cache key to return the value for
     *
     * @return mixed The value for the passed cache key
     * @throws \Exception Is thrown, if no value is available
     */
    public function fromCache($cacheKey)
    {

        // query whether or not a value for the cache key is available
        if (isset($this->cache[$resolvedCacheKey = $this->resolveReference($cacheKey)])) {
            return $this->cache[$resolvedCacheKey];
        }

        // throw an exception if not
        throw new \Exception(sprintf('Can\'t find cached value for key "%s"', $cacheKey));
    }

    /**
     * Flush the cache, or the value with the passed key.
     *
     * @param mixed|null $cacheKey The key of the value to flush
     *
     * @return void
     */
    public function flushCache($cacheKey = null)
    {

        // flush the complete cache, if NO cache key has been passed
        if ($cacheKey === null) {
            $this->references = $this->cache = array();
            return;
        }

        // only flush the value with the passed cache key
        if (isset($this->cache[$resolvedCacheKey = $this->resolveReference($cacheKey)])) {
            unset($this->cache[$resolvedCacheKey]);
        }
    }
}


<?php

/**
 * TechDivision\Import\Repositories\EavAttributeSetRepository
 *
 * 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 EAV attribute set 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 EavAttributeSetRepository extends AbstractRepository implements EavAttributeSetRepositoryInterface
{

    /**
     * The prepared statement to load a specific attribute set.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeSetStmt;

    /**
     * The prepared statement to load an attribute set for a specific entity type ID.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeSetsByEntityTypeIdStmt;

    /**
     * The prepared statement to load an attribute set with the passed entity type ID and attribute set name.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeSetByEntityTypeIdAndAttributeSetNameStmt;

    /**
     * The prepared statement to load an attribute set with the passed entity type code and attribute set name.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeSetByEntityTypeCodeAndAttributeSetNameStmt;

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

        // initialize the prepared statements
        $this->eavAttributeSetStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_SET));
        $this->eavAttributeSetsByEntityTypeIdStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_SETS_BY_ENTITY_TYPE_ID));
        $this->eavAttributeSetByEntityTypeIdAndAttributeSetNameStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_SET_BY_ENTITY_TYPE_ID_AND_ATTRIBUTE_SET_NAME));
        $this->eavAttributeSetByEntityTypeCodeAndAttributeSetNameStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_SET_BY_ENTITY_TYPE_CODE_AND_ATTRIBUTE_SET_NAME));
    }

    /**
     * Return's the EAV attribute set with the passed ID.
     *
     * @param integer $id The EAV attribute set ID
     *
     * @return array The attribute set
     */
    public function load($id)
    {

        // execute the prepared statement and return the EAV attribute set with the passed ID
        $this->eavAttributeSetStmt->execute(array($id));
        return $this->eavAttributeSetStmt->fetch(\PDO::FETCH_ASSOC);
    }

    /**
     * Return's the attribute sets for the passed entity type ID, whereas the array
     * is prepared with the attribute set names as keys.
     *
     * @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 findAllByEntityTypeId($entityTypeId)
    {

        // initialize the array for the attribute sets
        $eavAttributeSets = array();

        // load the attributes
        $this->eavAttributeSetsByEntityTypeIdStmt->execute(array($entityTypeId));

        // load the available EAV attribute sets
        $availableEavAttributeSets = $this->eavAttributeSetsByEntityTypeIdStmt->fetchAll(\PDO::FETCH_ASSOC);

        // prepare the array with the attribute set names as keys
        foreach ($availableEavAttributeSets as $eavAttributeSet) {
            $eavAttributeSets[$eavAttributeSet[MemberNames::ATTRIBUTE_SET_NAME]] = $eavAttributeSet;
        }

        // return the array with the attribute sets
        return $eavAttributeSets;
    }

    /**
     * Load's and return's the EAV attribute set with the passed entity type ID and attribute set name.
     *
     * @param string $entityTypeId     The entity type ID of the EAV attribute set to load
     * @param string $attributeSetName The attribute set name of the EAV attribute set to return
     *
     * @return array The EAV attribute set
     */
    public function findOneByEntityTypeIdAndAttributeSetName($entityTypeId, $attributeSetName)
    {

        // initialize the params
        $params = array(
            MemberNames::ENTITY_TYPE_ID     => $entityTypeId,
            MemberNames::ATTRIBUTE_SET_NAME => $attributeSetName
        );

        // load and return the attribute set
        $this->eavAttributeSetByEntityTypeIdAndAttributeSetNameStmt->execute($params);
        return $this->eavAttributeSetByEntityTypeIdAndAttributeSetNameStmt->fetch(\PDO::FETCH_ASSOC);
    }

    /**
     * Load's and return's the EAV attribute set with the passed entity type code and attribute set name.
     *
     * @param string $entityTypeCode   The entity type code of the EAV attribute set to load
     * @param string $attributeSetName The attribute set name of the EAV attribute set to return
     *
     * @return array The EAV attribute set
     */
    public function findOneByEntityTypeCodeAndAttributeSetName($entityTypeCode, $attributeSetName)
    {

        // initialize the params
        $params = array(
            MemberNames::ENTITY_TYPE_CODE   => $entityTypeCode,
            MemberNames::ATTRIBUTE_SET_NAME => $attributeSetName
        );

        // load and return the attribute set
        $this->eavAttributeSetByEntityTypeCodeAndAttributeSetNameStmt->execute($params);
        return $this->eavAttributeSetByEntityTypeCodeAndAttributeSetNameStmt->fetch(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Adapter\CsvExportAdapterFactory
 *
 * 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\Adapter;

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

/**
 * Factory for all CSV export adapter implementations.
 *
 * @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 CsvExportAdapterFactory implements ExportAdapterFactoryInterface
{

    /**
     * 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 export adapter for the subject with the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subjectConfiguration The subject configuration
     *
     * @return \TechDivision\Import\Adapter\ExportAdapterInterface The export adapter instance
     */
    public function createExportAdapter(SubjectConfigurationInterface $subjectConfiguration)
    {

        // load the export adapter configuration
        $exportAdapterConfiguration = $subjectConfiguration->getExportAdapter();

        // load the serializer factory instance
        $serializerFactory = $this->container->get($exportAdapterConfiguration->getSerializer()->getId());

        // create the instance and pass the export adapter configuration instance
        $exportAdapter = $this->container->get(DependencyInjectionKeys::IMPORT_ADAPTER_EXPORT_CSV);
        $exportAdapter->init($exportAdapterConfiguration, $serializerFactory);

        // return the initialized export adapter instance
        return $exportAdapter;
    }
}


<?php

/**
 * TechDivision\Import\Observers\GenericCompositeObserver
 *
 * 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\Observers;

use TechDivision\Import\Subjects\SubjectInterface;

/**
 * A generic observer implementation that implements the composit pattern to bundle
 * the necessary observers of a special use case to simplify configuration.
 *
 * @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 GenericCompositeObserver implements ObserverInterface
{

    /**
     * The composite's subject instance.
     *
     * @var \TechDivision\Import\Subjects\SubjectInterface
     */
    protected $subject;

    /**
     * The actual row that will be processed by the composite's observers.
     *
     * @var array
     */
    protected $row = array();

    /**
     * The array with the composite's observers.
     *
     * @var \TechDivision\Import\Observers\ObserverInterface[]
     */
    protected $observers = array();

    /**
     * Will be invoked by the action on the events the listener has been registered for.
     *
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance
     *
     * @return array The modified row
     */
    public function handle(SubjectInterface $subject)
    {

        // initialize the subject
        $this->setSubject($subject);

        // load the observers
        $observers = $this->getObservers();

        // process the observers
        foreach ($observers as $observer) {
            // query whether or not we have to skip the row
            if ($subject->rowHasToBeSkipped()) {
                // log a debug message with the actual line nr/file information
                $subject->getSystemLogger()->warning(
                    $subject->appendExceptionSuffix(
                        sprintf(
                            'Skip processing operation "%s" after observer "%s"',
                            $subject->getOperationName(),
                            $subject->getConfiguration()->getId()
                        )
                    )
                );

                // skip the row
                break;
            }

            // if not, set the subject and process the observer
            $subject->setRow($observer->handle($subject));
        }

        // returns the subject's row
        return $subject->getRow();
    }

    /**
     * Return's the observer's subject instance.
     *
     * @return \TechDivision\Import\Subjects\SubjectInterface The observer's subject instance
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * Adds the passed observer to the composites array with observers.
     *
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The observer to add
     *
     * @return void
     */
    public function addObserver(ObserverInterface $observer)
    {
        $this->observers[] = $observer;
    }

    /**
     * Set's the obeserver's subject instance to initialize the observer with.
     *
     * @param \TechDivision\Import\Subjects\SubjectInterface $subject The observer's subject
     *
     * @return void
     */
    protected function setSubject(SubjectInterface $subject)
    {
        $this->subject = $subject;
    }

    /**
     * Returns the array with the composite's observers.
     *
     * @return \TechDivision\Import\Observers\ObserverInterface[]
     */
    protected function getObservers()
    {
        return $this->observers;
    }
}


<?php

/**
 * TechDivision\Import\Connection\PDOConnectionWrapper
 *
 * 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\Connection;

/**
 * A wrapper for a \PDO connection instance.
 *
 * @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 PDOConnectionWrapper implements ConnectionInterface
{

    /**
     * The \PDO connection that has to be wrapped.
     *
     * @var \PDO
     */
    protected $connection;

    /**
     * Initializes the wrapper with the passed PDO connection.
     *
     * @param \PDO $connection The PDO connection to wrap
     */
    public function __construct(\PDO $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Turns off autocommit mode. While autocommit mode is turned off, changes made to the database via
     * the instance are not committed until you end the transaction by calling commit().
     *
     * @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 beginTransaction() starts a new transaction.
     *
     * @return void
     * @throws \PDOException Throws a PDOException if there is no active transaction.
     * @link http://php.net/manual/en/pdo.commit.php
     */
    public function commit()
    {
        $this->connection->commit();
    }

    /**
     * Rolls back the current transaction, as initiated by beginTransaction().
     *
     * @return boolean Returns TRUE on success or FALSE on failure
     * @throws \PDOException Throws a PDOException if there is no active transaction
     */
    public function rollBack()
    {
        return $this->connection->rollBack();
    }

    /**
     * Fetch the SQLSTATE associated with  the last operation on the database handle.
     *
     * @return mixed The error code of the last operation of the database handle
     * @link http://php.net/manual/en/pdo.errorcode.php
     */
    public function errorCode()
    {
        return $this->connection->errorCode();
    }

    /**
     * Fetch extended error information associated with the last operation on the database handle.
     *
     * @return array Returns an array of error information about the last operation performed by this database handle
     * @link http://php.net/manual/en/pdo.errorinfo.php
     */
    public function errorInfo()
    {
        return $this->connection->errorInfo();
    }

    /**
     * Executes an SQL statement, returning a result set as a PDOStatement object.
     *
     * @param string $statement The SQL statement to prepare and execute
     *
     * @return \PDOStatement|boolean Returns a \PDOStatement object, or FALSE on failure
     * @link http://php.net/manual/en/pdo.query.php
     */
    public function query($statement)
    {
        return call_user_func_array(array($this->connection, 'query'), func_get_args());
    }

    /**
     * Prepares a statement for execution and returns a statement object.
     *
     * @param string $statement     This must be a valid SQL statement template for the target database server
     * @param array  $driverOptions This array holds one or more key=>value pairs to set attribute values for the \PDOStatement object that this method returns
     *
     * @return \PDOStatement If the database server successfully prepares the statement, this method returns a \PDOStatement object
     * @throws \PDOException Throws a PDOException if the database server cannot successfully prepare the statement
     * @link http://php.net/manual/en/pdo.prepare.php
     */
    public function prepare($statement, array $driverOptions = array())
    {
        return $this->connection->prepare($statement, $driverOptions);
    }

    /**
     * Execute an SQL statement and return the number of affected rows.
     *
     * @param string $statement The SQL statement to prepare and execute
     *
     * @return integer The number of affected rows
     * @link http://php.net/manual/en/pdo.exec.php
     */
    public function exec($statement)
    {
        return $this->connection->exec($statement);
    }

    /**
     * Returns the ID of the last inserted row or sequence value.
     *
     * @param string $name Name of the sequence object from which the ID should be returned
     *
     * @return string A string representing the row ID of the last row that was inserted into the database
     * @link http://php.net/manual/en/pdo.lastinsertid.php
     */
    public function lastInsertId($name = null)
    {
        return $this->connection->lastInsertId($name);
    }

    /**
     * Quotes a string for use in a query.
     *
     * @param string  $string        The string to be quoted
     * @param integer $parameterType Provides a data type hint for drivers that have alternate quoting styles
     *
     * @return string|boolean Returns a quoted string that is theoretically safe to pass into an SQL statement or FALSE if the driver does not support quoting in this way
     * @link http://php.net/manual/en/pdo.quote.php
     */
    public function quote($string, $parameterType = \PDO::PARAM_STR)
    {
        return $this->connection->quote($string, $parameterType);
    }
}


<?php

/**
 * TechDivision\Import\Listeners\InitializeRegistryListener
 *
 * 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\Utils\RegistryKeys;
use TechDivision\Import\ApplicationInterface;
use TechDivision\Import\ConfigurationInterface;
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 InitializeRegistryListener extends AbstractListener
{

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

    /**
     * The configuration instance.
     *
     * @var \TechDivision\Import\ConfigurationInterface
     */
    protected $configuration;

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

    /**
     * Handle the event.
     *
     * @param \League\Event\EventInterface              $event       The event that triggered the listener
     * @param \TechDivision\Import\ApplicationInterface $application The application instance
     *
     * @return void
     */
    public function handle(EventInterface $event, ApplicationInterface $application = null)
    {

        // initialize the status
        $status = array(
            RegistryKeys::STATUS                => 1,
            RegistryKeys::BUNCHES               => 0,
            RegistryKeys::SOURCE_DIRECTORY      => $this->getConfiguration()->getSourceDir(),
            RegistryKeys::MISSING_OPTION_VALUES => array()
        );

        // append it to the registry
        $this->getRegistryProcessor()->setAttribute($application->getSerial(), $status);
    }

    /**
     * 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;
    }

    /**
     * Returns the configuration instance.
     *
     * @return \TechDivision\Import\ConfigurationInterface The configuration instance
     */
    protected function getConfiguration()
    {
        return $this->configuration;
    }
}


<?php

/**
 * TechDivision\Import\Repositories\UrlRewriteRepository
 *
 * 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 URL rewrite 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 UrlRewriteRepository extends AbstractRepository implements UrlRewriteRepositoryInterface
{

    /**
     * The prepared statement to load the existing URL rewrites by their entity type and ID.
     *
     * @var \PDOStatement
     */
    protected $urlRewritesByEntityTypeAndEntityIdStmt;

    /**
     * The prepared statement to load the existing URL rewrites by their entity type, entity and store ID.
     *
     * @var \PDOStatement
     */
    protected $urlRewritesByEntityTypeAndEntityIdAndStoreIdStmt;

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

        // initialize the prepared statements
        $this->urlRewritesByEntityTypeAndEntityIdStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID));
        $this->urlRewritesByEntityTypeAndEntityIdAndStoreIdStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::URL_REWRITES_BY_ENTITY_TYPE_AND_ENTITY_ID_AND_STORE_ID));
    }

    /**
     * Return's an array with the URL rewrites for the passed entity type and ID.
     *
     * @param string  $entityType The entity type to load the URL rewrites for
     * @param integer $entityId   The entity ID to load the URL rewrites for
     *
     * @return array The URL rewrites
     */
    public function findAllByEntityTypeAndEntityId($entityType, $entityId)
    {

        // initialize the params
        $params = array(
            MemberNames::ENTITY_TYPE => $entityType,
            MemberNames::ENTITY_ID   => $entityId
        );

        // load and return the URL rewrites
        $this->urlRewritesByEntityTypeAndEntityIdStmt->execute($params);
        return $this->urlRewritesByEntityTypeAndEntityIdStmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    /**
     * Return's an array with the URL rewrites for the passed entity type, entity and store ID.
     *
     * @param string  $entityType The entity type to load the URL rewrites for
     * @param integer $entityId   The entity ID to load the URL rewrites for
     * @param integer $storeId    The store ID to load the URL rewrites for
     *
     * @return array The URL rewrites
     */
    public function findAllByEntityTypeAndEntityIdAndStoreId($entityType, $entityId, $storeId)
    {

        // initialize the params
        $params = array(
            MemberNames::ENTITY_TYPE => $entityType,
            MemberNames::ENTITY_ID   => $entityId,
            MemberNames::STORE_ID    => $storeId
        );

        // load and return the URL rewrites
        $this->urlRewritesByEntityTypeAndEntityIdAndStoreIdStmt->execute($params);
        return $this->urlRewritesByEntityTypeAndEntityIdAndStoreIdStmt->fetchAll(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Adapter\Goodby\Interpreter
 *
 * 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\Adapter\Goodby;

use Goodby\CSV\Import\Protocol\InterpreterInterface;
use Goodby\CSV\Import\Standard\Exception\StrictViolationException;

/**
 * Custom interpreter implementation which allows to reset observers and row consistency on every import.
 *
 * @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 Interpreter implements InterpreterInterface
{

    /**
     * The array with the observers.
     *
     * @var array
     */
    private $observers = array();

    /**
     * The number of rows found in the header.
     *
     * @var int
     */
    private $rowConsistency = null;

    /**
     * Query whether or not strict mode is activated or not.
     *
     * @var boolean
     */
    private $strict = true;

    /**
     * Interpret the passed line.
     *
     * @param array $row The row that has to be processed
     *
     * @return void
     * @throws \Goodby\CSV\Import\Protocol\Exception\InvalidLexicalException Is thrown, if the passed line is not an array
     */
    public function interpret($row)
    {

        // check the row consistency
        $this->checkRowConsistency($row);

        // invoke the observers
        $this->notify($row);
    }

    /**
     * Disable strict mode.
     *
     * @return void
     */
    public function unstrict()
    {
        $this->strict = false;
    }

    /**
     * Add's the passed observer to the interpreter.
     *
     * @param callable $observer The observer to add
     *
     * @return void
     */
    public function addObserver(callable $observer)
    {
        $this->observers[] = $observer;
    }

    /**
     * Reset the interpreter.
     *
     * @return void
     */
    public function reset()
    {
        $this->observers = array();
        $this->rowConsistency = null;
    }

    /**
     * Query whether or not strict mode has been activated.
     *
     * @return boolean TRUE if the strict mode has NOT been activated, else FALSE
     */
    private function isNotStrict()
    {
        return $this->strict === false;
    }

    /**
     * Notify the observers.
     *
     * @param array $row The row that has to be processed
     *
     * @return void
     */
    private function notify(array $row)
    {

        // make the observers local
        $observers = $this->observers;

        // invoke the observers on the passed line
        foreach ($observers as $observer) {
            $this->delegate($observer, $row);
        }
    }

    /**
     * Delegate the row to the observer.
     *
     * @param callable $observer The observer that has to be invoked
     * @param array    $row      The row that has to be processed
     *
     * @return void
     */
    private function delegate(callable $observer, array $row)
    {
        call_user_func($observer, $row);
    }

    /**
     * Check if the column count is consistent with comparing other rows.
     *
     * @param array $row The row that has to be processed
     *
     * @return void
     * @throws \Goodby\CSV\Export\Standard\Exception\StrictViolationException Is thrown, if row consistency check fails
     */
    private function checkRowConsistency(array $row)
    {

        // query whether or not strict mode is enabled
        if ($this->isNotStrict()) {
            return;
        }

        // count the number of columns
        $current = count($row);

        // if the row consistency has not been set, set it
        if ($this->rowConsistency === null) {
            $this->rowConsistency = $current;
        }

        // check row consistency
        if ($current !== $this->rowConsistency) {
            throw new StrictViolationException(sprintf('Column size should be %u, but %u columns given', $this->rowConsistency, $current));
        }

        // set the new row consistency
        $this->rowConsistency = $current;
    }
}


<?php

/**
 * TechDivision\Import\RowTrait
 *
 * 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;

/**
 * A trait that provides row handling.
 *
 * @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 RowTrait
{

    /**
     * The actual row, that has to be processed.
     *
     * @var array
     */
    protected $row = array();

    /**
     * The flag that stop's overserver execution on the actual row.
     *
     * @var boolean
     */
    protected $skipRow = false;

    /**
     * Stop's observer execution on the actual row.
     *
     * @param boolean $skip The flag to skip row exection or not
     *
     * @return void
     */
    public function skipRow($skip = true)
    {
        $this->skipRow = $skip;
    }

    /**
     * Query's whether or not the observer execution for the given row has to be skipped.
     *
     * @return boolean TRUE if the observer execution has to be skipped, else FALSE
     */
    public function rowHasToBeSkipped()
    {
        return $this->skipRow;
    }

    /**
     * Set's the actual row, that has to be processed.
     *
     * @param array $row The row
     *
     * @return void
     */
    public function setRow(array $row)
    {
        $this->row = $row;
    }

    /**
     * Return's the actual row.
     *
     * @return array The actual row
     */
    public function getRow()
    {
        return $this->row;
    }

    /**
     * Query whether or not a value for the column with the passed name exists.
     *
     * @param string $name The column name to query for a valid value
     *
     * @return boolean TRUE if the value is set, else FALSE
     */
    public function hasValue($name)
    {

        // query whether or not the header is available
        if ($this->hasHeader($name)) {
            // load the key for the row
            $headerValue = $this->getHeader($name);

            // query whether the rows column has a vaild value
            return (isset($this->row[$headerValue]) && $this->row[$headerValue] !== '');
        }

        // return FALSE if not
        return false;
    }

    /**
     * Set the value in the passed column name.
     *
     * @param string $name  The column name to set the value for
     * @param mixed  $value The value to set
     *
     * @return void
     */
    protected function setValue($name, $value)
    {
        $this->row[$this->getHeader($name)] = $value;
    }

    /**
     * Resolve's the value with the passed colum name from the actual row. If a callback will
     * be passed, the callback will be invoked with the found value as parameter. If
     * the value is NULL or empty, the default value will be returned.
     *
     * @param string        $name     The name of the column to return the value for
     * @param mixed|null    $default  The default value, that has to be returned, if the row's value is empty
     * @param callable|null $callback The callback that has to be invoked on the value, e. g. to format it
     *
     * @return mixed|null The, almost formatted, value
     */
    public function getValue($name, $default = null, callable $callback = null)
    {

        // initialize the value
        $value = null;

        // query whether or not the header is available
        if ($this->hasHeader($name)) {
            // load the header value
            $headerValue = $this->getHeader($name);
            // query wheter or not, the value with the requested key is available
            if ((isset($this->row[$headerValue]) && $this->row[$headerValue] !== '')) {
                $value = $this->row[$headerValue];
            }
        }

        // query whether or not, a callback has been passed
        if ($value != null && is_callable($callback)) {
            $value = call_user_func($callback, $value);
        }

        // query whether or not
        if ($value == null && $default !== null) {
            $value = $default;
        }

        // return the value
        return $value;
    }
}


<?php

/**
 * TechDivision\Import\Subjects\SubjectFactory
 *
 * 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 Doctrine\Common\Collections\Collection;
use Symfony\Component\DependencyInjection\ContainerInterface;
use TechDivision\Import\SystemLoggerTrait;
use TechDivision\Import\Adapter\ImportAdapterInterface;
use TechDivision\Import\Adapter\ExportAdapterInterface;
use TechDivision\Import\Adapter\ImportAdapterFactoryInterface;
use TechDivision\Import\Adapter\ExportAdapterFactoryInterface;
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
use TechDivision\Import\Subjects\I18n\DateConverterFactoryInterface;
use TechDivision\Import\Subjects\I18n\NumberConverterFactoryInterface;

/**
 * A generic subject factory 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 SubjectFactory implements SubjectFactoryInterface
{

    /**
     * The trait that provides basic filesystem handling functionality.
     *
     * @var TechDivision\Import\SystemLoggerTrait
     */
    use SystemLoggerTrait;

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

    /**
     * The number converter factory instance.
     *
     * @var \TechDivision\Import\Subjects\I18n\NumberConverterFactoryInterface
     */
    protected $numberConverterFactory;

    /**
     * The date converter factory instance.
     *
     * @var \TechDivision\Import\Subjects\I18n\DateConverterFactoryInterface
     */
    protected $dateConverterFactory;

    /**
     * Initialize the factory with the DI container instance.
     *
     * @param \Symfony\Component\DependencyInjection\ContainerInterface          $container              The DI container instance
     * @param \Doctrine\Common\Collections\Collection                            $systemLoggers          The collection with the system loggers instances
     * @param \TechDivision\Import\Subjects\I18n\NumberConverterFactoryInterface $numberConverterFactory The number converter factory instance
     * @param \TechDivision\Import\Subjects\I18n\DateConverterFactoryInterface   $dateConverterFactory   The date converter factory instance
     */
    public function __construct(
        ContainerInterface $container,
        Collection $systemLoggers,
        NumberConverterFactoryInterface $numberConverterFactory,
        DateConverterFactoryInterface $dateConverterFactory
    ) {
        $this->container = $container;
        $this->systemLoggers = $systemLoggers;
        $this->numberConverterFactory = $numberConverterFactory;
        $this->dateConverterFactory = $dateConverterFactory;
    }

    /**
     * Factory method to create new subject instance.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subjectConfiguration The subject configuration
     *
     * @return \TechDivision\Import\Subjects\SubjectInterface The subject instance
     */
    public function createSubject(SubjectConfigurationInterface $subjectConfiguration)
    {

        // load the subject instance from the DI container and set the subject configuration
        $subjectInstance = $this->container->get($subjectConfiguration->getId());
        $subjectInstance->setConfiguration($subjectConfiguration);

        // load the import adapter instance from the DI container and set it on the subject instance
        $importAdapter = $this->container->get($subjectConfiguration->getImportAdapter()->getId());

        // query whether or not we've found a factory or the instance itself
        if ($importAdapter instanceof ImportAdapterInterface) {
            $subjectInstance->setImportAdapter($importAdapter);
            // log a warning, that this is deprecated
            $this->getSystemLogger()->warning(
                sprintf(
                    'Direct injection of import adapter with DI ID "%s" is deprecated since version 3.0.0, please use factory instead',
                    $subjectConfiguration->getImportAdapter()->getId()
                )
            );
        } elseif ($importAdapter instanceof ImportAdapterFactoryInterface) {
            $subjectInstance->setImportAdapter($importAdapter->createImportAdapter($subjectConfiguration));
        } else {
            throw new \Exception(
                sprintf(
                    'Expected either an instance of ImportAdapterInterface or ImportAdapterFactoryInterface for DI ID "%s"',
                    $subjectConfiguration->getImportAdapter()->getId()
                )
            );
        }

        // query whether or not we've a subject instance that implements the exportable subject interface
        if ($subjectInstance instanceof ExportableSubjectInterface) {
            // load the export adapter instance from the DI container and set it on the subject instance
            $exportAdapter = $this->container->get($subjectConfiguration->getExportAdapter()->getId());

            // query whether or not we've found a factory or the instance itself
            if ($exportAdapter instanceof ExportAdapterInterface) {
                // inject the export adapter into the subject
                $subjectInstance->setExportAdapter($exportAdapter);
                // log a warning, that this is deprecated
                $this->getSystemLogger()->warning(
                    sprintf(
                        'Direct injection of export adapter with DI ID "%s" is deprecated since version 3.0.0, please use factory instead',
                        $subjectConfiguration->getExportAdapter()->getId()
                    )
                );
            } elseif ($exportAdapter instanceof ExportAdapterFactoryInterface) {
                $subjectInstance->setExportAdapter($exportAdapter->createExportAdapter($subjectConfiguration));
            } else {
                throw new \Exception(
                    sprintf(
                        'Expected either an instance of ExportAdapterInterface or ExportAdapterFactoryInterface for DI ID "%s"',
                        $subjectConfiguration->getExportAdapter()->getId()
                    )
                );
            }
        }

        // load te number converter instance from the DI container and set it on the subject instance
        if ($subjectInstance instanceof NumberConverterSubjectInterface) {
            $subjectInstance->setNumberConverter($this->numberConverterFactory->createNumberConverter($subjectConfiguration));
        }

        // load te date converter instance from the DI container and set it on the subject instance
        if ($subjectInstance instanceof DateConverterSubjectInterface) {
            $subjectInstance->setDateConverter($this->dateConverterFactory->createDateConverter($subjectConfiguration));
        }

        // load the filesystem adapter instance from the DI container and set it on the subject instance
        if ($subjectInstance instanceof FilesystemSubjectInterface) {
            $filesystemAdapterFactory = $this->container->get($subjectConfiguration->getFilesystemAdapter()->getId());
            $subjectInstance->setFilesystemAdapter($filesystemAdapterFactory->createFilesystemAdapter($subjectConfiguration));
        }

        // return the initialized subject instance
        return $subjectInstance;
    }
}


<?php

/**
 * TechDivision\Import\Serializers\AdditionalAttributeCsvSerializerFactory
 *
 * 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\Serializers;

use Symfony\Component\DependencyInjection\ContainerInterface;
use TechDivision\Import\Configuration\CsvConfigurationInterface;
use TechDivision\Import\Utils\DependencyInjectionKeys;

/**
 * The factory implementation for CSV value serializer 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 AdditionalAttributeCsvSerializerFactory implements ConfigurationAwareSerializerFactoryInterface
{

    /**
     * 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 serializer instance.
     *
     * @param \TechDivision\Import\Configuration\CsvConfigurationInterface $configuration The CSV configuration
     *
     * @return \TechDivision\Import\Serializers\ConfigurationAwareSerializerInterface The serializer instance
     */
    public function createSerializer(CsvConfigurationInterface $configuration)
    {

        // load the serializer instance from the container and pass the configuration
        /** @var \TechDivision\Import\Serializers\ConfigurationAwareSerializerInterface $serializer */
        $serializer = $this->container->get(DependencyInjectionKeys::IMPORT_SERIALIZER_CSV_ADDITIONAL_ATTRIBUTE);
        $serializer->init($configuration);

        // return the serializer instance
        return $serializer;
    }
}