<?php

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

use TechDivision\Import\Observers\ObserverInterface;

/**
 * An abstract callback 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
 */
abstract class AbstractCallback implements CallbackInterface
{

    /**
     * The observer's subject instance.
     *
     * @var \TechDivision\Import\Observers\ObserverInterface
     */
    protected $observer;

    /**
     * Set's the callback's observer instance.
     *
     * @param \TechDivision\Import\Observers\ObserverInterface $observer The callback's observer
     *
     * @return void
     */
    protected function setObserver(ObserverInterface $observer)
    {
        $this->observer = $observer;
    }

    /**
     * Return's the callback's observer instance.
     *
     * @return \TechDivision\Import\Observers\ObserverInterface The callback's observer
     */
    protected function getObserver()
    {
        return $this->observer;
    }

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

    /**
     * Return's the system logger.
     *
     * @return \Psr\Log\LoggerInterface The system logger instance
     */
    protected function getSystemLogger()
    {
        return $this->getSubject()->getSystemLogger();
    }

    /**
     * Append's the exception suffix containing filename and line number to the
     * passed message. If no message has been passed, only the suffix will be
     * returned.
     *
     * @param string|null $message    The message to append the exception suffix to
     * @param string|null $filename   The filename used to create the suffix
     * @param string|null $lineNumber The line number used to create the suffx
     *
     * @return string The message with the appended exception suffix
     */
    protected function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
    {
        return $this->getSubject()-> appendExceptionSuffix($message, $filename, $lineNumber);
    }

    /**
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
     * line number and column name and use it for a detailed exception message.
     *
     * @param string     $columnName The column name that should be resolved
     * @param \Exception $parent     The exception we want to wrap
     * @param string     $className  The class name of the exception type we want to wrap the parent one
     *
     * @return \Exception the wrapped exception
     */
    protected function wrapException(
        $columnName,
        \Exception $parent = null,
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
    ) {
        return $this->getSubject()->wrapException($columnName, $parent, $className);
    }

    /**
     * Queries whether or not debug mode is enabled or not, default is TRUE.
     *
     * @return boolean TRUE if debug mode is enabled, else FALSE
     */
    protected function isDebugMode()
    {
        return $this->getSubject()->isDebugMode();
    }

    /**
     * Raises the value for the counter with the passed key by one.
     *
     * @param mixed $counterName The name of the counter to raise
     *
     * @return integer The counter's new value
     */
    protected function raiseCounter($counterName)
    {
        return $this->getSubject()->raiseCounter($counterName);
    }

    /**
     * Merge the passed array into the status of the actual import.
     *
     * @param array $status The status information to be merged
     *
     * @return void
     */
    protected function mergeAttributesRecursive(array $status)
    {
        $this->getSubject()->mergeAttributesRecursive($status);
    }

    /**
     * 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
     */
    protected function getValue($name, $default = null, callable $callback = null)
    {
        return $this->getSubject()->getValue($name, $default, $callback);
    }

    /**
     * Return's the store ID of the store with the passed store view code
     *
     * @param string $storeViewCode The store view code to return the store ID for
     *
     * @return integer The ID of the store with the passed ID
     * @throws \Exception Is thrown, if the store with the actual code is not available
     */
    protected function getStoreId($storeViewCode)
    {
        return $this->getSubject()->getStoreId($storeViewCode);
    }

    /**
     * Return's the store ID of the actual row, or of the default store
     * if no store view code is set in the CSV file.
     *
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
     *
     * @return integer The ID of the actual store
     * @throws \Exception Is thrown, if the store with the actual code is not available
     */
    protected function getRowStoreId($default = null)
    {
        return $this->getSubject()->getRowStoreId($default);
    }

    /**
     * Return's the unique identifier of the actual row, e. g. a products SKU.
     *
     * @return mixed The row's unique identifier
     */
    abstract protected function getUniqueIdentifier();
}


<?php

/**
 * TechDivision\Import\Adapter\CsvImportAdapter
 *
 * 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 Goodby\CSV\Import\Protocol\LexerInterface;
use Goodby\CSV\Import\Protocol\InterpreterInterface;
use TechDivision\Import\Configuration\Subject\ImportAdapterConfigurationInterface;
use TechDivision\Import\Serializers\ConfigurationAwareSerializerFactoryInterface;

/**
 * CSV import adapter 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 CsvImportAdapter implements ImportAdapterInterface, SerializerAwareAdapterInterface
{

    /**
     * The trait that provides serializer functionality.
     *
     * @var \TechDivision\Import\Adapter\SerializerTrait
     */
    use SerializerTrait;

    /**
     * The lexer instance.
     *
     * @var \Goodby\CSV\Import\Protocol\LexerInterface
     */
    protected $lexer;

    /**
     * The interpreter instance.
     *
     * @var \Goodby\CSV\Import\Protocol\InterpreterInterface
     */
    protected $interpreter;

    /**
     * The adapter's serializer instance.
     *
     * @var \TechDivision\Import\Serializers\SerializerInterface
     */
    protected $serializer;

    /**
     * Initialize the adapter with the configuration.
     *
     * @param \Goodby\CSV\Import\Protocol\LexerInterface       $lexer       The lexer instance
     * @param \Goodby\CSV\Import\Protocol\InterpreterInterface $interpreter The interpreter instance
     */
    public function __construct(LexerInterface $lexer, InterpreterInterface $interpreter)
    {
        $this->lexer = $lexer;
        $this->interpreter = $interpreter;
    }

    /**
     * Overwrites the default CSV configuration values with the one from the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\Subject\ImportAdapterConfigurationInterface $importAdapterConfiguration The configuration to use the values from
     * @param \TechDivision\Import\Serializers\ConfigurationAwareSerializerFactoryInterface  $serializerFactory          The serializer factory instance
     *
     * @return void
     */
    public function init(
        ImportAdapterConfigurationInterface $importAdapterConfiguration,
        ConfigurationAwareSerializerFactoryInterface $serializerFactory
    ) {

        // load the lexer configuration and overwrite the values
        /** @var \Goodby\CSV\Import\Standard\LexerConfig $config */
        $config = $this->lexer->getConfig();

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

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

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

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

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

        // load the serializer instance from the DI container and set it on the subject instance
        $this->setSerializer($serializerFactory->createSerializer($importAdapterConfiguration));
    }

    /**
     * Imports the content of the CSV file with the passed filename.
     *
     * @param callable $callback The callback that processes the row
     * @param string   $filename The filename to process
     *
     * @return void
     */
    public function import(callable $callback, $filename)
    {

        // reset and initialize the interpreter
        $this->interpreter->reset();
        $this->interpreter->addObserver($callback);

        // parse the CSV file to be imported
        $this->lexer->parse($filename, $this->interpreter);
    }
}


<?php

/**
 * TechDivision\Import\Adapter\LeagueFilesystemAdapter
 *
 * 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 League\Flysystem\FilesystemInterface;

/**
 * Adapter for the league filesystem 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 LeagueFilesystemAdapter implements FilesystemAdapterInterface
{

    /**
     * The league filesystem implementation.
     *
     * @var \League\Flysystem\FilesystemInterface
     */
    protected $filesystem;

    /**
     * This is a utility class, so protect it against direct
     * instantiation.
     *
     * @param \League\Flysystem\FilesystemInterface $filesystem The league filesystem instance
     */
    public function __construct(FilesystemInterface $filesystem)
    {
        $this->filesystem = $filesystem;
    }

    /**
     * Creates a new directroy.
     *
     * @param string  $pathname The directory path
     * @param integer $mode     The mode is 0700 by default, which means the widest possible access
     *
     * @return boolean TRUE on success, else FALSE
     */
    public function mkdir($pathname, $mode = 0755)
    {
        return $this->filesystem->createDir($pathname, array('visibility' => $mode === 0755 ? 'public' : 'private'));
    }

    /**
     * Query whether or not the passed filename exists.
     *
     * @param string $filename The filename to query
     *
     * @return boolean TRUE if the passed filename exists, else FALSE
     */
    public function isFile($filename)
    {
        return $this->filesystem->has($filename);
    }

    /**
     * Tells whether the filename is a directory.
     *
     * @param string $filename Path to the file
     *
     * @return TRUE if the filename exists and is a directory, else FALSE
     */
    public function isDir($filename)
    {
        return $this->filesystem->has($filename);
    }

    /**
     * Creates an empty file with the passed filename.
     *
     * @param string $filename The name of the file to create
     *
     * @return boolean TRUE if the file can be created, else FALSE
     */
    public function touch($filename)
    {
        return $this->filesystem->put($filename, '');
    }

    /**
     * Renames a file or directory.
     *
     * @param string $oldname The old name
     * @param string $newname The new name
     *
     * @return boolean TRUE on success, else FALSE
     */
    public function rename($oldname, $newname)
    {
        return $this->filesystem->rename($oldname, $newname);
    }

    /**
     * Writes the passed data to file with the passed name.
     *
     * @param string $filename The name of the file to write the data to
     * @param string $data     The data to write to the file
     *
     * @return number The number of bytes written to the file
     */
    public function write($filename, $data)
    {
        return $this->filesystem->put($filename, $data);
    }

    /**
     * Delete the file with the passed name.
     *
     * @param string $filename The name of the file to be deleted
     *
     * @return boolean TRUE on success, else FALSE
     */
    public function delete($filename)
    {
        return $this->filesystem->delete($filename);
    }

    /**
     * Copy's a file from source to destination.
     *
     * @param string $src  The source file
     * @param string $dest The destination file
     *
     * @return boolean TRUE on success, else FALSE
     */
    public function copy($src, $dest)
    {
        return $this->filesystem->copy($src, $dest);
    }

    /**
     * List the filenames of a directory.
     *
     * @param string  $directory The directory to list
     * @param boolean $recursive Whether to list recursively
     *
     * @return array A list of filenames
     */
    public function listContents($directory = '', $recursive = false)
    {

        // initialize the array for the filenames
        $files = array();

        // load the filenames of the passed directory
        $contents = $this->filesystem->listContents($directory, $recursive);

        // prepare the array with the file names
        foreach ($contents as $key => $file) {
            $files[$key] = $file['path'];
        }

        // return the filenames
        return $files;
    }
}


<?php

/**
 * TechDivision\Import\Adapter\Goodby\Lexer
 *
 * 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\LexerInterface;
use Goodby\CSV\Import\Protocol\InterpreterInterface;
use Goodby\CSV\Import\Standard\LexerConfig;
use Goodby\CSV\Import\Standard\StreamFilter\ConvertMbstringEncoding;

/**
 * Custom exporter implementation which resets 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 Lexer implements LexerInterface
{

    /**
     * The exporter configuration.
     *
     * @var \Goodby\CSV\Import\Standard\LexerConfig
     */
    private $config;

    /**
     * Initialize the instance with the passed configuration.
     *
     * @param \Goodby\CSV\Import\Standard\LexerConfig $config The lexer configuration
     */
    public function __construct(LexerConfig $config = null)
    {

        // query whether or not a configuration has been passed
        if ($config instanceof LexerConfig) {
            $this->config = $config;
        } else {
            $this->config = new LexerConfig();
        }

        // register the encoding filter
        ConvertMbstringEncoding::register();
    }

    /**
     * Returns the lexer configuration.
     *
     * @return \Goodby\CSV\Import\Standard\LexerConfig The configuration instance
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * Parse the passed CSV file.
     *
     * @param string                                           $filename    The filename to parse
     * @param \Goodby\CSV\Import\Protocol\InterpreterInterface $interpreter The interpreter instance
     *
     * @return void
     */
    public function parse($filename, InterpreterInterface $interpreter)
    {

        // for mac's office excel csv
        ini_set('auto_detect_line_endings', true);

        // initialize the configuration
        $delimiter      = $this->config->getDelimiter();
        $enclosure      = $this->config->getEnclosure();
        $escape         = $this->config->getEscape();
        $fromCharset    = $this->config->getFromCharset();
        $toCharset      = $this->config->getToCharset();
        $flags          = $this->config->getFlags();
        $ignoreHeader   = $this->config->getIgnoreHeaderLine();

        // query whether or not the charset has to be converted
        if ($fromCharset === null) {
            $url = $filename;
        } else {
            $url = ConvertMbstringEncoding::getFilterURL($filename, $fromCharset, $toCharset);
        }

        // initialize the CSV file object
        $csv = new \SplFileObject($url);
        $csv->setCsvControl($delimiter, $enclosure, $escape);
        $csv->setFlags($flags);

        // backup current locale
        $originalLocale = setlocale(LC_ALL, '0');
        setlocale(LC_ALL, 'en_US.UTF-8');

        // process each line of the CSV file
        foreach ($csv as $lineNumber => $line) {
            if ($ignoreHeader && $lineNumber == 0 || (count($line) === 1 && trim($line[0]) === '')) {
                continue;
            }
            $interpreter->interpret($line);
        }

        // reset locale
        $localeArray = array();
        parse_str(str_replace(';', '&', $originalLocale), $localeArray);
        setlocale(LC_ALL, $localeArray);
    }
}


<?php

/**
 * TechDivision\Import\Repositories\EavAttributeGroupRepository
 *
 * 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 group 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 EavAttributeGroupRepository extends AbstractRepository implements EavAttributeGroupRepositoryInterface
{

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

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

    /**
     * The prepared statement to load an attribute group for the given entity type code, attribute set + attribute group name.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeGroupByEntityTypeCodeAndAttributeSetNameAndAttributeGroupNameStmt;

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

        // initialize the prepared statements
        $this->eavAttributeGroupStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_GROUP));
        $this->eavAttributeGroupsByAttributeSetIdStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_GROUPS_BY_ATTRIBUTE_SET_ID));
        $this->eavAttributeGroupByEntityTypeCodeAndAttributeSetNameAndAttributeGroupNameStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_GROUP_BY_ENTITY_TYPE_CODE_AND_ATTRIBUTE_SET_NAME_AND_ATTRIBUTE_GROUP_NAME));
    }

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

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

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

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

        // load the attributes
        $this->eavAttributeGroupsByAttributeSetIdStmt->execute(array(MemberNames::ATTRIBUTE_SET_ID => $attributeSetId));

        // load the available EAV attribute option groups
        $availableEavAttributeGroups = $this->eavAttributeGroupsByAttributeSetIdStmt->fetchAll(\PDO::FETCH_ASSOC);

        // prepare the array with the attribute group names as keys
        foreach ($availableEavAttributeGroups as $eavAttributeGroup) {
            $eavAttributeGroups[$eavAttributeGroup[MemberNames::ATTRIBUTE_GROUP_NAME]] = $eavAttributeGroup;
        }

        // return the array with the EAV attribute groups
        return $eavAttributeGroups;
    }

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

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

        // execute the prepared statement and return the EAV attribute group with the passed params
        $this->eavAttributeGroupByEntityTypeCodeAndAttributeSetNameAndAttributeGroupNameStmt->execute($params);
        return $this->eavAttributeGroupByEntityTypeCodeAndAttributeSetNameAndAttributeGroupNameStmt->fetch(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Cli\Subjects\MoveFilesSubject
 *
 * 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;

/**
 * The subject implementation to move the files to their target directory.
 *
 * @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 MoveFilesSubject extends AbstractSubject
{

    /**
     * Return's the header mappings for the actual entity.
     *
     * @return array The header mappings
     */
    public function getHeaderMappings()
    {
        return array();
    }

    /**
     * Return's the default callback frontend input mappings for the user defined attributes.
     *
     * @return array The default frontend input callback mappings
     */
    public function getDefaultFrontendInputCallbackMappings()
    {
        return array();
    }

    /**
     * Imports the content of the file with the passed filename.
     *
     * @param string $serial   The serial of the actual import
     * @param string $filename The filename to process
     *
     * @return void
     * @throws \Exception Is thrown, if the import can't be processed
     */
    public function import($serial, $filename)
    {

        // initialize the serial/filename
        $this->setSerial($serial);
        $this->setFilename($filename);

        // query whether the new source directory has to be created or not
        if (!$this->isDir($newSourceDir = $this->getNewSourceDir($serial))) {
            $this->mkdir($newSourceDir);
        }

        // move the file to the new source directory
        $this->rename($filename, sprintf('%s/%s', $newSourceDir, basename($filename)));
    }
}


<?php

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

namespace TechDivision\Import\Plugins;

use TechDivision\Import\Utils\RegistryKeys;

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

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

        // load the global data from the import processor
        $globalData = $this->getImportProcessor()->getGlobalData();

        // add the status with the global data
        $this->getRegistryProcessor()->mergeAttributesRecursive(
            $this->getSerial(),
            array(RegistryKeys::GLOBAL_DATA => $globalData)
        );
    }
}


<?php

/**
 * TechDivision\Import\Utils\SwiftMailer\SmtpTransportMailerFactory
 *
 * 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\SwiftMailer;

use TechDivision\Import\Utils\SwiftMailerKeys;
use TechDivision\Import\Configuration\SwiftMailerConfigurationInterface;

/**
 * Factory implementation for a swift mailer with SMTP transport.
 *
 * @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 SmtpTransportMailerFactory
{

    /**
     * Creates a new swift mailer instance based on the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\SwiftMailerConfigurationInterface $swiftMailerConfiguration The mailer configuration
     *
     * @return \Swift_Mailer The mailer instance
     */
    public static function factory(SwiftMailerConfigurationInterface $swiftMailerConfiguration)
    {

        // load the transport configuration
        $transportConfiguration = $swiftMailerConfiguration->getTransport();

        // load the transport factory
        $transportFactory = $transportConfiguration->getTransportFactory();

        // load the SMTP host from the configuration
        $smtpHost = null;
        if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_HOST)) {
            $smtpHost = $transportConfiguration->getParam(SwiftMailerKeys::SMTP_HOST);
        }

        // load the SMTP port from the configuration
        $smtpPort = null;
        if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_PORT)) {
            $smtpPort = $transportConfiguration->getParam(SwiftMailerKeys::SMTP_PORT);
        }

        // load the SMTP security from the configuration
        $smtpSecurity = null;
        if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_SECURITY)) {
            $smtpSecurity = $transportConfiguration->getParam(SwiftMailerKeys::SMTP_SECURITY);
        }

        // initialize and create the mailer transport instance
        $transport = $transportFactory::newInstance($smtpHost, $smtpPort, $smtpSecurity);

        // query whether or not if a SMTP authentication mode has been specified
        if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_AUTH_MODE)) {
            // load the authentication mode from the configuration
            $transport->setAuthMode($transportConfiguration->getParam(SwiftMailerKeys::SMTP_AUTH_MODE));
            // set the stream context options, e. g. to allow self signed certificates
            $transport->setStreamOptions(
                array(
                    'ssl' => array(
                        'allow_self_signed' => true,
                        'verify_peer'       => false
                    )
                )
            );

            // load the SMTP username from the configuration
            if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_USERNAME)) {
                $transport->setUsername($transportConfiguration->getParam(SwiftMailerKeys::SMTP_USERNAME));
            }

            // load the SMTP password from the configuration
            if ($transportConfiguration->hasParam(SwiftMailerKeys::SMTP_PASSWORD)) {
                $transport->setPassword($transportConfiguration->getParam(SwiftMailerKeys::SMTP_PASSWORD));
            }
        }

        // load the mailer factory
        $mailerFactory = $swiftMailerConfiguration->getMailerFactory();

        // initialize, create and return the swift mailer instance
        return $mailerFactory::newInstance($transport);
    }
}


<?php

/**
 * TechDivision\Import\Adapter\Csv\LexerConfigFactory
 *
 * 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\Import\Standard\LexerConfig;
use TechDivision\Import\Configuration\CsvConfigurationInterface;

/**
 * Factory implementation for a CSV lexer 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 LexerConfigFactory implements LexerConfigFactoryInterface
{

    /**
     * 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 lexer configuration instance.
     *
     * @return \Goodby\CSV\Import\Standard\LexerConfig The lexer configuration
     */
    public function createLexerConfig()
    {

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

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

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


<?php

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

/**
 * Cached 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
 */
class EavAttributeOptionValueRepository extends AbstractCachedRepository implements EavAttributeOptionValueRepositoryInterface
{

    /**
     * The prepared statement to load the existing EAV attribute option values.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeOptionValuesStmt;

    /**
     * The prepared statement to load an existing EAV attribute option value by its option id and store ID
     *
     * @var \PDOStatement
     */
    protected $eavAttributeOptionValueByOptionIdAndStoreIdStmt;

    /**
     * The prepared statement to load an existing EAV attribute option value by its attribute code, store ID and value.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeOptionValueByAttributeCodeAndStoreIdAndValueStmt;

    /**
     * The prepared statement to load an existing EAV attribute option value by its entity type ID, attribute code, store ID and value.
     *
     * @var \PDOStatement
     */
    protected $eavAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValueStmt;

    /**
     * Return's the primary key name of the entity.
     *
     * @return string The name of the entity's primary key
     */
    public function getPrimaryKeyName()
    {
        return MemberNames::VALUE_ID;
    }

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

        // initialize the prepared statements
        $this->eavAttributeOptionValuesStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUES));

        // initialize the prepared statements
        $this->eavAttributeOptionValueByOptionIdAndStoreIdStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUE_BY_OPTION_ID_AND_STORE_ID));

        // initialize the prepared statements
        $this->eavAttributeOptionValueByAttributeCodeAndStoreIdAndValueStmt =
            $this->getConnection()->prepare($this->loadStatement(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUE_BY_ATTRIBUTE_CODE_AND_STORE_ID_AND_VALUE));

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

    /**
     * Load's and return's the available EAV attribute option values.
     *
     * @return array The EAV attribute option values
     */
    public function findAll()
    {

        // load and return all available EAV attribute option values
        $this->eavAttributeOptionValuesStmt->execute(array());
        return $this->eavAttributeOptionValuesStmt->fetchAll(\PDO::FETCH_ASSOC);
    }

    /**
     * Load's and return's the EAV attribute option value with the passed option ID and store ID
     *
     * @param string  $optionId The option ID of the attribute option
     * @param integer $storeId  The store ID of the attribute option to load
     *
     * @return array The EAV attribute option value
     */
    public function findOneByOptionIdAndStoreId($optionId, $storeId)
    {

        // the parameters of the EAV attribute option to load
        $params = array(
            MemberNames::OPTION_ID => $optionId,
            MemberNames::STORE_ID  => $storeId,
        );

        // prepare the cache key
        $cacheKey = $this->cacheKey(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUE_BY_OPTION_ID_AND_STORE_ID, $params);

        // return the cached result if available
        if ($this->isCached($cacheKey)) {
            return $this->fromCache($cacheKey);
        }

        // load and return the EAV attribute option value with the passed parameters
        $this->eavAttributeOptionValueByOptionIdAndStoreIdStmt->execute($params);

        // query whether or not the EAV attribute option value is available in the database
        if ($eavAttributeOptionValue = $this->eavAttributeOptionValueByOptionIdAndStoreIdStmt->fetch(\PDO::FETCH_ASSOC)) {
            // add the EAV attribute option value to the cache, register the cache key reference as well
            $this->toCache(
                $eavAttributeOptionValue[MemberNames::VALUE_ID],
                $eavAttributeOptionValue,
                array($cacheKey => $eavAttributeOptionValue[MemberNames::VALUE_ID])
            );
            // finally, return it
            return $eavAttributeOptionValue;
        }
    }

    /**
     * Load's and return's the EAV attribute option value with the passed code, store ID and value.
     *
     * @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
     * @deprecated Since 2.0.2, because multiple attributes with the same attribute code, but differenct entity type code can be available
     * @see \TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface::findOneByEntityTypeIdAndAttributeCodeAndStoreIdAndValue()
     */
    public function findOneByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value)
    {

        // the parameters of the EAV attribute option to load
        $params = array(
            MemberNames::ATTRIBUTE_CODE => $attributeCode,
            MemberNames::STORE_ID       => $storeId,
            MemberNames::VALUE          => $value
        );

        // prepare the cache key
        $cacheKey = $this->cacheKey(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUE_BY_ATTRIBUTE_CODE_AND_STORE_ID_AND_VALUE, $params);

        // return the cached result if available
        if ($this->isCached($cacheKey)) {
            return $this->fromCache($cacheKey);
        }

        // load and return the EAV attribute option value with the passed parameters
        $this->eavAttributeOptionValueByAttributeCodeAndStoreIdAndValueStmt->execute($params);

        // query whether or not the result has been cached
        if ($eavAttributeOptionValue = $this->eavAttributeOptionValueByAttributeCodeAndStoreIdAndValueStmt->fetch(\PDO::FETCH_ASSOC)) {
            // add the EAV attribute option value to the cache, register the cache key reference as well
            $this->toCache(
                $eavAttributeOptionValue[MemberNames::VALUE_ID],
                $eavAttributeOptionValue,
                array($cacheKey => $eavAttributeOptionValue[MemberNames::VALUE_ID])
            );
            // finally, return it
            return $eavAttributeOptionValue;
        }
    }

    /**
     * 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
     */
    public function findOneByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {

        // the parameters of the EAV attribute option to load
        $params = array(
            MemberNames::ENTITY_TYPE_ID => $entityTypeId,
            MemberNames::ATTRIBUTE_CODE => $attributeCode,
            MemberNames::STORE_ID       => $storeId,
            MemberNames::VALUE          => $value
        );

        // prepare the cache key
        $cacheKey = $this->cacheKey(SqlStatementKeys::EAV_ATTRIBUTE_OPTION_VALUE_BY_ENTITY_TYPE_ID_AND_ATTRIBUTE_CODE_AND_STORE_ID_AND_VALUE, $params);

        // return the cached result if available
        if ($this->isCached($cacheKey)) {
            return $this->fromCache($cacheKey);
        }

        // load and return the EAV attribute option value with the passed parameters
        $this->eavAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValueStmt->execute($params);

        // query whether or not the result has been cached
        if ($eavAttributeOptionValue = $this->eavAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValueStmt->fetch(\PDO::FETCH_ASSOC)) {
            // add the EAV attribute option value to the cache, register the cache key reference as well
            $this->toCache(
                $eavAttributeOptionValue[MemberNames::VALUE_ID],
                $eavAttributeOptionValue,
                array($cacheKey => $eavAttributeOptionValue[MemberNames::VALUE_ID])
            );
            // finally, return it
            return $eavAttributeOptionValue;
        }
    }
}


<?php

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

namespace TechDivision\Import\Subjects\FileResolver;

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

/**
 * Factory for file resolver instances.
 *
 * @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 FileResolverFactory implements FileResolverFactoryInterface
{

    /**
     * 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 file resolver instance for the subject with the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject The subject to create the file resolver for
     *
     * @return \TechDivision\Import\Subjects\FileResolver\FileResolverInterface The file resolver instance
     */
    public function createFileResolver(SubjectConfigurationInterface $subject)
    {

        // create a new file resolver instance for the subject with the passed configuration
        $fileResolver = $this->container->get($subject->getFileResolver()->getId());
        $fileResolver->setSubjectConfiguration($subject);

        // return the file resolver instance
        return $fileResolver;
    }
}


<?php

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

/**
 * Repository implementation to load link attribute 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 LinkAttributeRepository extends AbstractRepository implements LinkAttributeRepositoryInterface
{

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

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

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

    /**
     * Return's an array with all available link attributes.
     *
     * @return array The available link attributes
     */
    public function findAll()
    {
        $this->linkAttributesStmt->execute();
        return $this->linkAttributesStmt->fetchAll(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Repositories\CoreConfigDataRepository
 *
 * 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\SqlStatementKeys;
use TechDivision\Import\Utils\Generators\GeneratorInterface;
use TechDivision\Import\Connection\ConnectionInterface;

/**
 * Repository implementation to load the Magento 2 configuration 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 CoreConfigDataRepository extends AbstractRepository implements CoreConfigDataRepositoryInterface
{

    /**
     * The UID generator for the core config data.
     *
     * @var \TechDivision\Import\Utils\Generators\GeneratorInterface
     */
    protected $coreConfigDataUidGenerator;

    /**
     * The statement to load the configuration.
     *
     * @var \PDOStatement
     */
    protected $coreConfigDataStmt;

    /**
     * Initialize the repository with the passed connection and utility class name.
     * .
     *
     * @param \TechDivision\Import\Utils\Generators\GeneratorInterface          $coreConfigDataUidGenerator The UID generator for the core config data
     * @param \TechDivision\Import\Connection\ConnectionInterface               $connection                 The connection instance
     * @param \TechDivision\Import\Repositories\SqlStatementRepositoryInterface $sqlStatementRepository     The SQL repository instance
     */
    public function __construct(
        GeneratorInterface $coreConfigDataUidGenerator,
        ConnectionInterface $connection,
        SqlStatementRepositoryInterface $sqlStatementRepository
    ) {
        parent::__construct($connection, $sqlStatementRepository);
        $this->coreConfigDataUidGenerator = $coreConfigDataUidGenerator;
    }

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

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

    /**
     * Return's an array with the Magento 2 configuration.
     *
     * @return array The configuration
     */
    public function findAll()
    {

        // prepare the core configuration data
        $coreConfigDatas = array();

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

        // load the available core config data
        $availableCoreConfigDatas = $this->coreConfigDataStmt->fetchAll(\PDO::FETCH_ASSOC);

        // create the array with the resolved category path as keys
        foreach ($availableCoreConfigDatas as $coreConfigData) {
            // prepare the unique identifier
            $uniqueIdentifier = $this->coreConfigDataUidGenerator->generate($coreConfigData);
            // append the config data value with the unique identifier
            $coreConfigDatas[$uniqueIdentifier] = $coreConfigData;
        }

        // return array with the configuration data
        return $coreConfigDatas;
    }
}


<?php

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

use TechDivision\Import\Utils\LoggerKeys;

/**
 * A trait that provides system logger 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 SystemLoggerTrait
{

    /**
     * The array with the system logger instances.
     *
     * @var array
     */
    protected $systemLoggers = array();

    /**
     * Return's the logger with the passed name, by default the system logger.
     *
     * @param string $name The name of the requested system logger
     *
     * @return \Psr\Log\LoggerInterface The logger instance
     * @throws \Exception Is thrown, if the requested logger is NOT available
     */
    public function getSystemLogger($name = LoggerKeys::SYSTEM)
    {

        // query whether or not, the requested logger is available
        if (isset($this->systemLoggers[$name])) {
            return $this->systemLoggers[$name];
        }

        // throw an exception if the requested logger is NOT available
        throw new \Exception(sprintf('The requested logger \'%s\' is not available', $name));
    }

    /**
     * Return's the array with the system logger instances.
     *
     * @return array The logger instance
     */
    public function getSystemLoggers()
    {
        return $this->systemLoggers;
    }
}


<?php

/**
 * TechDivision\Import\Plugins\PluginFactory
 *
 * 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 Symfony\Component\DependencyInjection\ContainerInterface;
use TechDivision\Import\Configuration\PluginConfigurationInterface;

/**
 * A generic plugin 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 PluginFactory implements PluginFactoryInterface
{

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

    /**
     * Factory method to create new plugin instance.
     *
     * @param \TechDivision\Import\Configuration\PluginConfigurationInterface $pluginConfiguration The plugin configuration
     *
     * @return \TechDivision\Import\Plugins\PluginInterface The plugin instance
     */
    public function createPlugin(PluginConfigurationInterface $pluginConfiguration)
    {

        // load the plugin instance from the DI container and set the plugin configuration
        $pluginInstance = $this->container->get($pluginConfiguration->getId());
        $pluginInstance->setPluginConfiguration($pluginConfiguration);

        // return the plugin instance
        return $pluginInstance;
    }
}


<?php

/**
 * TechDivision\Import\Observers\AbstractObserver
 *
 * 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\RowTrait;
use TechDivision\Import\Utils\ScopeKeys;
use TechDivision\Import\Utils\LoggerKeys;
use TechDivision\Import\Utils\EntityStatus;
use TechDivision\Import\Subjects\SubjectInterface;

/**
 * An abstract observer 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
 */
abstract class AbstractObserver implements ObserverInterface
{

    /**
     * The trait that provides row handling functionality.
     *
     * @var TechDivision\Import\RowTrait
     */
    use RowTrait;

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

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

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

    /**
     * Initialize's and return's a new entity with the status 'create'.
     *
     * @param array $attr The attributes to merge into the new entity
     *
     * @return array The initialized entity
     */
    protected function initializeEntity(array $attr = array())
    {
        return array_merge(array(EntityStatus::MEMBER_NAME => EntityStatus::STATUS_CREATE), $attr);
    }

    /**
     * Merge's and return's the entity with the passed attributes and set's the
     * status to 'update'.
     *
     * @param array $entity The entity to merge the attributes into
     * @param array $attr   The attributes to be merged
     *
     * @return array The merged entity
     */
    protected function mergeEntity(array $entity, array $attr)
    {
        return array_merge($entity, $attr, array(EntityStatus::MEMBER_NAME => EntityStatus::STATUS_UPDATE));
    }

    /**
     * Set's the array containing header row.
     *
     * @param array $headers The array with the header row
     *
     * @return void
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function setHeaders(array $headers)
    {
        $this->getSubject()->setHeaders($headers);
    }

    /**
     * Return's the array containing header row.
     *
     * @return array The array with the header row
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getHeaders()
    {
        return $this->getSubject()->getHeaders();
    }

    /**
     * Return's the RegistryProcessor instance to handle the running threads.
     *
     * @return \TechDivision\Import\Services\RegistryProcessorInterface The registry processor instance
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getRegistryProcessor()
    {
        return $this->getSubject()->getRegistryProcessor();
    }

    /**
     * Append's the exception suffix containing filename and line number to the
     * passed message. If no message has been passed, only the suffix will be
     * returned
     *
     * @param string|null $message    The message to append the exception suffix to
     * @param string|null $filename   The filename used to create the suffix
     * @param string|null $lineNumber The line number used to create the suffx
     *
     * @return string The message with the appended exception suffix
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function appendExceptionSuffix($message = null, $filename = null, $lineNumber = null)
    {
        return $this->getSubject()->appendExceptionSuffix($message, $filename, $lineNumber);
    }

    /**
     * Wraps the passed exeception into a new one by trying to resolve the original filname,
     * line number and column name and use it for a detailed exception message.
     *
     * @param string     $columnName The column name that should be resolved
     * @param \Exception $parent     The exception we want to wrap
     * @param string     $className  The class name of the exception type we want to wrap the parent one
     *
     * @return \Exception the wrapped exception
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function wrapException(
        $columnName,
        \Exception $parent = null,
        $className = '\TechDivision\Import\Exceptions\WrappedColumnException'
    ) {
        return $this->getSubject()->wrapException($columnName, $parent, $className);
    }

    /**
     * Queries whether or not debug mode is enabled or not, default is TRUE.
     *
     * @return boolean TRUE if debug mode is enabled, else FALSE
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function isDebugMode()
    {
        return $this->getSubject()->isDebugMode();
    }

    /**
     * Stop's observer execution on the actual row.
     *
     * @return void
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function skipRow()
    {
        $this->getSubject()->skipRow();
    }

    /**
     * Return's the name of the file to import.
     *
     * @return string The filename
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getFilename()
    {
        return $this->getSubject()->getFilename();
    }

    /**
     * Return's the actual line number.
     *
     * @return integer The line number
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getLineNumber()
    {
        return $this->getSubject()->getLineNumber();
    }

    /**
     * Return's the logger with the passed name, by default the system logger.
     *
     * @param string $name The name of the requested system logger
     *
     * @return \Psr\Log\LoggerInterface The logger instance
     * @throws \Exception Is thrown, if the requested logger is NOT available
     *
     * @codeCoverageIgnore
     */
    protected function getSystemLogger($name = LoggerKeys::SYSTEM)
    {
        return $this->getSubject()->getSystemLogger($name);
    }

    /**
     * Return's the array with the system logger instances.
     *
     * @return array The logger instance
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getSystemLoggers()
    {
        return $this->getSubject()->getSystemLoggers();
    }

    /**
     * Return's the multiple field delimiter character to use, default value is comma (,).
     *
     * @return string The multiple field delimiter character
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getMultipleFieldDelimiter()
    {
        return $this->getSubject()->getMultipleFieldDelimiter();
    }

    /**
     * Return's the multiple value delimiter character to use, default value is comma (|).
     *
     * @return string The multiple value delimiter character
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getMultipleValueDelimiter()
    {
        return $this->getSubject()->getMultipleValueDelimiter();
    }

    /**
     * Queries whether or not the header with the passed name is available.
     *
     * @param string $name The header name to query
     *
     * @return boolean TRUE if the header is available, else FALSE
     *
     * @codeCoverageIgnore
     */
    public function hasHeader($name)
    {
        return $this->getSubject()->hasHeader($name);
    }

    /**
     * Return's the header value for the passed name.
     *
     * @param string $name The name of the header to return the value for
     *
     * @return mixed The header value
     * @throws \InvalidArgumentException Is thrown, if the header with the passed name is NOT available
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getHeader($name)
    {
        return $this->getSubject()->getHeader($name);
    }

    /**
     * Add's the header with the passed name and position, if not NULL.
     *
     * @param string $name The header name to add
     *
     * @return integer The new headers position
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function addHeader($name)
    {
        return $this->getSubject()->addHeader($name);
    }

    /**
     * Return's the ID of the product that has been created recently.
     *
     * @return string The entity Id
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getLastEntityId()
    {
        return $this->getSubject()->getLastEntityId();
    }

    /**
     * Return's the source date format to use.
     *
     * @return string The source date format
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getSourceDateFormat()
    {
        return $this->getSubject()->getSourceDateFormat();
    }

    /**
     * Cast's the passed value based on the backend type information.
     *
     * @param string $backendType The backend type to cast to
     * @param mixed  $value       The value to be casted
     *
     * @return mixed The casted value
     *
     * @codeCoverageIgnore
     */
    public function castValueByBackendType($backendType, $value)
    {
        return $this->getSubject()->castValueByBackendType($backendType, $value);
    }

    /**
     * Set's the store view code the create the product/attributes for.
     *
     * @param string $storeViewCode The store view code
     *
     * @return void
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function setStoreViewCode($storeViewCode)
    {
        $this->getSubject()->setStoreViewCode($storeViewCode);
    }

    /**
     * Return's the store view code the create the product/attributes for.
     *
     * @param string|null $default The default value to return, if the store view code has not been set
     *
     * @return string The store view code
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getStoreViewCode($default = null)
    {
        return $this->getSubject()->getStoreViewCode($default);
    }

    /**
     * Prepare's the store view code in the subject.
     *
     * @return void
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function prepareStoreViewCode()
    {
        $this->getSubject()->prepareStoreViewCode();
    }

    /**
     * Return's the store ID of the store with the passed store view code
     *
     * @param string $storeViewCode The store view code to return the store ID for
     *
     * @return integer The ID of the store with the passed ID
     * @throws \Exception Is thrown, if the store with the actual code is not available
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getStoreId($storeViewCode)
    {
        return $this->getSubject()->getStoreId($storeViewCode);
    }

    /**
     * Return's the store ID of the actual row, or of the default store
     * if no store view code is set in the CSV file.
     *
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
     *
     * @return integer The ID of the actual store
     * @throws \Exception Is thrown, if the store with the actual code is not available
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getRowStoreId($default = null)
    {
        return $this->getSubject()->getRowStoreId($default);
    }

    /**
     * Tries to format the passed value to a valid date with format 'Y-m-d H:i:s'.
     * If the passed value is NOT a valid date, NULL will be returned.
     *
     * @param string|null $value The value to format
     *
     * @return string The formatted date
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function formatDate($value)
    {
        return $this->getSubject()->formatDate($value);
    }

    /**
     * Extracts the elements of the passed value by exploding them
     * with the also passed delimiter.
     *
     * @param string      $value     The value to extract
     * @param string|null $delimiter The delimiter used to extrace the elements
     *
     * @return array The exploded values
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function explode($value, $delimiter = null)
    {
        return $this->getSubject()->explode($value, $delimiter);
    }

    /**
     * Return's the Magento configuration value.
     *
     * @param string  $path    The Magento path of the requested configuration value
     * @param mixed   $default The default value that has to be returned, if the requested configuration value is not set
     * @param string  $scope   The scope the configuration value has been set
     * @param integer $scopeId The scope ID the configuration value has been set
     *
     * @return mixed The configuration value
     * @throws \Exception Is thrown, if nor a value can be found or a default value has been passed
     * @deprecated Will be removed with version 1.0.0, use subject method instead
     *
     * @codeCoverageIgnore
     */
    protected function getCoreConfigData($path, $default = null, $scope = ScopeKeys::SCOPE_DEFAULT, $scopeId = 0)
    {
        return $this->getSubject()->getCoreConfigData($path, $default, $scope, $scopeId);
    }
}


<?php

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

namespace TechDivision\Import\Subjects\I18n;

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

/**
 * Factory for number converter instances.
 *
 * @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 NumberConverterFactory implements NumberConverterFactoryInterface
{

    /**
     * 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 number converter instance for the subject with the passed configuration.
     *
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject The subject to create the number converter for
     *
     * @return \TechDivision\Import\Subjects\I18n\NumberConverterInterface The number converter instance
     */
    public function createNumberConverter(SubjectConfigurationInterface $subject)
    {

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

        // return the number converter instance
        return $numberConverter;
    }
}


<?php

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

namespace TechDivision\Import\Subjects;

use TechDivision\Import\Utils\MemberNames;
use TechDivision\Import\Utils\RegistryKeys;
use TechDivision\Import\Utils\BackendTypeKeys;
use TechDivision\Import\Utils\EntityTypeCodes;

/**
 * An abstract EAV subject 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
 */
abstract class AbstractEavSubject extends AbstractSubject implements EavSubjectInterface
{

    /**
     * The available EAV attributes, grouped by their attribute set and the attribute set name as keys.
     *
     * @var array
     */
    protected $attributes = array();

    /**
     * The available user defined EAV attributes, grouped by their entity type.
     *
     * @var array
     */
    protected $userDefinedAttributes = array();

    /**
     * The attribute set of the entity that has to be created.
     *
     * @var array
     */
    protected $attributeSet = array();

    /**
     * The available EAV attribute sets.
     *
     * @var array
     */
    protected $attributeSets = array();

    /**
     * The mapping for the supported backend types (for the EAV entity) => persist methods.
     *
     * @var array
     */
    protected $backendTypes = array(
        BackendTypeKeys::BACKEND_TYPE_DATETIME => array('persistDatetimeAttribute', 'loadDatetimeAttribute', 'deleteDatetimeAttribute'),
        BackendTypeKeys::BACKEND_TYPE_DECIMAL  => array('persistDecimalAttribute', 'loadDecimalAttribute', 'deleteDecimalAttribute'),
        BackendTypeKeys::BACKEND_TYPE_INT      => array('persistIntAttribute', 'loadIntAttribute', 'deleteIntAttribute'),
        BackendTypeKeys::BACKEND_TYPE_TEXT     => array('persistTextAttribute', 'loadTextAttribute', 'deleteTextAttribute'),
        BackendTypeKeys::BACKEND_TYPE_VARCHAR  => array('persistVarcharAttribute', 'loadVarcharAttribute', 'deleteVarcharAttribute')
    );

    /**
     * The mappings for the entity type code to attribute set.
     *
     * @var array
     */
    protected $entityTypeCodeToAttributeSetMappings = array(
        EntityTypeCodes::CATALOG_PRODUCT           => EntityTypeCodes::CATALOG_PRODUCT,
        EntityTypeCodes::CATALOG_PRODUCT_PRICE     => EntityTypeCodes::CATALOG_PRODUCT,
        EntityTypeCodes::CATALOG_PRODUCT_INVENTORY => EntityTypeCodes::CATALOG_PRODUCT,
        EntityTypeCodes::CATALOG_CATEGORY          => EntityTypeCodes::CATALOG_CATEGORY,
        EntityTypeCodes::EAV_ATTRIBUTE             => EntityTypeCodes::EAV_ATTRIBUTE,
        EntityTypeCodes::NONE                      => EntityTypeCodes::NONE
    );

    /**
     * The default mappings for the user defined attributes, based on the attributes frontend input type.
     *
     * @var array
     */
    protected $defaultFrontendInputCallbackMappings = array();

    /**
     * Return's the default callback frontend input mappings for the user defined attributes.
     *
     * @return array The default frontend input callback mappings
     */
    public function getDefaultFrontendInputCallbackMappings()
    {
        return $this->defaultFrontendInputCallbackMappings;
    }

    /**
     * Intializes the previously loaded global data for exactly one bunch.
     *
     * @param string $serial The serial of the actual import
     *
     * @return void
     */
    public function setUp($serial)
    {

        // load the status of the actual import
        $status = $this->getRegistryProcessor()->getAttribute($serial);

        // load the global data we've prepared initially
        $this->attributes = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::EAV_ATTRIBUTES];
        $this->attributeSets = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ATTRIBUTE_SETS];
        $this->userDefinedAttributes = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::EAV_USER_DEFINED_ATTRIBUTES];

        // load the default frontend callback mappings from the child instance and merge with the one from the configuration
        $defaultFrontendInputCallbackMappings = $this->getDefaultFrontendInputCallbackMappings();

        // load the available frontend input callbacks from the configuration
        $availableFrontendInputCallbacks = $this->getConfiguration()->getFrontendInputCallbacks();

        // merge the default mappings with the one's found in the configuration
        foreach ($availableFrontendInputCallbacks as $frontendInputCallbackMappings) {
            foreach ($frontendInputCallbackMappings as $frontendInput => $frontentInputMappings) {
                $defaultFrontendInputCallbackMappings[$frontendInput] = $frontentInputMappings;
            }
        }

        // load the user defined EAV attributes
        $eavUserDefinedAttributes = $this->getEavUserDefinedAttributes();

        // load the user defined attributes and add the callback mappings
        foreach ($eavUserDefinedAttributes as $eavAttribute) {
            // load attribute code and frontend input type
            $attributeCode = $eavAttribute[MemberNames::ATTRIBUTE_CODE];
            $frontendInput = $eavAttribute[MemberNames::FRONTEND_INPUT];

            // query whether or not the array for the mappings has been initialized
            if (!isset($this->callbackMappings[$attributeCode])) {
                $this->callbackMappings[$attributeCode] = array();
            }

            // set the appropriate callback mapping for the attributes input type
            if (isset($defaultFrontendInputCallbackMappings[$frontendInput])) {
                foreach ($defaultFrontendInputCallbackMappings[$frontendInput] as $defaultFrontendInputCallbackMapping) {
                    $this->callbackMappings[$attributeCode][] = $defaultFrontendInputCallbackMapping;
                }
            }
        }

        // prepare the callbacks
        parent::setUp($serial);
    }

    /**
     * Return's mapping for the supported backend types (for the product entity) => persist methods.
     *
     * @return array The mapping for the supported backend types
     */
    public function getBackendTypes()
    {
        return $this->backendTypes;
    }

    /**
     * Set's the attribute set of the product that has to be created.
     *
     * @param array $attributeSet The attribute set
     *
     * @return void
     */
    public function setAttributeSet(array $attributeSet)
    {
        $this->attributeSet = $attributeSet;
    }

    /**
     * Return's the attribute set of the product that has to be created.
     *
     * @return array The attribute set
     */
    public function getAttributeSet()
    {
        return $this->attributeSet;
    }

    /**
     * Cast's the passed value based on the backend type information.
     *
     * @param string $backendType The backend type to cast to
     * @param mixed  $value       The value to be casted
     *
     * @return mixed The casted value
     */
    public function castValueByBackendType($backendType, $value)
    {

        // cast the value to a valid timestamp
        if ($backendType === BackendTypeKeys::BACKEND_TYPE_DATETIME) {
            return \DateTime::createFromFormat($this->getConfiguration()->getDateConverter()->getSourceDateFormat(), $value)->format('Y-m-d H:i:s');
        }

        // cast the value to a float value
        if ($backendType === BackendTypeKeys::BACKEND_TYPE_FLOAT) {
            return (float) $value;
        }

        // cast the value to an integer
        if ($backendType === BackendTypeKeys::BACKEND_TYPE_INT) {
            return (int) $value;
        }

        // we don't need to cast strings
        return $value;
    }

    /**
     * Return's the entity type code to be used.
     *
     * @return string The entity type code to be used
     */
    public function getEntityTypeCode()
    {

        // load the entity type code from the configuration
        $entityTypeCode = $this->getConfiguration()->getConfiguration()->getEntityTypeCode();

        // try to map the entity type code
        if (isset($this->entityTypeCodeToAttributeSetMappings[$entityTypeCode])) {
            $entityTypeCode = $this->entityTypeCodeToAttributeSetMappings[$entityTypeCode];
        }

        // return the (mapped) entity type code
        return $entityTypeCode;
    }

    /**
     * Return's the attribute set with the passed attribute set name.
     *
     * @param string $attributeSetName The name of the requested attribute set
     *
     * @return array The attribute set data
     * @throws \Exception Is thrown, if the attribute set or the given entity type with the passed name is not available
     */
    public function getAttributeSetByAttributeSetName($attributeSetName)
    {

        // query whether or not attribute sets for the actualy entity type code are available
        if (isset($this->attributeSets[$entityTypeCode = $this->getEntityTypeCode()])) {
            // load the attribute sets for the actualy entity type code
            $attributSets = $this->attributeSets[$entityTypeCode];

            // query whether or not, the requested attribute set is available
            if (isset($attributSets[$attributeSetName])) {
                return $attributSets[$attributeSetName];
            }

            // throw an exception, if not
            throw new \Exception(
                $this->appendExceptionSuffix(
                    sprintf('Found invalid attribute set name "%s"', $attributeSetName)
                )
            );
        }

        // throw an exception, if not
        throw new \Exception(
            $this->appendExceptionSuffix(
                sprintf('Found invalid entity type code "%s"', $entityTypeCode)
            )
        );
    }

    /**
     * Return's the attributes for the attribute set of the product that has to be created.
     *
     * @return array The attributes
     * @throws \Exception Is thrown, if the attribute set or the given entity type with the passed name is not available
     */
    public function getAttributes()
    {

        // query whether or not, the requested EAV attributes are available
        if (isset($this->attributes[$entityTypeCode = $this->getEntityTypeCode()])) {
            // load the attributes for the entity type code
            $attributes = $this->attributes[$entityTypeCode];

            // query whether or not an attribute set has been loaded from the source file
            if (is_array($this->attributeSet) && isset($this->attributeSet[MemberNames::ATTRIBUTE_SET_NAME])) {
                // load the attribute set name
                $attributeSetName = $this->attributeSet[MemberNames::ATTRIBUTE_SET_NAME];

                // query whether or not attributes for the actual attribute set name
                if ($attributeSetName && isset($attributes[$attributeSetName])) {
                    return $attributes[$attributeSetName];
                }

                // throw an exception, if not
                throw new \Exception(
                    $this->appendExceptionSuffix(
                        sprintf('Found invalid attribute set name "%s"', $attributeSetName)
                    )
                );
            } else {
                return call_user_func_array('array_merge', $attributes);
            }
        }

        // throw an exception, if not
        throw new \Exception(
            $this->appendExceptionSuffix(
                sprintf('Found invalid entity type code "%s"', $entityTypeCode)
            )
        );
    }

    /**
     * Return's an array with the available user defined EAV attributes for the actual entity type.
     *
     * @return array The array with the user defined EAV attributes
     */
    public function getEavUserDefinedAttributes()
    {

        // initialize the array with the user defined EAV attributes
        $eavUserDefinedAttributes = array();

        // query whether or not user defined EAV attributes for the actualy entity type are available
        if (isset($this->userDefinedAttributes[$entityTypeCode = $this->getEntityTypeCode()])) {
            $eavUserDefinedAttributes = $this->userDefinedAttributes[$entityTypeCode];
        }

        // return the array with the user defined EAV attributes
        return $eavUserDefinedAttributes;
    }

    /**
     * Return's the EAV attribute with the passed attribute code.
     *
     * @param string $attributeCode The attribute code
     *
     * @return array The array with the EAV attribute
     * @throws \Exception Is thrown if the attribute with the passed code is not available
     */
    public function getEavAttributeByAttributeCode($attributeCode)
    {

        // load the attributes
        $attributes = $this->getAttributes();

        // query whether or not the attribute exists
        if (isset($attributes[$attributeCode])) {
            return $attributes[$attributeCode];
        }

        // throw an exception if the requested attribute is not available
        throw new \Exception(
            $this->appendExceptionSuffix(
                sprintf('Can\'t load attribute with code "%s"', $attributeCode)
            )
        );
    }
}


<?php

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

/**
 * Loader implementation that dynamically creates an array with columns from the CSV file that contains values.
 *
 * @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 DynamicAttributeLoader implements AttributeLoaderInterface
{

    /**
     * Prepare the attributes of the entity that has to be persisted.
     *
     * Second parameter has  to be an associative array with the member name as key
     * and an array that contains the column name and the data's backend type, e. g.
     *
     * array('min_qty => array('out_of_stock_qty', 'float'))
     *
     * @param \TechDivision\Import\Observers\DynamicAttributeObserverInterface $observer The observer to load the attributes for
     * @param array                                                            $columns  The array with the possible columns to load the data from
     *
     * @return array The prepared attributes
     * @throws \Exception Is thrown, if the size of the option values doesn't equals the size of swatch values, in case
     */
    public function load(DynamicAttributeObserverInterface $observer, array $columns)
    {

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

        // iterate over the possible columns and handle the data
        foreach ($columns as $memberName => $metadata) {
            // extract column name and backend type from the metadata
            list ($columnName, $backendType) = $metadata;
            // query whether or not, the column is available in the CSV file
            if ($observer->hasHeader($columnName)) {
                // query whether or not a column contains a value
                if ($observer->hasValue($columnName)) {
                    $attr[$memberName] = $observer->castValueByBackendType($backendType, $observer->getValue($columnName));
                }
            }
        }

        // return the prepared product
        return $attr;
    }
}