<?php

namespace StatonLab\TripalTestSuite\Console\Commands;

use Symfony\Component\Console\Input\InputArgument;
use StatonLab\TripalTestSuite\Exceptions\FileNotFoundException;

class MakeSeederCommand extends BaseCommand
{
    /**
     * Configure the command.
     */
    public function configure()
    {
        $this->setName('make:seeder')
            ->setHelp('make:seeder ExampleTableSeeder')
            ->setDescription('Creates a database seeder file.');

        $this->addArgument('name', InputArgument::REQUIRED, 'The name of the seeder such as ExampleTableSeeder');
    }

    /**
     * Execute the command.
     */
    public function handle()
    {
        try {
            $this->createDatabaseSeedersFolder();
        } catch (FileNotFoundException $exception) {
            $this->error($exception->getMessage());
            $message = "Please make sure you are running this command from the module's root directory. ";
            $message .= "If you have not run `tripaltest init` yet, please run it before running this command again";
            $this->error($message);
        }

        try {
            $path = $this->makeSeeder();
            $this->line("Seeder $path was created successfully.");
        } catch (\Exception $exception) {
            $this->error($exception->getMessage());
        }
    }

    /**
     * Creates the database seeders folder if it doesn't exist.
     *
     * @throws FileNotFoundException
     */
    protected function createDatabaseSeedersFolder()
    {
        $path = getcwd().'/tests';
        if (! file_exists($path)) {
            throw new FileNotFoundException('Tests folder not found.');
        }

        if (! file_exists($path.'/DatabaseSeeders')) {
            mkdir($path.'/DatabaseSeeders');
        }
    }

    /**
     * Make a database seeder.
     *
     * @throws \Exception
     */
    protected function makeSeeder()
    {
        $path = getcwd().'/tests/DatabaseSeeders';
        $name = $this->getArgument('name');
        $stub = __DIR__.'/../../../stubs/SeederStub.php';
        $content = file_get_contents($stub);
        $content = str_replace('$$CLASS_NAME$$', $name, $content);
        $path = "{$path}/{$name}.php";

        if (file_exists($path)) {
            throw new \Exception("File already exists at $path");
        }

        $done = file_put_contents($path, $content);

        if ($done === false) {
            throw new \Exception("Could not create file at $path");
        }

        return $path;
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\AttributeOptionUpdateObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Utils\StoreViewCodes;
use TechDivision\Import\Attribute\Utils\ColumnKeys;

/**
 * Observer that update's the attribute options found in the additional CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class AttributeOptionUpdateObserver extends AttributeOptionObserver
{

    /**
     * Initialize the EAV attribute option with the passed attributes and returns an instance.
     *
     * @param array $attr The EAV attribute option attributes
     *
     * @return array The initialized EAV attribute option
     */
    protected function initializeAttribute(array $attr)
    {

        // load the entity type ID for the value from the system configuration
        $entityTypeId = $this->getEntityTypeId();

        // initialize the data to load the EAV attribute option
        $value = $this->getValue(ColumnKeys::VALUE);
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
        $attributeCode = $this->getValue(ColumnKeys::ATTRIBUTE_CODE);

        // try to load the EAV attribute option
        if ($attributeOption = $this->loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)) {
            return $this->mergeEntity($attributeOption, $attr);
        }

        // simply return the attributes
        return $attr;
    }

    /**
     * Load's and return's the EAV attribute option 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 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
     */
    protected function loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
    }
}


<?php

namespace StatonLab\TripalTestSuite;

trait DBTransaction
{
    /**
     * @var \DatabaseTransaction
     */
    protected $_transaction;

    /**
     * Start a Drupal DB Transaction.
     */
    protected function DBTransactionSetUp()
    {
        $this->_transaction = db_transaction(uniqid());

        $shutdown = '\\StatonLab\\TripalTestSuite\\DBTransaction::rollbackTransaction';
        register_shutdown_function($shutdown, $this->_transaction);
    }

    /**
     * Rolls back a transaction in case of shutdown.
     *
     * @param \DatabaseTransaction $transaction
     */
    public static function rollbackTransaction($transaction)
    {
        if ($transaction instanceof \DatabaseTransaction) {
            try {
                $transaction->rollback();
            } catch (\DatabaseTransactionNoActiveException $exception) {

            }
        }
    }

    /**
     * Rollback the DB transaction.
     */
    protected function DBTransactionTearDown()
    {
        if ($this->_transaction) {
            $this->_transaction->rollback();

            // Null the transaction so the shutdown function doesn't attempt to rollback again
            $this->_transaction = null;
        }
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\ClearAttributeObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Attribute\Utils\ColumnKeys;
use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface;

/**
 * Observer that removes the EAV attribute with the code found in the CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class ClearAttributeObserver extends AbstractAttributeImportObserver
{

    /**
     * The attribute processor instance.
     *
     * @var \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface
     */
    protected $attributeBunchProcessor;

    /**
     * Initializes the observer with the passed subject instance.
     *
     * @param \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface $attributeBunchProcessor The attribute bunch processor instance
     */
    public function __construct(AttributeBunchProcessorInterface $attributeBunchProcessor)
    {
        $this->attributeBunchProcessor = $attributeBunchProcessor;
    }

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

        // query whether or not, we've found a new EAV attribute code => means we've found a new attribute
        if ($this->hasBeenProcessed($attributeCode = $this->getValue(ColumnKeys::ATTRIBUTE_CODE))) {
            return;
        }

        // try to load the EAV attribute with the code found in the CSV file
        $attribute = $this->loadAttributeByEntityTypeIdAndAttributeCode($this->getEntityTypeId(), $attributeCode);

        // delete the EAV attribute with the code found in the CSV file
        $this->deleteAttribute(array(MemberNames::ATTRIBUTE_ID => $attribute[MemberNames::ATTRIBUTE_ID]));

        // temporary persist the ID of the deleted attribute
        $this->setLastAttributeId($attribute[MemberNames::ATTRIBUTE_ID]);
    }

    /**
     * Return's the attribute bunch processor instance.
     *
     * @return \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface The attribute bunch processor instance
     */
    protected function getAttributeBunchProcessor()
    {
        return $this->attributeBunchProcessor;
    }

    /**
     * Queries whether or not the attribute with the passed code has already been processed.
     *
     * @param string $attributeCode The attribute code to check
     *
     * @return boolean TRUE if the path has been processed, else FALSE
     */
    protected function hasBeenProcessed($attributeCode)
    {
        return $this->getSubject()->hasBeenProcessed($attributeCode);
    }

    /**
     * Set's the ID of the attribute that has been created recently.
     *
     * @param integer $lastAttributeId The attribute ID
     *
     * @return void
     */
    protected function setLastAttributeId($lastAttributeId)
    {
        $this->getSubject()->setLastAttributeId($lastAttributeId);
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    protected function loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode);
    }

    /**
     * Delete's the EAV attribute with the passed attributes.
     *
     * @param array       $row  The attributes of the EAV attribute to delete
     * @param string|null $name The name of the prepared statement that has to be executed
     *
     * @return void
     */
    protected function deleteAttribute($row, $name = null)
    {
        $this->getAttributeBunchProcessor()->deleteAttribute($row, $name);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\AttributeDefaultOptionObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Attribute\Utils\ColumnKeys;
use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface;
use TechDivision\Import\Utils\StoreViewCodes;

/**
 * Observer that updates the attribute with the default value found in the CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class AttributeDefaultOptionObserver extends AbstractAttributeImportObserver
{

    /**
     * The attribute processor instance.
     *
     * @var \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface
     */
    protected $attributeBunchProcessor;

    /**
     * Initializes the observer with the passed subject instance.
     *
     * @param \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface $attributeBunchProcessor The attribute bunch processor instance
     */
    public function __construct(AttributeBunchProcessorInterface $attributeBunchProcessor)
    {
        $this->attributeBunchProcessor = $attributeBunchProcessor;
    }

    /**
     * Process the observer's business logic.
     *
     * @return void
     */
    protected function process()
    {

        // load the default value we want to set
        $defaultValue = $this->getValue(ColumnKeys::DEFAULT_VALUE);

        // load the entity type ID for the value from the system configuration
        $entityTypeId = $this->getEntityTypeId();

        // initialize the data to load the EAV attribute option
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
        $attributeCode = $this->getValue(ColumnKeys::ATTRIBUTE_CODE);

        // try to load the EAV attribute option
        if ($attributeOption = $this->loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $defaultValue)) {
            // load the EAV attribute with the actual code
            $attribute = $this->loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode);
            // set the default value with the EAV attribute option ID and update the attribute
            $this->persistAttribute($this->mergeEntity($attribute, array(MemberNames::DEFAULT_VALUE => $attributeOption[MemberNames::OPTION_ID])));
        }
    }

    /**
     * Return's the attribute bunch processor instance.
     *
     * @return \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface The attribute bunch processor instance
     */
    protected function getAttributeBunchProcessor()
    {
        return $this->attributeBunchProcessor;
    }

    /**
     * Persist the passed attribute.
     *
     * @param array $attribute The attribute to persist
     *
     * @return void
     */
    protected function persistAttribute(array $attribute)
    {
        return $this->getAttributeBunchProcessor()->persistAttribute($attribute);
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    protected function loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode);
    }

    /**
     * Load's and return's the EAV attribute option 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 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
     */
    protected function loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Repositories\CatalogAttributeRepository
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Repositories;

use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Attribute\Utils\SqlStatementKeys;
use TechDivision\Import\Repositories\AbstractRepository;

/**
 * Repository implementation to load EAV catalog 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-attribute
 * @link      http://www.techdivision.com
 */
class CatalogAttributeRepository extends AbstractRepository implements CatalogAttributeRepositoryInterface
{

    /**
     * The prepared statement to load an existing EAV catalog attribute by its ID.
     *
     * @var \PDOStatement
     */
    protected $catalogAttributeStmt;

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

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

    /**
     * Return's the EAV catalog attribute with the passed ID.
     *
     * @param string $attributeId The ID of the EAV catalog attribute to return
     *
     * @return array The EAV catalog attribute
     */
    public function load($attributeId)
    {
        // load and return the EAV catalog attribute with the ID
        $this->catalogAttributeStmt->execute(array(MemberNames::ATTRIBUTE_ID => $attributeId));
        return $this->catalogAttributeStmt->fetch(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Actions\Processors\CatalogAttributeCreateProcessor
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Actions\Processors;

use TechDivision\Import\Utils\EntityStatus;
use TechDivision\Import\Attribute\Utils\SqlStatementKeys;
use TechDivision\Import\Actions\Processors\AbstractCreateProcessor;

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

    /**
     * Implements the CRUD functionality the processor is responsible for,
     * can be one of CREATE, READ, UPDATE or DELETE a entity.
     *
     * @param array       $row  The data to handle
     * @param string|null $name The name of the prepared statement to execute
     *
     * @return void
     */
    public function execute($row, $name = null)
    {

        // load the field names
        $keys = array_keys($row);

        // create a unique name for the prepared statement
        $name = sprintf('%s-%s', $name, md5(implode('-', $keys)));

        // query whether or not the statement has been prepared
        if (!$this->hasPreparedStatement($name)) {
            // remove the entity status from the keys
            unset($keys[array_search(EntityStatus::MEMBER_NAME, $keys)]);

            // create the prepared UPDATE statement
            $statement = sprintf($this->loadStatement(SqlStatementKeys::CREATE_CATALOG_ATTRIBUTE), implode(',', $keys), implode(',:', $keys));

            // prepare the statement
            $this->addPreparedStatement($name, $this->getConnection()->prepare($statement));
        }

        // pass the call to the parent method
        return parent::execute($row, $name);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\AttributeUpdateObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Attribute\Utils\MemberNames;

/**
 * Observer that add/update's the EAV attribute itself.
 *
 * @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-attribute
 * @link      http://www.techdivision.com
 */
class AttributeUpdateObserver extends AttributeObserver
{

    /**
     * Initialize the attribute with the passed attributes and returns an instance.
     *
     * @param array $attr The attribute attributes
     *
     * @return array The initialized attribute
     */
    protected function initializeAttribute(array $attr)
    {

        // try to load the EAV attribute with the attribute code
        if ($attribute = $this->loadAttributeByEntityTypeIdAndAttributeCode($this->getEntityTypeId(), $attr[MemberNames::ATTRIBUTE_CODE])) {
            return $this->mergeEntity($attribute, $attr);
        }

        // simply return the attributes
        return $attr;
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    protected function loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\PreLoadAttributeIdObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Attribute\Utils\ColumnKeys;
use TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface;

/**
 * Observer that pre-loads the attribute ID of the EAV attribute with the code found in the CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class PreLoadAttributeIdObserver extends AbstractAttributeImportObserver
{

    /**
     * The attribute processor instance.
     *
     * @var \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface
     */
    protected $attributeBunchProcessor;

    /**
     * Initializes the observer with the passed subject instance.
     *
     * @param \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface $attributeBunchProcessor The attribute bunch processor instance
     */
    public function __construct(AttributeBunchProcessorInterface $attributeBunchProcessor)
    {
        $this->attributeBunchProcessor = $attributeBunchProcessor;
    }

    /**
     * Return's the attribute bunch processor instance.
     *
     * @return \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface The attribute bunch processor instance
     */
    protected function getAttributeBunchProcessor()
    {
        return $this->attributeBunchProcessor;
    }

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

        // query whether or not, we've found a new attribute code => means we've found a new EAV attribute
        if ($this->hasBeenProcessed($attributeCode = $this->getValue(ColumnKeys::ATTRIBUTE_CODE))) {
            return;
        }

        // preserve the attribute ID for the EAV attribute with the passed code
        if ($attribute = $this->loadAttributeByEntityTypeIdAndAttributeCode($this->getEntityTypeId(), $attributeCode)) {
            $this->preLoadAttributeId($attribute);
        }
    }

    /**
     * Queries whether or not the attribute with the passed code has already been processed.
     *
     * @param string $attributeCode The attribute code to check
     *
     * @return boolean TRUE if the path has been processed, else FALSE
     */
    protected function hasBeenProcessed($attributeCode)
    {
        return $this->getSubject()->hasBeenProcessed($attributeCode);
    }

    /**
     * Pre-load and temporary persist the attribute ID for the passed EAV attribute.
     *
     * @param array $attribute The EAV attribute to pre-load
     *
     * @return void
     */
    protected function preLoadAttributeId(array $attribute)
    {
        return $this->getSubject()->preLoadAttributeId($attribute);
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    protected function loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode);
    }
}


<?php

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

namespace TechDivision\Import\Attribute\Services;

use TechDivision\Import\Actions\ActionInterface;
use TechDivision\Import\Connection\ConnectionInterface;
use TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface;
use TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\AttributeLabelRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\AttributeOptionRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\EntityAttributeRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\CatalogAttributeRepositoryInterface;
use TechDivision\Import\Attribute\Repositories\AttributeOptionSwatchRepositoryInterface;

/**
 * The attribute bunch processor implementation.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2019 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class AttributeBunchProcessor implements AttributeBunchProcessorInterface
{

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

    /**
     * The attribute repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface
     */
    protected $attributeRepository;

    /**
     * The attribute label repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\AttributeLabelRepositoryInterface
     */
    protected $attributeLabelRepository;

    /**
     * The attribute option repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\AttributeOptionRepositoryInterface
     */
    protected $attributeOptionRepository;

    /**
     * The repository to access EAV attribute option values.
     *
     * @var \TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface
     */
    protected $eavAttributeOptionValueRepository;

    /**
     * The attribute option swatch repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\AttributeOptionSwatchRepositoryInterface
     */
    protected $attributeOptionSwatchRepository;

    /**
     * The catalog attribute repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\CatalogAttributeRepositoryInterface
     */
    protected $catalogAttributeRepository;

    /**
     * The entity attribute repository instance.
     *
     * @var \TechDivision\Import\Attribute\Repositories\EntityAttributeRepositoryInterface
     */
    protected $entityAttributeRepository;

    /**
     * The EAV entity type repository instance.
     *
     * @var \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface
     */
    protected $entityTypeRepository;

    /**
     * The attribute action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $attributeAction;

    /**
     * The attribute label action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $attributeLabelAction;

    /**
     * The attribute option action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $attributeOptionAction;

    /**
     * The attribute option value action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $attributeOptionValueAction;

    /**
     * The attribute option swatch action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $attributeOptionSwatchAction;

    /**
     * The attribute action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $catalogAttributeAction;

    /**
     * The entity attribute action instance.
     *
     * @var \TechDivision\Import\Actions\ActionInterface
     */
    protected $entityAttributeAction;

    /**
     * Initialize the processor with the necessary assembler and repository instances.
     *
     * @param \TechDivision\Import\Connection\ConnectionInterface                                  $connection                        The connection to use
     * @param \TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface             $attributeRepository               The attribute repository instance
     * @param \TechDivision\Import\Attribute\Repositories\AttributeLabelRepositoryInterface        $attributeLabelRepository          The attribute label repository instance
     * @param \TechDivision\Import\Attribute\Repositories\AttributeOptionRepositoryInterface       $attributeOptionRepository         The attribute repository instance
     * @param \TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface         $eavAttributeOptionValueRepository The EAV attribute option value repository to use
     * @param \TechDivision\Import\Attribute\Repositories\AttributeOptionSwatchRepositoryInterface $attributeOptionSwatchRepository   The attribute repository swatch instance
     * @param \TechDivision\Import\Attribute\Repositories\CatalogAttributeRepositoryInterface      $catalogAttributeRepository        The catalog attribute repository instance
     * @param \TechDivision\Import\Attribute\Repositories\EntityAttributeRepositoryInterface       $entityAttributeRepository         The entity attribute repository instance
     * @param \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface                   $entityTypeRepository              The entity type repository instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $attributeAction                   The attribute action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $attributeLabelAction              The attribute label action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $attributeOptionAction             The attribute option action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $attributeOptionValueAction        The attribute option value action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $attributeOptionSwatchAction       The attribute option swatch action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $catalogAttributeAction            The catalog attribute action instance
     * @param \TechDivision\Import\Actions\ActionInterface                                         $entityAttributeAction             The entity attribute action instance
     */
    public function __construct(
        ConnectionInterface $connection,
        AttributeRepositoryInterface $attributeRepository,
        AttributeLabelRepositoryInterface $attributeLabelRepository,
        AttributeOptionRepositoryInterface $attributeOptionRepository,
        EavAttributeOptionValueRepositoryInterface $eavAttributeOptionValueRepository,
        AttributeOptionSwatchRepositoryInterface $attributeOptionSwatchRepository,
        CatalogAttributeRepositoryInterface $catalogAttributeRepository,
        EntityAttributeRepositoryInterface $entityAttributeRepository,
        EavEntityTypeRepositoryInterface $entityTypeRepository,
        ActionInterface $attributeAction,
        ActionInterface $attributeLabelAction,
        ActionInterface $attributeOptionAction,
        ActionInterface $attributeOptionValueAction,
        ActionInterface $attributeOptionSwatchAction,
        ActionInterface $catalogAttributeAction,
        ActionInterface $entityAttributeAction
    ) {
        $this->setConnection($connection);
        $this->setAttributeRepository($attributeRepository);
        $this->setAttributeLabelRepository($attributeLabelRepository);
        $this->setAttributeOptionRepository($attributeOptionRepository);
        $this->setEavAttributeOptionValueRepository($eavAttributeOptionValueRepository);
        $this->setAttributeOptionSwatchRepository($attributeOptionSwatchRepository);
        $this->setCatalogAttributeRepository($catalogAttributeRepository);
        $this->setEntityAttributeRepository($entityAttributeRepository);
        $this->setEntityTypeRepository($entityTypeRepository);
        $this->setAttributeAction($attributeAction);
        $this->setAttributeLabelAction($attributeLabelAction);
        $this->setAttributeOptionAction($attributeOptionAction);
        $this->setAttributeOptionValueAction($attributeOptionValueAction);
        $this->setAttributeOptionSwatchAction($attributeOptionSwatchAction);
        $this->setCatalogAttributeAction($catalogAttributeAction);
        $this->setEntityAttributeAction($entityAttributeAction);
    }

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

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

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

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

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

    /**
     * Set's the attribute repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface $attributeRepository The attribute repository instance
     *
     * @return void
     */
    public function setAttributeRepository(AttributeRepositoryInterface $attributeRepository)
    {
        $this->attributeRepository = $attributeRepository;
    }

    /**
     * Return's the attribute repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface The attribute repository instance
     */
    public function getAttributeRepository()
    {
        return $this->attributeRepository;
    }

    /**
     * Set's the attribute label repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\AttributeLabelRepositoryInterface $attributeLabelRepository The attribute label repository instance
     *
     * @return void
     */
    public function setAttributeLabelRepository(AttributeLabelRepositoryInterface $attributeLabelRepository)
    {
        $this->attributeLabelRepository = $attributeLabelRepository;
    }

    /**
     * Return's the attribute label repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\AttributeLabelRepositoryInterface The attribute label repository instance
     */
    public function getAttributeLabelRepository()
    {
        return $this->attributeLabelRepository;
    }

    /**
     * Set's the attribute option repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\AttributeOptionRepositoryInterface $attributeOptionRepository The attribute option repository instance
     *
     * @return void
     */
    public function setAttributeOptionRepository(AttributeOptionRepositoryInterface $attributeOptionRepository)
    {
        $this->attributeOptionRepository = $attributeOptionRepository;
    }

    /**
     * Return's the attribute option repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\AttributeOptionRepositoryInterface The attribute option repository instance
     */
    public function getAttributeOptionRepository()
    {
        return $this->attributeOptionRepository;
    }

    /**
     * Set's the repository to access EAV attribute option values.
     *
     * @param \TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface $eavAttributeOptionValueRepository The repository to access EAV attribute option values
     *
     * @return void
     */
    public function setEavAttributeOptionValueRepository(EavAttributeOptionValueRepositoryInterface $eavAttributeOptionValueRepository)
    {
        $this->eavAttributeOptionValueRepository = $eavAttributeOptionValueRepository;
    }

    /**
     * Return's the repository to access EAV attribute option values.
     *
     * @return \TechDivision\Import\Repositories\EavAttributeOptionValueRepositoryInterface The repository instance
     */
    public function getEavAttributeOptionValueRepository()
    {
        return $this->eavAttributeOptionValueRepository;
    }

    /**
     * Set's the attribute option swatch repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\AttributeOptionSwatchRepositoryInterface $attributeOptionSwatchRepository The attribute option swatch repository instance
     *
     * @return void
     */
    public function setAttributeOptionSwatchRepository(AttributeOptionSwatchRepositoryInterface $attributeOptionSwatchRepository)
    {
        $this->attributeOptionSwatchRepository = $attributeOptionSwatchRepository;
    }

    /**
     * Return's the attribute option swatch repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\AttributeOptionSwatchRepositoryInterface The attribute option swatch repository instance
     */
    public function getAttributeOptionSwatchRepository()
    {
        return $this->attributeOptionSwatchRepository;
    }

    /**
     * Set's the catalog attribute repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\CatalogAttributeRepositoryInterface $catalogAttributeRepository The catalog attribute repository instance
     *
     * @return void
     */
    public function setCatalogAttributeRepository(CatalogAttributeRepositoryInterface $catalogAttributeRepository)
    {
        $this->catalogAttributeRepository = $catalogAttributeRepository;
    }

    /**
     * Return's the catalog attribute repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\CatalogAttributeRepositoryInterface The catalog attribute repository instance
     */
    public function getCatalogAttributeRepository()
    {
        return $this->catalogAttributeRepository;
    }

    /**
     * Set's the entity attribute repository instance.
     *
     * @param \TechDivision\Import\Attribute\Repositories\EntityAttributeRepositoryInterface $entityAttributeRepository The entity attribute repository instance
     *
     * @return void
     */
    public function setEntityAttributeRepository(EntityAttributeRepositoryInterface $entityAttributeRepository)
    {
        $this->entityAttributeRepository = $entityAttributeRepository;
    }

    /**
     * Return's the entity attribute repository instance.
     *
     * @return \TechDivision\Import\Attribute\Repositories\EntityAttributeRepositoryInterface The entity attribute repository instance
     */
    public function getEntityAttributeRepository()
    {
        return $this->entityAttributeRepository;
    }

    /**
     * Sets the EAV entity type repository.
     *
     * @param \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface $entitTypeRepository The repository instance
     *
     * @return void
     */
    public function setEntityTypeRepository(EavEntityTypeRepositoryInterface $entitTypeRepository)
    {
        $this->entityTypeRepository = $entitTypeRepository;
    }

    /**
     * Returns the EAV entity type repository.
     *
     * @return \TechDivision\Import\Repositories\EavEntityTypeRepositoryInterface The repository instance
     */
    public function getEntityTypeRepository()
    {
        return $this->entityTypeRepository;
    }

    /**
     * Set's the attribute action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $attributeAction The attribute action instance
     *
     * @return void
     */
    public function setAttributeAction(ActionInterface $attributeAction)
    {
        $this->attributeAction = $attributeAction;
    }

    /**
     * Return's the attribute action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The attribute action instance
     */
    public function getAttributeAction()
    {
        return $this->attributeAction;
    }

    /**
     * Set's the attribute label action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $attributeLabelAction The attribute label action instance
     *
     * @return void
     */
    public function setAttributeLabelAction(ActionInterface $attributeLabelAction)
    {
        $this->attributeLabelAction = $attributeLabelAction;
    }

    /**
     * Return's the attribute label action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The attribute label action instance
     */
    public function getAttributeLabelAction()
    {
        return $this->attributeLabelAction;
    }

    /**
     * Set's the attribute option action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $attributeOptionAction The attribute option action instance
     *
     * @return void
     */
    public function setAttributeOptionAction(ActionInterface $attributeOptionAction)
    {
        $this->attributeOptionAction = $attributeOptionAction;
    }

    /**
     * Return's the attribute option action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The attribute option action instance
     */
    public function getAttributeOptionAction()
    {
        return $this->attributeOptionAction;
    }

    /**
     * Set's the attribute option value action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $attributeOptionValueAction The attribute option value action instance
     *
     * @return void
     */
    public function setAttributeOptionValueAction(ActionInterface $attributeOptionValueAction)
    {
        $this->attributeOptionValueAction = $attributeOptionValueAction;
    }

    /**
     * Return's the attribute option value action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The attribute option value action instance
     */
    public function getAttributeOptionValueAction()
    {
        return $this->attributeOptionValueAction;
    }

    /**
     * Set's the attribute option swatch action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $attributeOptionSwatchAction The attribute option swatch action instance
     *
     * @return void
     */
    public function setAttributeOptionSwatchAction(ActionInterface $attributeOptionSwatchAction)
    {
        $this->attributeOptionSwatchAction = $attributeOptionSwatchAction;
    }

    /**
     * Return's the attribute option swatch action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The attribute option swatch action instance
     */
    public function getAttributeOptionSwatchAction()
    {
        return $this->attributeOptionSwatchAction;
    }

    /**
     * Set's the catalog attribute action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $catalogAttributeAction The catalog attribute action instance
     *
     * @return void
     */
    public function setCatalogAttributeAction(ActionInterface $catalogAttributeAction)
    {
        $this->catalogAttributeAction = $catalogAttributeAction;
    }

    /**
     * Return's the catalog attribute action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The catalog attribute action instance
     */
    public function getCatalogAttributeAction()
    {
        return $this->catalogAttributeAction;
    }

    /**
     * Set's the entity attribute action instance.
     *
     * @param \TechDivision\Import\Actions\ActionInterface $entityAttributeAction The entity attribute action instance
     *
     * @return void
     */
    public function setEntityAttributeAction(ActionInterface $entityAttributeAction)
    {
        $this->entityAttributeAction = $entityAttributeAction;
    }

    /**
     * Return's the entity attribute action instance.
     *
     * @return \TechDivision\Import\Actions\ActionInterface The entity attribute action instance
     */
    public function getEntityAttributeAction()
    {
        return $this->entityAttributeAction;
    }

    /**
     * Load's and return's the EAV attribute with the passed code.
     *
     * @param string $attributeCode The code of the EAV attribute to load
     *
     * @return array The EAV attribute
     * @deprecated Since 2.0.2
     * @see \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface::loadAttributeByEntityTypeIdAndAttributeCode()
     */
    public function loadAttributeByAttributeCode($attributeCode)
    {
        return $this->getAttributeRepository()->findOneByAttributeCode($attributeCode);
    }

    /**
     * Return's the EAV attribute label with the passed attribute code and store ID.
     *
     * @param string  $attributeCode The attribute code of the EAV attribute label to return
     * @param integer $storeId       The store ID of the EAV attribute label to return
     *
     * @return array The EAV attribute label
     * @deprecated Since 2.0.2
     * @see \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface::loadAttributeLabelByEntityTypeIdAndAttributeCodeAndStoreId()
     */
    public function loadAttributeLabelByAttributeCodeAndStoreId($attributeCode, $storeId)
    {
        return $this->getAttributeLabelRepository()->findOneByAttributeCodeAndStoreId($attributeCode, $storeId);
    }

    /**
     * Return's the EAV attribute label with the passed attribute code and store ID.
     *
     * @param integer $entityTypeId  The ID of the EAV entity attribute to return the label for
     * @param string  $attributeCode The attribute code of the EAV attribute label to return
     * @param integer $storeId       The store ID of the EAV attribute label to return
     *
     * @return array The EAV attribute label
     */
    public function loadAttributeLabelByEntityTypeIdAndAttributeCodeAndStoreId($entityTypeId, $attributeCode, $storeId)
    {
        return $this->getAttributeLabelRepository()->findOneByEntityTypeIdAndAttributeCodeAndStoreId($entityTypeId, $attributeCode, $storeId);
    }

    /**
     * Load's and return's the EAV attribute option 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
     * @deprecated Since 2.0.2
     * @see \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface::loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue()
     */
    public function loadAttributeOptionByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value)
    {
        return $this->getAttributeOptionRepository()->findOneByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value);
    }

    /**
     * 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
     * @see \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface::loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue()
     */
    public function loadAttributeOptionValueByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value)
    {
        return $this->getEavAttributeOptionValueRepository()->findOneByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value);
    }

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

    /**
     * Load's and return's the EAV attribute option swatch with the passed code, store ID, value and type.
     *
     * @param string  $attributeCode The code of the EAV attribute option swatch to load
     * @param integer $storeId       The store ID of the attribute option swatch to load
     * @param string  $value         The value of the attribute option swatch to load
     * @param string  $type          The type of the attribute option swatch to load
     *
     * @return array The EAV attribute option swatch
     * @deprecated Since 2.0.2
     * @see \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface::loadAttributeOptionSwatchByEntityTypeIdAndAttributeCodeAndStoreIdAndValue()
     */
    public function loadAttributeOptionSwatchByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value, $type)
    {
        return $this->getAttributeOptionSwatchRepository()->findOneByAttributeCodeAndStoreIdAndValue($attributeCode, $storeId, $value, $type);
    }

    /**
     * Load's and return's the EAV attribute option swatch with the passed option ID and store ID
     *
     * @param integer $optionId The option ID of the attribute option swatch to load
     * @param integer $storeId  The store ID of the attribute option swatch to load
     *
     * @return array The EAV attribute option swatch
     */
    public function loadAttributeOptionSwatchByOptionIdAndStoreId($optionId, $storeId)
    {
        return $this->getAttributeOptionSwatchRepository()->findOneByOptionIdAndStoreId($optionId, $storeId);
    }

    /**
     * Load's and retur's the EAV catalog attribute with the passed ID.
     *
     * @param string $attributeId The ID of the EAV catalog attribute to return
     *
     * @return array The EAV catalog attribute
     */
    public function loadCatalogAttribute($attributeId)
    {
        return $this->getCatalogAttributeRepository()->load($attributeId);
    }

    /**
     * Return's the EAV entity attribute with the passed attribute and attribute set ID.
     *
     * @param integer $attributeId    The ID of the EAV entity attribute's attribute to return
     * @param integer $attributeSetId The ID of the EAV entity attribute's attribute set to return
     *
     * @return array The EAV entity attribute
     */
    public function loadEntityAttributeByAttributeIdAndAttributeSetId($attributeId, $attributeSetId)
    {
        return $this->getEntityAttributeRepository()->findOneByAttributeIdAndAttributeSetId($attributeId, $attributeSetId);
    }

    /**
     * Return's the EAV entity attribute with the passed entity type, attribute, attribute set and attribute group ID.
     *
     * @param integer $entityTypeId     The ID of the EAV entity attribute's entity type to return
     * @param integer $attributeId      The ID of the EAV entity attribute's attribute to return
     * @param integer $attributeSetId   The ID of the EAV entity attribute's attribute set to return
     * @param integer $attributeGroupId The ID of the EAV entity attribute's attribute group to return
     *
     * @return array The EAV entity attribute
     */
    public function loadEntityAttributeByEntityTypeIdAndAttributeIdAndAttributeSetIdAndAttributeGroupId($entityTypeId, $attributeId, $attributeSetId, $attributeGroupId)
    {
        return $this->getEntityAttributeRepository()->findOneByEntityTypeIdAndAttributeIdAndAttributeSetIdAndAttributeGroupId($entityTypeId, $attributeId, $attributeSetId, $attributeGroupId);
    }

    /**
     * Return's an EAV entity type with the passed entity type code.
     *
     * @param string $entityTypeCode The code of the entity type to return
     *
     * @return array The entity type with the passed entity type code
     */
    public function loadEntityTypeByEntityTypeCode($entityTypeCode)
    {
        return $this->getEntityTypeRepository()->findOneByEntityTypeCode($entityTypeCode);
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    public function loadAttributeByEntityTypeIdAndAttributeCode($entityTypeId, $attributeCode)
    {
        return $this->getAttributeRepository()->findOneByEntityIdAndAttributeCode($entityTypeId, $attributeCode);
    }

    /**
     * Load's and return's the EAV attribute option 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 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
     */
    public function loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {
        return $this->getAttributeOptionRepository()->findOneByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
    }

    /**
     * Load's and return's the EAV attribute option with the passed entity type ID and code, store ID, swatch and type.
     *
     * @param string  $entityTypeId  The entity type ID of the EAV attribute to load the option 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  $swatch        The swatch of the attribute option to load
     * @param string  $type          The swatch type of the attribute option to load
     *
     * @return array The EAV attribute option
     */
    public function loadAttributeOptionByEntityTypeIdAndAttributeCodeAndStoreIdAndSwatchAndType($entityTypeId, $attributeCode, $storeId, $swatch, $type)
    {
        return $this->getAttributeOptionRepostory()->findOneByEntityTypeIdAndAttributeCodeAndStoreIdAndSwatchAndType($entityTypeId, $attributeCode, $storeId, $swatch, $type);
    }

    /**
     * Load's and return's the EAV attribute option value with the passed code, store ID and value.
     *
     * @param string  $entityTypeId  The entity type ID of the EAV attribute to load th option 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 loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
    {
        return $this->getEavAttributeOptionValueRepository()->findOneByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
    }

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

    /**
     * Returns the EAV attribute option of attribute with the passed ID with the highest sort order.
     *
     * @param integer $attributeId The ID of the attribute to return the EAV option with the highest sort order for
     *
     * @return array|null The EAV attribute option with the highest sort order
     */
    public function loadAttributeOptionByAttributeIdAndHighestSortOrder($attributeId)
    {
        return $this->getAttributeOptionRepository()->findOneByAttributeIdAndHighestSortOrder($attributeId);
    }

    /**
     * Persist's the passed EAV attribute data and return's the ID.
     *
     * @param array       $attribute The attribute data to persist
     * @param string|null $name      The name of the prepared statement that has to be executed
     *
     * @return string The ID of the persisted attribute
     */
    public function persistAttribute(array $attribute, $name = null)
    {
        return $this->getAttributeAction()->persist($attribute, $name);
    }

    /**
     * Persist the passed attribute label.
     *
     * @param array       $attributeLabel The attribute label to persist
     * @param string|null $name           The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function persistAttributeLabel(array $attributeLabel, $name = null)
    {
        $this->getAttributeLabelAction()->persist($attributeLabel, $name);
    }

    /**
     * Persist's the passed EAV attribute option data and return's the ID.
     *
     * @param array       $attributeOption The attribute option data to persist
     * @param string|null $name            The name of the prepared statement that has to be executed
     *
     * @return string The ID of the persisted attribute
     */
    public function persistAttributeOption(array $attributeOption, $name = null)
    {
        return $this->getAttributeOptionAction()->persist($attributeOption, $name);
    }

    /**
     * Persist's the passed EAV attribute option value data and return's the ID.
     *
     * @param array       $attributeOptionValue The attribute option value data to persist
     * @param string|null $name                 The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function persistAttributeOptionValue(array $attributeOptionValue, $name = null)
    {
        $this->getAttributeOptionValueAction()->persist($attributeOptionValue, $name);
    }

    /**
     * Persist the passed attribute option swatch.
     *
     * @param array       $attributeOptionSwatch The attribute option swatch to persist
     * @param string|null $name                  The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function persistAttributeOptionSwatch(array $attributeOptionSwatch, $name = null)
    {
        $this->getAttributeOptionSwatchAction()->persist($attributeOptionSwatch, $name);
    }

    /**
     * Persist's the passed EAV catalog attribute data and return's the ID.
     *
     * @param array       $catalogAttribute The catalog attribute data to persist
     * @param string|null $name             The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function persistCatalogAttribute(array $catalogAttribute, $name = null)
    {
        $this->getCatalogAttributeAction()->persist($catalogAttribute, $name);
    }

    /**
     * Persist's the passed EAV entity attribute data and return's the ID.
     *
     * @param array       $entityAttribute The entity attribute data to persist
     * @param string|null $name            The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function persistEntityAttribute(array $entityAttribute, $name = null)
    {
        $this->getEntityAttributeAction()->persist($entityAttribute, $name);
    }

    /**
     * Delete's the EAV attribute with the passed attributes.
     *
     * @param array       $row  The attributes of the EAV attribute to delete
     * @param string|null $name The name of the prepared statement that has to be executed
     *
     * @return void
     */
    public function deleteAttribute($row, $name = null)
    {
        $this->getAttributeAction()->delete($row, $name);
    }
}


<?php

namespace StatonLab\TripalTestSuite\Services;

use StatonLab\TripalTestSuite\Mocks\TestResponseMock;

class MenuCaller
{
    /**
     * Path to call.
     *
     * @var
     */
    protected $path;

    /**
     * POST or GET parameters.
     *
     * @var array
     */
    protected $params = [];

    /**
     * POST, GET, PUT, PATCH, DELETE.
     *
     * @var string
     */
    protected $method = 'GET';

    /**
     * Set method.
     *
     * @param $method
     * @return $this
     */
    public function setMethod($method)
    {
        $this->method = $method;

        return $this;
    }

    /**
     * Set public path.
     *
     * @param $path
     * @return $this
     */
    public function setPath($path)
    {
        $this->path = trim($path, '/');

        if(empty($this->path)) {
            // Home page requested so let's use /node
            $this->path = 'node';
        }

        return $this;
    }

    /**
     * Add a single param to the request.
     *
     * @param $key
     * @param $value
     */
    public function addParam($key, $value)
    {
        $this->params[$key] = $value;

        return $this;
    }

    /**
     * Add parameters to the request.
     *
     * @param $params
     * @return $this
     */
    public function addParams($params)
    {
        if (isset($params['form_params'])) {
            $params = $params['form_params'];
            $params['form_token'] = drupal_get_token();
        } elseif (isset($params['query'])) {
            $params = $params['query'];
        }

        foreach ($params as $key => $value) {
            $this->params[$key] = $value;
        }

        return $this;
    }

    /**
     * Creates a mock HTTP response.
     *
     * @return \StatonLab\TripalTestSuite\Mocks\TestResponseMock
     * @throws \Exception
     */
    public function send()
    {
        $allowed_methods = ['POST', 'GET', 'PUT', 'PATCH', 'DELETE'];
        if (! in_array($this->method, $allowed_methods)) {
            throw new \Exception("Unknown method $this->method.");
        }
        $_SERVER['REQUEST_METHOD'] = $this->method;
        $this->injectParams();

        list($status, $value, $headers) = $this->execute();

        drupal_add_http_header('Content-Type', null);

        return new TestResponseMock([
            'status' => $status,
            'body' => $value,
            'headers' => [
                    'Cache-Control' => 'no-cache, must-revalidate',
                    'Connection' => 'Keep-Alive',
                    'Content-Language' => 'en',
                    'Content-Type' => 'text/html; charset=utf-8',
                    'Date' => gmdate('D, d M Y H:is e'),
                    'Expires' => gmdate('D, d M Y H:is e'),
                    'Keep-Alive' => 'timeout=5, max=100',
                    'Server' => 'Apache/2.4.29 (Unix) PHP/'.phpversion(),
                    'Transfer-Encoding' => 'chunked',
                    'X-Content-Type-Options' => 'nosniff',
                    'X-Frame-Options' => 'SAMEORIGIN',
                    'X-Generator' => 'Drupal 7 (http://drupal.org)',
                ] + $headers,
        ]);
    }

    /**
     * Execute the menu handler and construct the response.
     *
     * @return array
     */
    protected function execute()
    {
        $this->resetHeaders();
        $this->resetStaticCache();

        // Move to drupal root where the index.php is
        $current_dir = getcwd();
        chdir(DRUPAL_ROOT);

        $value = '';
        ob_start(function ($str) use (&$value) {
            $value .= $str;
        });
        $buffer = menu_execute_active_handler($this->path, false);
        ob_end_clean();

        $status = 200;
        if ($buffer === MENU_NOT_FOUND) {
            $status = 404;
            $value = '';
            ob_start(function ($str) use (&$value) {
                $value .= $str;
            });
            drupal_not_found();
            ob_end_clean();
        } elseif ($buffer === MENU_ACCESS_DENIED) {
            $status = 403;
            $value = '';
            ob_start(function ($str) use (&$value) {
                $value .= $str;
            });
            drupal_access_denied();
            ob_end_clean();
        } elseif (empty($value)) {
            $value = drupal_render_page($buffer);
        }

        // Go back to the test directory
        chdir($current_dir);

        return [
            $status,
            $value,
            drupal_get_http_header(),
        ];
    }

    /**
     * Injects parameters into the request.
     */
    protected function injectParams()
    {
        $_GET['q'] = $this->path;
        $_SERVER['REQUEST_URI'] = $this->path;

        if (in_array($this->method, ['GET', 'DELETE'])) {
            foreach ($this->params as $key => $param) {
                $_GET[$key] = $param;
            }

            return;
        }

        $menu_item = menu_get_item($this->path);
        $_POST['form_id'] = isset($menu_item['page_arguments']) ? $menu_item['page_arguments'][0] : '';
        foreach ($this->params as $key => $param) {
            $_POST[$key] = $param;
        }
    }

    /**
     * Remove any php and drupal headers.
     */
    protected function resetHeaders()
    {
        header_remove();
        $headers = &drupal_static('drupal_http_headers', []);
        $headers = [];
    }

    /**
     *
     */
    protected function resetStaticCache()
    {
        drupal_static_reset();
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Repositories\AttributeRepository
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Repositories;

use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Attribute\Utils\SqlStatementKeys;
use TechDivision\Import\Repositories\AbstractRepository;

/**
 * Repository implementation to load EAV 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-attribute
 * @link      http://www.techdivision.com
 */
class AttributeRepository extends AbstractRepository implements AttributeRepositoryInterface
{

    /**
     * The prepared statement to load an existing EAV attribute by its attribute code.
     *
     * @var \PDOStatement
     */
    protected $attributeByAttributeCodeStmt;

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

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

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

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

    /**
     * Return's the EAV attribute with the passed code.
     *
     * @param string $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     * @deprecated Since 2.0.2
     * @see \TechDivision\Import\Attribute\Repositories\AttributeRepositoryInterface::findOneByEntityIdAndAttributeCode()
     */
    public function findOneByAttributeCode($attributeCode)
    {
        // load and return the EAV attribute with the passed code
        $this->attributeByAttributeCodeStmt->execute(array(MemberNames::ATTRIBUTE_CODE => $attributeCode));
        return $this->attributeByAttributeCodeStmt->fetch(\PDO::FETCH_ASSOC);
    }

    /**
     * Return's the EAV attribute with the passed entity type ID and code.
     *
     * @param integer $entityTypeId  The entity type ID of the EAV attribute to return
     * @param string  $attributeCode The code of the EAV attribute to return
     *
     * @return array The EAV attribute
     */
    public function findOneByEntityIdAndAttributeCode($entityTypeId, $attributeCode)
    {

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

        // load and return the EAV attribute with the passed params
        $this->attributeByEntityTypeAndAttributeCodeStmt->execute($params);
        return $this->attributeByEntityTypeAndAttributeCodeStmt->fetch(\PDO::FETCH_ASSOC);
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Subjects\BunchSubject
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Subjects;

use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Attribute\Utils\RegistryKeys;
use TechDivision\Import\Subjects\ExportableSubjectInterface;
use TechDivision\Import\Subjects\ExportableTrait;

/**
 * The subject implementation that handles the business logic to persist attributes.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class BunchSubject extends AbstractAttributeSubject implements BunchSubjectInterface, ExportableSubjectInterface
{

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

    /**
     * The ID of the attribute that has been created recently.
     *
     * @var integer
     */
    protected $lastAttributeId;

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

    /**
     * The array with the available attribute groups.
     *
     * @var array
     */
    protected $attributeGroups = array();

    /**
     * The array with the pre-loaded attribute IDs.
     *
     * @var array
     */
    protected $preLoadedAttributeIds = array();

    /**
     * The attribute code => attribute ID mapping.
     *
     * @var array
     */
    protected $attributeCodeIdMapping = array();

    /**
     * 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->attributeSets = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ATTRIBUTE_SETS];
        $this->attributeGroups = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::ATTRIBUTE_GROUPS];

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

    /**
     * Clean up the global data after importing the bunch.
     *
     * @param string $serial The serial of the actual import
     *
     * @return void
     */
    public function tearDown($serial)
    {

        // invoke the parent method
        parent::tearDown($serial);

        // load the registry processor
        $registryProcessor = $this->getRegistryProcessor();

        // update the status
        $registryProcessor->mergeAttributesRecursive(
            $serial,
            array(
                RegistryKeys::PRE_LOADED_ATTRIBUTE_IDS => $this->preLoadedAttributeIds,
            )
        );
    }

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

        // query whether or not attribute sets for the actual entity type code are available
        if (isset($this->attributeSets[$entityTypeCode])) {
            // 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(
            sprintf(
                'Found invalid attribute set name %s in file %s on line %d',
                $attributeSetName,
                $this->getFilename(),
                $this->getLineNumber()
            )
        );
    }

    /**
     * Return's the attribute group with the passed attribute set/group name.
     *
     * @param string $entityTypeCode     The entity type code of the requested attribute group
     * @param string $attributeSetName   The name of the requested attribute group's attribute set
     * @param string $attributeGroupName The name of the requested attribute group
     *
     * @return array The EAV attribute group
     * @throws \Exception Is thrown, if the attribute group with the passed attribute set/group name is not available
     */
    public function getAttributeGroupByEntityTypeCodeAndAttributeSetNameAndAttributeGroupName($entityTypeCode, $attributeSetName, $attributeGroupName)
    {

        // query whether or not attribute groups for the actual entity type code are available
        if (isset($this->attributeGroups[$entityTypeCode])) {
            // query whether or not, the attribute group with the passed set/group name is available
            if (isset($this->attributeGroups[$entityTypeCode][$attributeSetName][$attributeGroupName])) {
                return $this->attributeGroups[$entityTypeCode][$attributeSetName][$attributeGroupName];
            }
        }

        // throw an exception, if not
        throw new \Exception(
            sprintf(
                'Found invalid attribute group with entity type code %s attribute set/group name %s/%s in file %s on line %d',
                $entityTypeCode,
                $attributeSetName,
                $attributeGroupName,
                $this->getFilename(),
                $this->getLineNumber()
            )
        );
    }

    /**
     * Pre-load and temporary persist the attribute ID for the passed EAV attribute.
     *
     * @param array $attribute The EAV attribute to pre-load
     *
     * @return void
     */
    public function preLoadAttributeId(array $attribute)
    {
        $this->preLoadedAttributeIds[$attribute[MemberNames::ATTRIBUTE_CODE]]= $attribute[MemberNames::ATTRIBUTE_ID];
    }

    /**
     * Return's the ID of the attribute that has been created recently.
     *
     * @return integer The attribute ID
     */
    public function getLastEntityId()
    {
        return $this->getLastAttributeId();
    }

    /**
     * Map's the passed attribute code to the attribute ID that has been created recently.
     *
     * @param string $attributeCode The attribute code that has to be mapped
     *
     * @return void
     */
    public function addAttributeCodeIdMapping($attributeCode)
    {
        $this->attributeCodeIdMapping[$attributeCode] = $this->getLastEntityId();
    }

    /**
     * Queries whether or not the attribute with the passed code has already been processed.
     *
     * @param string $attributeCode The attribute code to check
     *
     * @return boolean TRUE if the path has been processed, else FALSE
     */
    public function hasBeenProcessed($attributeCode)
    {
        return isset($this->attributeCodeIdMapping[$attributeCode]);
    }

    /**
     * Set's the ID of the attribute that has been created recently.
     *
     * @param integer $lastAttributeId The attribute ID
     *
     * @return void
     */
    public function setLastAttributeId($lastAttributeId)
    {
        $this->lastAttributeId = $lastAttributeId;
    }

    /**
     * Return's the ID of the attribute that has been created recently.
     *
     * @return integer The attribute ID
     */
    public function getLastAttributeId()
    {
        return $this->lastAttributeId;
    }
}


<?php
namespace StatonLab\TripalTestSuite\Concerns;

use StatonLab\TripalTestSuite\Database\PublishRecords;

trait PublishesData
{
    /**
     * Publish records from chado to entities.
     *
     * @param string data_table Chado table name such as feature.
     * @param array $ids CURRENTLY UNSUPPORTED.
     * @param string $primary_key The primary key name such as feature_id. If not
     *                             provided, the key is obtained using the tripal API.
     * @throws \Exception
     * @return \StatonLab\TripalTestSuite\Database\PublishRecords
     */
    public function publish($data_table, array $ids = [], $primary_key = '')
    {
        $publisher = new PublishRecords($data_table, $ids, $primary_key);
        $publisher->publish();

        return $publisher;
    }
}


<?php

namespace StatonLab\TripalTestSuite\Services;

use PHPUnit\Framework\Assert as PHPUnit;
use StatonLab\TripalTestSuite\Exceptions\InvalidJSONException;

abstract class BaseResponse
{
    /**
     * Return response content.
     *
     * @return string
     */
    abstract public function getResponseBody();

    /**
     * Assert string exists in response body.
     *
     * @param string $content
     * @return $this
     */
    public function assertSee($content)
    {
        $response = (string)$this->getResponseBody();
        PHPUnit::assertContains($content, $response, "Unable to find [$content] in response.");

        return $this;
    }

    /**
     * Get the json response as an associative array.
     *
     * @return array
     * @throws \StatonLab\TripalTestSuite\Exceptions\InvalidJSONException
     */
    public function json()
    {
        $json = json_decode($this->getResponseBody(), true);
        if (is_null($json)) {
            PHPUnit::fail('Unable to decode json response!');

            throw new InvalidJSONException('Unable to decode json response!');
        }

        return $json;
    }

    /**
     * Assert that the response has the given structure.
     *
     * @param array $structure Expected structure
     * @params array $data Response data
     * @return $this
     */
    public function assertJsonStructure(array $structure, $data = null)
    {
        if (is_null($data)) {
            try {
                $data = $this->json();
            } catch (InvalidJSONException $exception) {
                PHPUnit::fail('Unable to decode json response!');
            }
        }

        foreach ($structure as $key => $value) {
            if (is_array($value)) {
                PHPUnit::assertArrayHasKey($key, $data);

                // Prevent infinite loops
                if (is_null($data[$key])) {
                    continue;
                }

                $this->assertJsonStructure($structure[$key], $data[$key]);
                continue;
            }

            PHPUnit::assertArrayHasKey($value, $data);
        }

        return $this;
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\AttributeOptionValueUpdateObserver
 *
 * 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>
 * @author    Vadim Justus <v.justus@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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Utils\StoreViewCodes;

/**
 * Observer that update's the attribute option values found in the additional CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @author    Vadim Justus <v.justus@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-attribute
 * @link      http://www.techdivision.com
 */
class AttributeOptionValueUpdateObserver extends AttributeOptionValueObserver
{

    /**
     * Initialize the EAV attribute option value with the passed attributes and returns an instance.
     *
     * @param array $attr The EAV attribute option value attributes
     *
     * @return array The initialized EAV attribute option value
     */
    protected function initializeAttribute(array $attr)
    {
        // initialize the data to load the EAV attribute option
        $optionId = $this->getLastOptionId();
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);

        // try to load the EAV attribute option value
        if ($attributeOptionValue = $this->loadAttributeOptionValueByOptionIdAndStoreId($optionId, $storeId)) {
            return $this->mergeEntity($attributeOptionValue, $attr);
        }

        // simply return the attributes
        return $attr;
    }

    /**
     * Load's and return's the EAV attribute option value with the passed option ID and store ID.
     *
     * @param string  $optionId The option ID
     * @param integer $storeId  The store ID of the attribute option to load
     *
     * @return array The EAV attribute option value
     */
    protected function loadAttributeOptionValueByOptionIdAndStoreId($optionId, $storeId)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeOptionValueByOptionIdAndStoreId($optionId, $storeId);
    }
}


<?php

namespace StatonLab\TripalTestSuite\Services;

use StatonLab\TripalTestSuite\Exceptions\TripalTestSuiteException;

class BootstrapDrupal
{
    /**
     * @var bool
     */
    public static $bootstrapped = false;

    /**
     * Bootstrap Drupal.
     *
     * @throws \Statonlab\TripalTestSuite\Exceptions\TripalTestSuiteException
     */
    public function run()
    {
        // Don't bootstrap twice in runtime
        if (static::$bootstrapped) {
            return;
        }

        // Read environment variables to set drupal root
        $this->readEnvironmentFile();

        // Set the base url if provided in environment
        $this->setBaseURL();

        // Read environment variables
        if (! defined('DRUPAL_ROOT')) {
            define('DRUPAL_ROOT', $this->getDrupalRoot());
        }

        if (empty(DRUPAL_ROOT)) {
            throw new TripalTestSuiteException('DRUPAL_ROOT is not configured correctly. Please use .env files to set DRUPAL_ROOT.');
        }

        require_once DRUPAL_ROOT.'/includes/bootstrap.inc';
        $_SERVER['REMOTE_ADDR'] = '127.0.0.1';

        $cwd = getcwd();
        chdir(DRUPAL_ROOT);
        // The bootstrap function won't run twice since drupal checks if it has run it before
        drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
        chdir($cwd);

        static::$bootstrapped = true;
    }

    /**
     * Gets Drupal root path.
     *
     * @return string
     */
    protected function getDrupalRoot()
    {
        if ($path = getenv('DRUPAL_ROOT')) {
            return $path;
        }

        $path = getcwd();
        while ($path !== '/') {
            if (file_exists($path.'/includes/bootstrap.inc')) {
                return $path;
            }

            $path = dirname($path);
        }

        return '';
    }

    /**
     * Set the global base url if provided in .env
     */
    protected function setBaseURL()
    {
        global $base_url;

        if ($url = getenv('BASE_URL')) {
            $base_url = $url;

            return;
        }

        $base_url = 'http://127.0.0.1';
    }

    /**
     * Look for environment variable file.
     *
     * @return bool|string
     */
    protected function getEnvironmentFilePath()
    {
        $dir = getcwd();

        // If running phpunit from the module root dir
        if (file_exists($dir.'/tests/.env')) {
            return $dir.'/tests/.env';
        }

        // If running phpunit from the module root dir
        if (file_exists($dir.'/test/.env')) {
            return $dir.'/test/.env';
        }

        // If running php unit from within the tests dir
        if (file_exists($dir.'/.env')) {
            return $dir.'/.env';
        }

        return false;
    }

    /**
     * Read env files.
     *
     * @throws \Statonlab\TripalTestSuite\Exceptions\TripalTestSuiteException
     */
    protected function readEnvironmentFile()
    {
        $env_file_path = $this->getEnvironmentFilePath();

        if ($env_file_path) {
            $line_count = 0;
            $file = fopen($env_file_path, 'r');
            while ($line = fgets($file)) {
                $line++;
                $line = trim($line);
                if (empty($line)) {
                    continue;
                }

                // Ignore comments
                if(str_begins_with('//', $line) || str_begins_with('-', $line) || str_begins_with('#', $line)) {
                    continue;
                }

                if (putenv(str_replace("\n", '', trim($line))) === false) {
                    throw new TripalTestSuiteException("Could not read environment line $line_count: $line");
                }
            }
        }
    }
}


<?php

/**
 * TechDivision\Import\Attribute\Observers\AttributeOptionSwatchObserver
 *
 * 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-attribute
 * @link      http://www.techdivision.com
 */

namespace TechDivision\Import\Attribute\Observers;

use TechDivision\Import\Attribute\Utils\MemberNames;
use TechDivision\Import\Utils\StoreViewCodes;

/**
 * Observer that update's the attribute option swatchs found in the additional CSV file.
 *
 * @author    Tim Wagner <t.wagner@techdivision.com>
 * @copyright 2016 TechDivision GmbH <info@techdivision.com>
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
 * @link      https://github.com/techdivision/import-attribute
 * @link      http://www.techdivision.com
 */
class AttributeOptionSwatchUpdateObserver extends AttributeOptionSwatchObserver
{

    /**
     * Initialize the EAV attribute option value with the passed attributes and returns an instance.
     *
     * @param array $attr The EAV attribute option value attributes
     *
     * @return array The initialized EAV attribute option value
     */
    protected function initializeAttribute(array $attr)
    {

        // initialize the data to load the EAV attribute option swatch
        $optionId = isset($attr[MemberNames::OPTION_ID]) ? $attr[MemberNames::OPTION_ID] : null;
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);

        // try to load the EAV attribute option swatch
        if ($attributeOptionSwatch = $this->loadAttributeOptionSwatchByOptionIdAndStoreId($optionId, $storeId)) {
            return $this->mergeEntity($attributeOptionSwatch, $attr);
        }

        // simply return the attributes
        return $attr;
    }

    /**
     * Load's and return's the EAV attribute option swatch with the passed code, store ID, value and type.
     *
     * @param integer $optionId The option ID of the attribute option swatch to load
     * @param integer $storeId  The store ID of the attribute option swatch to load
     *
     * @return array The EAV attribute option swatch
     */
    protected function loadAttributeOptionSwatchByOptionIdAndStoreId($optionId, $storeId)
    {
        return $this->getAttributeBunchProcessor()->loadAttributeOptionSwatchByOptionIdAndStoreId($optionId, $storeId);
    }
}