diff --git a/Category/Model/Factory/Import.php b/Category/Model/Factory/Import.php
index 5c2fa17..918f6ce 100644
--- a/Category/Model/Factory/Import.php
+++ b/Category/Model/Factory/Import.php
@@ -5,6 +5,8 @@
use \Pimgento\Import\Model\Factory;
use \Pimgento\Entities\Model\Entities;
use \Pimgento\Import\Helper\Config as helperConfig;
+use \Pimgento\Staging\Helper\Config as StagingConfigHelper;
+use \Pimgento\Staging\Helper\Import as StagingHelper;
use \Pimgento\Import\Helper\UrlRewrite as urlRewriteHelper;
use \Magento\Framework\Event\ManagerInterface;
use \Magento\Catalog\Model\Category;
@@ -38,6 +40,16 @@ class Import extends Factory
*/
protected $_urlRewriteHelper;
+ /**
+ * @var StagingHelper
+ */
+ protected $stagingHelper;
+
+ /**
+ * @var StagingConfigHelper
+ */
+ protected $stagingConfigHelper;
+
/**
* @param \Pimgento\Entities\Model\Entities $entities
* @param \Pimgento\Import\Helper\Config $helperConfig
@@ -47,6 +59,8 @@ class Import extends Factory
* @param \Magento\Catalog\Model\Category $category
* @param \Magento\Framework\App\Cache\TypeListInterface $cacheTypeList
* @param urlRewriteHelper $urlRewriteHelper
+ * @param StagingConfigHelper $stagingConfigHelper
+ * @param StagingHelper $stagingHelper
* @param array $data
*/
public function __construct(
@@ -58,6 +72,8 @@ public function __construct(
Category $category,
TypeListInterface $cacheTypeList,
urlRewriteHelper $urlRewriteHelper,
+ StagingConfigHelper $stagingConfigHelper,
+ StagingHelper $stagingHelper,
array $data = []
)
{
@@ -66,6 +82,8 @@ public function __construct(
$this->_category = $category;
$this->_cacheTypeList = $cacheTypeList;
$this->_urlRewriteHelper = $urlRewriteHelper;
+ $this->stagingConfigHelper = $stagingConfigHelper;
+ $this->stagingHelper = $stagingHelper;
}
/**
@@ -84,6 +102,17 @@ public function createTable()
}
}
+ /**
+ * Add required columns
+ */
+ public function addRequiredData()
+ {
+ $connection = $this->_entities->getResource()->getConnection();
+ $tmpTable = $this->_entities->getTableName($this->getCode());
+
+ $this->stagingHelper->addRequiredData($connection, $tmpTable);
+ }
+
/**
* Insert data into temporary table
*/
@@ -103,7 +132,26 @@ public function insertData()
*/
public function matchEntity()
{
- $this->_entities->matchEntity($this->getCode(), 'code', 'catalog_category_entity', 'entity_id');
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ /**
+ * When using staging module entity id's are not the primary key of the catalog_product_entity
+ * table anymore. The new primary keys is row_id. Before we get information on the row_id, we still
+ * need to get the entiy_id of the products to be imported. We are therefore going to use a different
+ * table built for this purpose in magento.
+ */
+ $this->_entities->matchEntity($this->getCode(), 'code', 'sequence_catalog_category', 'sequence_value');
+
+ // Once the entitie id's are matched we can match the row ids.
+ $this->stagingHelper->matchEntityRows(
+ $this->_entities,
+ 'catalog_category_entity',
+ $this->getCode(),
+ StagingConfigHelper::STAGING_MODE_LAST
+ );
+
+ } else {
+ $this->_entities->matchEntity($this->getCode(), 'code', 'catalog_category_entity', 'entity_id');
+ }
}
/**
@@ -281,29 +329,37 @@ public function createEntities()
'children_count' => new Expr('0'),
);
- $columnIdentifier = $this->_entities->getColumnIdentifier($table);
-
- if ($columnIdentifier == 'row_id') {
- $values['row_id'] = '_entity_id';
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ $values['created_in'] = new Expr(1);
+ $values['updated_in'] = new Expr(VersionManager::MAX_VERSION);
+ $values['row_id'] = '_row_id';
}
+ $this->stagingHelper->createEntitiesBefore($connection, 'sequence_catalog_category', $tmpTable);
+
$parents = $connection->select()->from($tmpTable, $values);
$connection->query(
$connection->insertFromSelect(
- $parents, $table, array_keys($values), 1
+ $parents,
+ $table,
+ array_keys($values),
+ 1
)
);
+ $this->stagingHelper->createEntitiesAfter(
+ $connection,
+ 'catalog_category_entity',
+ $tmpTable,
+ StagingConfigHelper::STAGING_MODE_LAST
+ );
+
$values = array(
'created_at' => new Expr('now()')
);
$connection->update($table, $values, 'created_at IS NULL');
- if ($columnIdentifier == 'row_id') {
- $values = [
- 'created_in' => new Expr(1),
- 'updated_in' => new Expr(VersionManager::MAX_VERSION),
- ];
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
$connection->update($table, $values, 'created_in = 0 AND updated_in = 0');
}
}
@@ -342,7 +398,8 @@ public function setValues()
$resource->getTable('catalog_category_entity'),
$values,
3,
- $store['store_id']
+ $store['store_id'],
+ 1
);
}
}
diff --git a/Category/Observer/AddPimgentoImportObserver.php b/Category/Observer/AddPimgentoImportObserver.php
index 7a36691..0ecff46 100644
--- a/Category/Observer/AddPimgentoImportObserver.php
+++ b/Category/Observer/AddPimgentoImportObserver.php
@@ -59,6 +59,10 @@ protected function getStepsDefinition()
'comment' => __('Create temporary table'),
'method' => 'createTable',
),
+ array(
+ 'comment' => __('Add product required data'),
+ 'method' => 'addRequiredData',
+ ),
array(
'comment' => __('Fill temporary table'),
'method' => 'insertData',
diff --git a/Category/etc/module.xml b/Category/etc/module.xml
index 39ebfa9..ae02075 100644
--- a/Category/etc/module.xml
+++ b/Category/etc/module.xml
@@ -4,6 +4,7 @@
+
\ No newline at end of file
diff --git a/Entities/Model/Entities.php b/Entities/Model/Entities.php
index c3c84a9..055cf7c 100644
--- a/Entities/Model/Entities.php
+++ b/Entities/Model/Entities.php
@@ -183,7 +183,14 @@ public function matchEntity($tableSuffix, $pimKey, $entityTable, $entityKey, $pr
public function setValues($tableSuffix, $entityTable, $values, $entityTypeId, $storeId, $mode = 1)
{
$this->_getResource()
- ->setValues($this->getTableName($tableSuffix), $entityTable, $values, $entityTypeId, $storeId, $mode);
+ ->setValues(
+ $this->getTableName($tableSuffix),
+ $entityTable,
+ $values,
+ $entityTypeId,
+ $storeId,
+ $mode
+ );
return $this;
}
diff --git a/Entities/Model/ResourceModel/Entities.php b/Entities/Model/ResourceModel/Entities.php
index 8afcf6d..85864d1 100644
--- a/Entities/Model/ResourceModel/Entities.php
+++ b/Entities/Model/ResourceModel/Entities.php
@@ -375,7 +375,7 @@ public function matchEntity($tableName, $pimKey, $entityTable, $entityKey, $impo
public function setValues($tableName, $entityTable, $values, $entityTypeId, $storeId, $mode = 1)
{
$connection = $this->getConnection();
-
+
foreach ($values as $code => $value) {
if (($attribute = $this->getAttribute($code, $entityTypeId))) {
if ($attribute['backend_type'] !== 'static') {
@@ -396,13 +396,13 @@ public function setValues($tableName, $entityTable, $values, $entityTypeId, $sto
array(
'attribute_id' => new Expr($attribute['attribute_id']),
'store_id' => new Expr($storeId),
- $identifier => '_entity_id',
+ $identifier => '_' . $identifier,
'value' => $value,
)
);
if ($columnExists) {
- $select->where('`' . $columnName . '` <> ?', self::IGNORE_VALUE);
+ $select->where('TRIM(`' . $columnName . '`) <> ?', self::IGNORE_VALUE);
}
$insert = $connection->insertFromSelect(
@@ -421,7 +421,9 @@ public function setValues($tableName, $entityTable, $values, $entityTypeId, $sto
'value = ?' => '0000-00-00 00:00:00'
);
$connection->update(
- $this->getTable($entityTable . '_' . $backendType), $values, $where
+ $this->getTable($entityTable . '_' . $backendType),
+ $values,
+ $where
);
}
}
@@ -431,6 +433,53 @@ public function setValues($tableName, $entityTable, $values, $entityTypeId, $sto
return $this;
}
+ /**
+ * Update the values for an entity in all it's stages.
+ *
+ * @param $tableName
+ * @param $entityTable
+ * @param $entityTypeId
+ * @param $attributeCode
+ */
+ public function updateAllStageValues($tableName, $entityTable, $entityTypeId, $attributeCode, $joinCondition = 't._row_id != e.row_id')
+ {
+ $connection = $this->getConnection();
+
+ if (($attribute = $this->getAttribute($attributeCode, $entityTypeId))) {
+ if ($attribute['backend_type'] !== 'static') {
+ $backendType = $attribute['backend_type'];
+ $attributeTable = $connection->getTableName($entityTable . '_' . $backendType);
+
+ $select = $connection->select()
+ ->from(
+ ['e' =>$entityTable],
+ []
+ )->joinInner(
+ ['t' => $tableName],
+ "t._entity_id = e.entity_id AND $joinCondition",
+ []
+ )->joinInner(
+ ['u' => $attributeTable],
+ 'u.row_id = t._row_id AND attribute_id = ' . $attribute['attribute_id'],
+ []
+ );
+
+ $select->columns(['u.attribute_id', 'u.store_id', 'e.row_id', 'u.value']);
+
+ $select->setPart('disable_staging_preview', true);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $attributeTable,
+ array('attribute_id', 'store_id', 'row_id', 'value'),
+ 1
+ );
+
+ $connection->query($insert);
+ }
+ }
+ }
+
/**
* Copy column to an other
*
diff --git a/Product/Helper/Config.php b/Product/Helper/Config.php
index 0625a29..463cd67 100644
--- a/Product/Helper/Config.php
+++ b/Product/Helper/Config.php
@@ -5,26 +5,37 @@
use \Magento\Framework\App\Helper\AbstractHelper;
use \Magento\Framework\App\Helper\Context;
use \Magento\Store\Model\StoreManagerInterface;
-use \Magento\Framework\Filesystem;
+use \Pimgento\Staging\Helper\Config as StagingConfigHelper;
class Config extends AbstractHelper
{
+ /**
+ * Constants to configuration profile.
+ */
+ const CONFIG_PROFILE = 'product';
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
protected $_storeManager;
+ /**
+ * @var \Pimgento\Import\Helper\Config
+ */
+ protected $stagingConfigHelper;
+
/**
* @param \Magento\Framework\App\Helper\Context $context
* @param StoreManagerInterface $storeManager
*/
public function __construct(
Context $context,
- StoreManagerInterface $storeManager
- )
- {
+ StoreManagerInterface $storeManager,
+ StagingConfigHelper $statingConfigHelper
+ ) {
$this->_storeManager = $storeManager;
+ $this->stagingConfigHelper = $statingConfigHelper;
+
parent::__construct($context);
}
@@ -72,4 +83,24 @@ public function getDefaultWebsiteId()
return $this->_storeManager->getStore()->getWebsiteId();
}
+ /**
+ * Get import staging mode to use.
+ *
+ * @return mixed|string
+ */
+ public function getImportStagingMode()
+ {
+
+ return $this->stagingConfigHelper->getImportStagingMode(self::CONFIG_PROFILE);
+ }
+
+ /**
+ * Check if current configuration asks import to be in full staging mode or not.
+ *
+ * @return bool
+ */
+ public function isImportInFullStagingMode()
+ {
+ return $this->getImportStagingMode() == StagingConfigHelper::STAGING_MODE_FULL;
+ }
}
\ No newline at end of file
diff --git a/Product/Helper/Staging.php b/Product/Helper/Staging.php
new file mode 100644
index 0000000..9a42176
--- /dev/null
+++ b/Product/Helper/Staging.php
@@ -0,0 +1,321 @@
+stagingConfigHelper = $statingConfigHelper;
+ $this->stagingHelper = $stagingHelper;
+
+ parent::__construct($context);
+ }
+
+ /**
+ * Updates the created & updated in dates of configurable products with the ones of the simple products.
+ *
+ * @param Entities $entities
+ * @param $tmpTable
+ * @param $entityTableCode
+ * @param $code
+ * @param $stagingMode
+ */
+ public function updateConfigurableStages(Entities $entities, $tmpTable, $entityTableCode, $code, $stagingMode)
+ {
+ $connection = $entities->getResource()->getConnection();
+
+ $query = "
+ UPDATE $tmpTable tc, $tmpTable ts
+ SET tc.created_in = ts.created_in, tc.updated_in = ts.updated_in
+ WHERE tc._first_children = ts.sku
+ ";
+ $connection->query($query);
+ }
+
+ /**
+ * Duplicate values into multiple stages. This is necessary for the all mode when all stages are updated with
+ * the same values & also to duplicate the values properly for the initial stage that might be in multiple pieces.
+ *
+ * @param Entities $entities
+ * @param string $tmpTable
+ * @param string $condition
+ * @param string $dataTable
+ */
+ public function updateAllStageValues(
+ Entities $entities,
+ $tmpTable,
+ $condition = 't._row_id != e.row_id',
+ $dataTable = null
+ ) {
+ if (is_null($dataTable)) {
+ $dataTable = $tmpTable;
+ }
+
+ $connection = $entities->getResource()->getConnection();
+
+ $columns = array_keys($connection->describeTable($dataTable));
+ $column[] = 'options_container';
+ $column[] = 'tax_class_id';
+ $column[] = 'visibility';
+
+ $except = array(
+ '_entity_id',
+ '_is_new',
+ '_status',
+ '_type_id',
+ '_options_container',
+ '_tax_class_id',
+ '_attribute_set_id',
+ '_visibility',
+ '_children',
+ '_first_children',
+ '_axis',
+ 'sku',
+ 'categories',
+ 'family',
+ 'groups',
+ 'enabled',
+ 'created_in',
+ 'updated_in',
+ );
+
+ if ($connection->tableColumnExists($tmpTable, 'enabled')) {
+ $column[] = 'status';
+ }
+
+ foreach ($columns as $column) {
+ if (in_array($column, $except)) {
+ continue;
+ }
+
+ if (preg_match('/-unit/', $column)) {
+ continue;
+ }
+
+ $columnPrefix = explode('-', $column);
+ $columnPrefix = reset($columnPrefix);
+
+ $entities
+ ->getResource()
+ ->updateAllStageValues (
+ $tmpTable,
+ $connection->getTableName('catalog_product_entity'),
+ 4,
+ $columnPrefix,
+ $condition
+ );
+ }
+ }
+
+ /**
+ * Duplicate relations between products for all stages.
+ *
+ * @see updateAllStageValues for more information on why it's used.
+ *
+ * @param Entities $entities
+ * @param string $tmpTable
+ * @param string $joinCondition
+ */
+ public function updateAllStageRelations(Entities $entities, $tmpTable, $joinCondition = 't._row_id != e.row_id')
+ {
+ $connection = $entities->getResource()->getConnection();
+
+ $entityTable = $connection->getTableName('catalog_product_entity');
+ $linkTable = $connection->getTableName('catalog_product_link');
+
+ $select = $this->stagingHelper->getBaseStageDuplicationSelect($connection, $entityTable, $tmpTable, $joinCondition);
+ $select->joinInner(
+ ['u' => $linkTable],
+ 'u.product_id = t._row_id',
+ []
+ );
+
+ $select->columns(['e.row_id', 'u.linked_product_id', 'u.link_type_id']);
+
+ $select->setPart('disable_staging_preview', true);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $linkTable,
+ array('product_id', 'linked_product_id', 'link_type_id'),
+ 1
+ );
+ $connection->query($insert);
+ }
+
+ /**
+ * Duplicate relations for configurable products for all stages.
+ *
+ * @see updateAllStageValues for more information on why it's used.
+ *
+ * @param Entities $entities
+ * @param string $tmpTable
+ * @param string $joinCondition
+ */
+ public function updateAllStageConfigurables(Entities $entities, $tmpTable, $joinCondition = 't._row_id != e.row_id')
+ {
+ $connection = $entities->getResource()->getConnection();
+
+ $entityTable = $connection->getTableName('catalog_product_entity');
+
+ $attributeTable = $connection->getTableName('catalog_product_super_attribute');
+ $labelTable = $connection->getTableName('catalog_product_super_attribute_label');
+ $relationTable = $connection->getTableName('catalog_product_relation');
+ $linkTable = $connection->getTableName('catalog_product_super_link');
+
+ $baseSelect = $this->stagingHelper
+ ->getBaseStageDuplicationSelect($connection, $entityTable, $tmpTable, $joinCondition);
+
+ /**
+ * Duplicating Data in catalog_product_super_attribute
+ */
+ $select = clone $baseSelect;
+ $select->joinInner(
+ ['u' => $attributeTable],
+ 'u.product_id = t._row_id',
+ []
+ )->columns(['e.row_id', 'u.attribute_id', 'u.position']);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $attributeTable,
+ array('product_id', 'attribute_id', 'position'),
+ 1
+ );
+ $connection->query($insert);
+
+ /**
+ * Duplicating Data in catalog_product_super_attribute_label
+ */
+ $select = clone $baseSelect;
+ $select->joinInner(
+ ['u_new' => $attributeTable],
+ 'u_new.product_id = e.row_id',
+ []
+ )->joinInner(
+ ['u_source' => $attributeTable],
+ 'u_source.product_id = t._row_id',
+ []
+ )->joinInner(
+ ['l_source' => $labelTable],
+ 'l_source.product_super_attribute_id = u_source.product_super_attribute_id',
+ []
+ )->columns(['u_new.product_super_attribute_id', 'l_source.store_id', 'l_source.use_default', 'l_source.value']);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $labelTable,
+ array('product_super_attribute_id', 'store_id', 'use_default', 'value'),
+ 1
+ );
+ $connection->query($insert);
+
+ /**
+ * Duplicating Data in catalog_product_relation
+ */
+ $select = clone $baseSelect;
+ $select->joinInner(
+ ['u' => $relationTable],
+ 'u.parent_id = t._row_id',
+ []
+ )->columns(['e.row_id', 'u.child_id']);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $relationTable,
+ array('parent_id', 'child_id'),
+ 1
+ );
+ $connection->query($insert);
+ /**
+ * Duplicating Data in catalog_product_super_link
+ */
+ $select = clone $baseSelect;
+ $select->joinInner(
+ ['u' => $linkTable],
+ 'u.parent_id = t._row_id',
+ []
+ )->columns(['e.row_id', 'u.product_id']);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $linkTable,
+ array('parent_id', 'product_id'),
+ 1
+ );
+ $connection->query($insert);
+ }
+
+ /**
+ * Duplicate medias between products for all stages.
+ *
+ * @see updateAllStageValues for more information on why it's used.
+ *
+ * @param Entities $entities
+ * @param string $tmpTable
+ * @param string $joinCondition
+ */
+ public function updateAllStageMedias(Entities $entities, $tmpTable, $joinCondition = 't._row_id != e.row_id')
+ {
+ $connection = $entities->getResource()->getConnection();
+
+ $entityTable = $connection->getTableName('catalog_product_entity');
+ $mediaTable = $connection->getTableName('catalog_product_entity_media_gallery_value_to_entity');
+
+ $select = $this->stagingHelper
+ ->getBaseStageDuplicationSelect($connection, $entityTable, $tmpTable, $joinCondition);
+ $select->joinInner(
+ ['u' => $mediaTable],
+ 'u.row_id = t._row_id',
+ []
+ );
+
+ $select->columns(['u.value_id', 'e.row_id']);
+
+ $select->setPart('disable_staging_preview', true);
+
+ $insert = $connection->insertFromSelect(
+ $select,
+ $mediaTable,
+ array('value_id', 'row_id'),
+ 1
+ );
+ $connection->query($insert);
+ }
+}
\ No newline at end of file
diff --git a/Product/Model/Factory/Import.php b/Product/Model/Factory/Import.php
index d41bb06..cb398ff 100755
--- a/Product/Model/Factory/Import.php
+++ b/Product/Model/Factory/Import.php
@@ -6,6 +6,9 @@
use \Pimgento\Entities\Model\Entities;
use \Pimgento\Import\Helper\Config as helperConfig;
use \Pimgento\Import\Helper\UrlRewrite as urlRewriteHelper;
+use \Pimgento\Staging\Helper\Config as StagingConfigHelper;
+use \Pimgento\Product\Helper\Staging as StagingProductHelper;
+use \Pimgento\Staging\Helper\Import as StagingHelper;
use \Pimgento\Product\Helper\Config as productHelper;
use \Pimgento\Product\Helper\Media as mediaHelper;
use \Pimgento\Product\Model\Factory\Import\Related;
@@ -23,6 +26,12 @@
class Import extends Factory
{
+ /**
+ * Column names for the staging support.
+ */
+ const COLUMN_STAGING_FROM = 'from';
+ const COLUMN_STAGING_TO = 'to';
+
/**
* @var Entities
*/
@@ -59,6 +68,21 @@ class Import extends Factory
*/
protected $_urlRewriteHelper;
+ /**
+ * @var StagingConfigHelper
+ */
+ protected $stagingConfigHelper;
+
+ /**
+ * @var StagingHelper
+ */
+ protected $stagingHelper;
+
+ /**
+ * @var StagingProductHelper
+ */
+ protected $stagingProductHelper;
+
/**
* @var Media $_media
*/
@@ -90,6 +114,9 @@ class Import extends Factory
* @param Related $related
* @param Media $media
* @param Product $product
+ * @param StagingConfigHelper $stagingConfigHelper
+ * @param StagingHelper $stagingHelper
+ * @param StagingProductHelper $stagingProductHelper
* @param array $data
*/
public function __construct(
@@ -103,6 +130,9 @@ public function __construct(
productHelper $productHelper,
mediaHelper $mediaHelper,
urlRewriteHelper $urlRewriteHelper,
+ StagingConfigHelper $stagingConfigHelper,
+ StagingHelper $stagingHelper,
+ StagingProductHelper $stagingProductHelper,
Related $related,
Media $media,
Product $product,
@@ -116,6 +146,9 @@ public function __construct(
$this->_productHelper = $productHelper;
$this->_mediaHelper = $mediaHelper;
$this->_urlRewriteHelper = $urlRewriteHelper;
+ $this->stagingConfigHelper = $stagingConfigHelper;
+ $this->stagingHelper = $stagingHelper;
+ $this->stagingProductHelper = $stagingProductHelper;
$this->_related = $related;
$this->_media = $media;
$this->_product = $product;
@@ -132,9 +165,16 @@ public function createTable()
$this->setContinue(false);
$this->setStatus(false);
$this->setMessage($this->getFileNotFoundErrorMessage());
-
} else {
- $this->_entities->createTmpTableFromFile($file, $this->getCode(), array('sku'));
+ $requiredColumns = ['sku'];
+
+ // If full mode is activated we need aditional columns.
+ if ($this->_productHelper->isImportInFullStagingMode()) {
+ $requiredColumns[] = self::COLUMN_STAGING_FROM;
+ $requiredColumns[] = self::COLUMN_STAGING_TO;
+ }
+
+ $this->_entities->createTmpTableFromFile($file, $this->getCode(), $requiredColumns);
}
}
@@ -180,6 +220,8 @@ public function addRequiredData()
$connection->update($tmpTable, array('_visibility' => new Expr('IF(`groups` <> "", 1, 4)')));
}
+ $this->stagingHelper->addRequiredData($connection, $tmpTable);
+
if ($connection->tableColumnExists($tmpTable, 'type_id')) {
$types = $connection->quote($this->_allowedTypeId);
$connection->update(
@@ -217,6 +259,40 @@ public function addRequiredData()
}
}
+ /**
+ * When the stage module is enable and we are on full staging mode the csv file may contain the start & end dates
+ * of versions. Transform those dates to timestamp & check that they are valid.
+ */
+ public function checkStageDates()
+ {
+ if ($this->_productHelper->isImportInFullStagingMode()) {
+
+ $connection = $this->_entities->getResource()->getConnection();
+ $tmpTable = $this->_entities->getTableName($this->getCode());
+
+ // First let's transform all the from & to dates to timestamps.
+ $this->stagingHelper->updateDates(
+ $connection,
+ $tmpTable,
+ [self::COLUMN_STAGING_FROM => 'created_in', self::COLUMN_STAGING_TO => 'updated_in']
+ );
+
+ $errorMsg = $this->stagingHelper->checkStageDates(
+ $connection,
+ $tmpTable,
+ $connection->getTableName('catalog_product_entity'),
+ 'sku'
+ );
+ if ($errorMsg) {
+ $this->setContinue(false);
+ $this->setStatus(false);
+ $this->setMessage($errorMsg);
+
+ return;
+ }
+ }
+ }
+
/**
* Create Configurable products
*/
@@ -238,12 +314,14 @@ public function createConfigurable()
);
} else {
$connection->addColumn($tmpTable, '_children', 'TEXT NULL');
+ $connection->addColumn($tmpTable, '_first_children', 'VARCHAR(255) NULL');
$connection->addColumn($tmpTable, '_axis', 'VARCHAR(255) NULL');
$data = array(
'sku' => 'e.groups',
'url_key' => 'e.groups',
'_children' => new Expr('GROUP_CONCAT(e.sku SEPARATOR ",")'),
+ '_first_children' => new Expr('COALESCE(e.sku)'),
'_type_id' => new Expr('"configurable"'),
'_options_container' => new Expr('"container1"'),
'_status' => 'e._status',
@@ -327,11 +405,40 @@ public function createConfigurable()
}
/**
- * Match code with entity
+ * Match code with entity, and if staging then also match with row id and create necessery duplications.
*/
public function matchEntity()
{
- $this->_entities->matchEntity($this->getCode(), 'sku', 'catalog_product_entity', 'entity_id');
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ /**
+ * When using staging module entity id's are not the primary key of the catalog_product_entity
+ * table anymore. The new primary keys is row_id. Before we get information on the row_id, we still
+ * need to get the entiy_id of the products to be imported. We are therefore going to use a different
+ * table built for this purpose in magento.
+ */
+ $this->_entities->matchEntity($this->getCode(), 'sku', 'sequence_product', 'sequence_value');
+ $tmpTable = $this->_entities->getTableName($this->getCode());
+
+ // We need to update the created/updated in values of the configurables.
+ $this->stagingProductHelper->updateConfigurableStages(
+ $this->_entities,
+ $tmpTable,
+ 'catalog_product_entity',
+ $this->getCode(),
+ $this->_productHelper->getImportStagingMode()
+ );
+
+ // Once the entiy id's are matched we can match the row ids.
+ $this->stagingHelper->matchEntityRows(
+ $this->_entities,
+ 'catalog_product_entity',
+ $this->getCode(),
+ $this->_productHelper->getImportStagingMode()
+ );
+
+ } else {
+ $this->_entities->matchEntity($this->getCode(), 'sku', 'catalog_product_entity', 'entity_id');
+ }
}
/**
@@ -403,6 +510,7 @@ public function updateOption()
'_attribute_set_id',
'_visibility',
'_children',
+ '_first_children',
'_axis',
'sku',
'categories',
@@ -410,6 +518,9 @@ public function updateOption()
'groups',
'url_key',
'enabled',
+ // columns to handle staging.
+ 'created_in',
+ 'updated_in'
);
foreach ($columns as $column) {
@@ -454,6 +565,7 @@ public function updateOption()
'entity_id' => 'p._entity_id'
)
)
+ ->distinct()
->joinInner(
array('c1' => new Expr('('.(string) $subSelect.')')),
new Expr($conditionJoin),
@@ -503,12 +615,14 @@ public function createEntities()
$table = $resource->getTable('catalog_product_entity');
- $columnIdentifier = $this->_entities->getColumnIdentifier($table);
-
- if ($columnIdentifier == 'row_id') {
- $values['row_id'] = '_entity_id';
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ $values['created_in'] = 'created_in';
+ $values['updated_in'] = 'updated_in';
+ $values['row_id'] = '_row_id';
}
+ $this->stagingHelper->createEntitiesBefore($connection, 'sequence_product', $tmpTable);
+
$parents = $connection->select()->from($tmpTable, $values);
$connection->query(
$connection->insertFromSelect(
@@ -516,12 +630,19 @@ public function createEntities()
)
);
+ $this->stagingHelper->createEntitiesAfter(
+ $connection,
+ 'catalog_product_entity',
+ $tmpTable,
+ $this->_productHelper->getImportStagingMode()
+ );
+
$values = array(
'created_at' => new Expr('now()')
);
$connection->update($table, $values, 'created_at IS NULL');
- if ($columnIdentifier == 'row_id') {
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
$values = [
'created_in' => new Expr(1),
'updated_in' => new Expr(VersionManager::MAX_VERSION),
@@ -560,12 +681,15 @@ public function setValues()
'_attribute_set_id',
'_visibility',
'_children',
+ '_first_children',
'_axis',
'sku',
'categories',
'family',
'groups',
'enabled',
+ 'created_in',
+ 'updated_in',
);
$values = array(
@@ -599,6 +723,8 @@ public function setValues()
$columnPrefix = explode('-', $column);
$columnPrefix = reset($columnPrefix);
+ $values[0][$columnPrefix] = $column;
+
foreach ($stores as $suffix => $affected) {
if (preg_match('/^' . $columnPrefix . '-' . $suffix . '$/', $column)) {
foreach ($affected as $store) {
@@ -631,6 +757,8 @@ public function linkConfigurable()
$connection = $resource->getConnection();
$tmpTable = $this->_entities->getTableName($this->getCode());
+ $identifierAttribute = $this->stagingConfigHelper->isCatalogStagingModulesEnabled() ? '_row_id' : '_entity_id';
+
if (!$this->moduleIsEnabled('Pimgento_Variant')) {
$this->setStatus(false);
$this->setMessage(
@@ -649,7 +777,7 @@ public function linkConfigurable()
->from(
$tmpTable,
array(
- '_entity_id',
+ $identifierAttribute,
'_axis',
'_children'
)
@@ -686,7 +814,7 @@ public function linkConfigurable()
/* catalog_product_super_attribute */
$values = array(
- 'product_id' => $row['_entity_id'],
+ 'product_id' => $row[$identifierAttribute],
'attribute_id' => $id,
'position' => $position++,
);
@@ -699,7 +827,7 @@ public function linkConfigurable()
$connection->select()
->from($resource->getTable('catalog_product_super_attribute'))
->where('attribute_id = ?', $id)
- ->where('product_id = ?', $row['_entity_id'])
+ ->where('product_id = ?', $row[$identifierAttribute])
->limit(1)
);
@@ -731,14 +859,14 @@ public function linkConfigurable()
if ($childId) {
/* catalog_product_relation */
$valuesRelations[] = array(
- 'parent_id' => $row['_entity_id'],
+ 'parent_id' => $row[$identifierAttribute],
'child_id' => $childId,
);
/* catalog_product_super_link */
$valuesSuperLink[] = array(
'product_id' => $childId,
- 'parent_id' => $row['_entity_id'],
+ 'parent_id' => $row[$identifierAttribute],
);
}
}
@@ -851,7 +979,8 @@ public function setCategories()
'FIND_IN_SET(`c`.`code`, `p`.`categories`) AND `c`.`import` = "category"',
array(
'category_id' => 'c.entity_id',
- 'product_id' => 'p._entity_id'
+ 'product_id' => 'p._entity_id',
+ 'position' => new Expr(1)
)
)
->joinInner(
@@ -864,7 +993,7 @@ public function setCategories()
$connection->insertFromSelect(
$select,
$resource->getTable('catalog_category_product'),
- array('category_id', 'product_id'),
+ array('category_id', 'product_id', 'position'),
1
)
);
@@ -1007,12 +1136,57 @@ public function setUrlRewrite()
$this->_urlRewriteHelper->dropUrlRewriteTmpTable();
}
+ /**
+ * Duplicate imported information in all the stages of the product.
+ */
+ public function updateAllStages()
+ {
+
+ $tmpTable = $this->_entities->getTableName($this->getCode());
+ if ($this->_productHelper->getImportStagingMode() == StagingConfigHelper::STAGING_MODE_ALL) {
+ /**
+ * In this mode pimgento don't really cares about the stage it updates because it simply updates all
+ * stages
+ */
+ $this->stagingProductHelper->updateAllStageValues($this->_entities, $tmpTable);
+
+ $this->stagingProductHelper->updateAllStageRelations($this->_entities, $tmpTable);
+
+ $this->stagingProductHelper->updateAllStageConfigurables($this->_entities, $tmpTable);
+
+ $this->stagingProductHelper->updateAllStageMedias($this->_entities, $tmpTable);
+
+ } elseif ($this->_productHelper->getImportStagingMode() == StagingConfigHelper::STAGING_MODE_FULL) {
+
+ /**
+ * In this mode pimgento will need to duplicate some information. It needs to do this because a new version
+ * has been created and the old version was split in 2 parts that needs to have the same data.
+ */
+ $duplicateTmpTable = $this->_entities->getResource()->getConnection()->getTableName('tmp_pimgento_entity_stage_duplicate');
+ $condition = 't.created_in = e.created_in';
+
+ $this->stagingProductHelper
+ ->updateAllStageValues($this->_entities, $duplicateTmpTable, $condition, $tmpTable);
+
+ $this->stagingProductHelper
+ ->updateAllStageRelations($this->_entities, $duplicateTmpTable, $condition);
+
+ $this->stagingProductHelper
+ ->updateAllStageConfigurables($this->_entities, $duplicateTmpTable, $condition);
+
+ $this->stagingProductHelper
+ ->updateAllStageMedias($this->_entities, $tmpTable, $condition);
+ }
+ }
+
/**
* Drop temporary table
*/
public function dropTable()
{
$this->_entities->dropTable($this->getCode());
+
+ $this->stagingHelper->dropTemporaryStageTable($this->_entities->getResource()->getConnection());
}
/**
@@ -1151,4 +1325,4 @@ public function importMedia()
$this->_media->mediaDropTmpTables();
}
}
-}
+}
\ No newline at end of file
diff --git a/Product/Model/Factory/Import/Media.php b/Product/Model/Factory/Import/Media.php
index 1abbedf..9b7c844 100644
--- a/Product/Model/Factory/Import/Media.php
+++ b/Product/Model/Factory/Import/Media.php
@@ -6,6 +6,7 @@
use \Pimgento\Entities\Model\Entities;
use \Pimgento\Import\Helper\Config as helperConfig;
use \Pimgento\Product\Helper\Media as mediaHelper;
+use \Pimgento\Staging\Helper\Config as StagingConfigHelper;
use \Magento\Framework\Event\ManagerInterface;
use \Magento\Framework\Module\Manager as moduleManager;
use \Magento\Framework\App\Config\ScopeConfigInterface as scopeConfig;
@@ -32,6 +33,11 @@ class Media extends Factory
*/
protected $image;
+ /**
+ * @var StagingConfigHelper
+ */
+ protected $stagingConfigHelper;
+
/**
* PHP Constructor
*
@@ -52,6 +58,7 @@ public function __construct(
Entities $entities,
mediaHelper $mediaHelper,
Image $image,
+ StagingConfigHelper $stagingConfigHelper,
array $data = []
) {
parent::__construct($helperConfig, $eventManager, $moduleManager, $scopeConfig, $data);
@@ -59,6 +66,7 @@ public function __construct(
$this->_entities = $entities;
$this->_mediaHelper = $mediaHelper;
$this->image = $image;
+ $this->stagingConfigHelper = $stagingConfigHelper;
}
/**
@@ -98,6 +106,11 @@ public function mediaCreateTmpTables()
$table->addColumn('media_cleaned', Table::TYPE_TEXT, 255, []);
$table->addColumn('media_value', Table::TYPE_TEXT, 255, []);
$table->addColumn('position', Table::TYPE_INTEGER, 10, ['unsigned' => true]);
+
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ $table->addColumn('row_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 10, ['unsigned' => true]);
+ }
+
$table->addIndex(
$tableMedia.'_entity_id',
['entity_id'],
@@ -164,23 +177,29 @@ public function mediaPrepareValues($column, $attributeId, $position)
$attributeId = 'NULL';
}
+ $cols = [
+ 'sku' => 't.sku',
+ 'entity_id' => 't._entity_id',
+ 'attribute_id' => new Expr($attributeId),
+ 'store_id' => new Expr('0'),
+ 'media_original' => "t.$column",
+ 'position' => new Expr($position)
+ ];
+
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ $cols['row_id'] = 't._row_id';
+ }
+
$select = $connection->select()
->from(
['t' => $tmpTable],
- [
- 'sku' => 't.sku',
- 'entity_id' => 't._entity_id',
- 'attribute_id' => new Expr($attributeId),
- 'store_id' => new Expr('0'),
- 'media_original' => "t.$column",
- 'position' => new Expr($position)
- ]
+ $cols
)->where("`t`.`$column` <> ''");
$query = $connection->insertFromSelect(
$select,
$tableMedia,
- ['sku', 'entity_id', 'attribute_id', 'store_id', 'media_original', 'position'],
+ array_keys($cols),
AdapterInterface::INSERT_ON_DUPLICATE
);
@@ -364,27 +383,32 @@ public function mediaUpdateDataBase()
$tableMedia = $this->_entities->getTableName('media');
$step = 5000;
+ // add the media in the varchar product table
+ $cols = [
+ 'attribute_id' => 'attribute_id',
+ 'store_id' => 'store_id',
+ 'value' => 'media_value',
+ ];
+
+ if ($this->stagingConfigHelper->isCatalogStagingModulesEnabled()) {
+ $cols['row_id'] = 'row_id';
+ $identifier = 'row_id';
+ } else {
+ $cols['entity_id'] = 'entity_id';
+ $identifier = 'entity_id';
+ }
+
// add the media in the varchar product table
$select = $connection->select()
->from(
['t' => $tableMedia],
- [
- 'attribute_id' => 'attribute_id',
- 'store_id' => 'store_id',
- 'entity_id' => 'entity_id',
- 'value' => 'media_value',
- ]
+ $cols
)
->where('t.attribute_id is not null');
-
- $identifier = $this->_entities->getColumnIdentifier(
- $resource->getTable('catalog_product_entity_varchar')
- );
-
$query = $connection->insertFromSelect(
$select,
$resource->getTable('catalog_product_entity_varchar'),
- ['attribute_id', 'store_id', $identifier, 'value'],
+ array_keys($cols),
AdapterInterface::INSERT_ON_DUPLICATE
);
$connection->query($query);
@@ -448,8 +472,6 @@ public function mediaUpdateDataBase()
// working on "media gallery value"
$tableGallery = $resource->getTable('catalog_product_entity_media_gallery_value');
- $identifier = $this->_entities->getColumnIdentifier($tableGallery);
-
// get the record id from gallery value (for new medias)
$maxId = $this->mediaGetMaxId($tableGallery, 'record_id');
for ($k=1; $k<=$maxId; $k+= $step) {
@@ -474,7 +496,7 @@ public function mediaUpdateDataBase()
[
'value_id' => 'value_id',
'store_id' => new Expr('0'),
- $identifier => 'entity_id',
+ $identifier => $identifier,
'label' => new Expr("''"),
'position' => 'position',
]
@@ -500,7 +522,7 @@ public function mediaUpdateDataBase()
['t' => $tableMedia],
[
'value_id' => 'value_id',
- $identifier => 'entity_id',
+ $identifier => $identifier,
]
);
$query = $connection->insertFromSelect(
diff --git a/Product/Model/Factory/Import/Related.php b/Product/Model/Factory/Import/Related.php
index a3c9e16..10b8bb5 100644
--- a/Product/Model/Factory/Import/Related.php
+++ b/Product/Model/Factory/Import/Related.php
@@ -151,7 +151,6 @@ public function relatedImportColumn($type)
);
}
- // Get the product ids for parents
$query = $connection->select()
->from(false, ['parent_id' => 'p.' . $this->_entities->getColumnIdentifier($tableProduct)])
->joinLeft(
@@ -159,7 +158,6 @@ public function relatedImportColumn($type)
'r.parent_sku = p.sku',
[]
);
-
$connection->query(
$connection->updateFromSelect($query, ['r' => $tableRelated])
);
diff --git a/Product/Observer/AddPimgentoImportObserver.php b/Product/Observer/AddPimgentoImportObserver.php
index 1d01bae..4742547 100644
--- a/Product/Observer/AddPimgentoImportObserver.php
+++ b/Product/Observer/AddPimgentoImportObserver.php
@@ -67,6 +67,10 @@ protected function getStepsDefinition()
'comment' => __('Add product required data'),
'method' => 'addRequiredData',
),
+ array(
+ 'comment' => __('Match code with Magento ID'),
+ 'method' => 'checkStageDates',
+ ),
array(
'comment' => __('Create configurable product'),
'method' => 'createConfigurable',
@@ -122,6 +126,11 @@ protected function getStepsDefinition()
'comment' => __('Import media files'),
'method' => 'importMedia',
),
+ /* Step that will work only if staging enabled & certain options are selected. */
+ array(
+ 'comment' => __('Update stages'),
+ 'method' => 'updateAllStages',
+ ),
);
$stepsAfter = array(
@@ -147,4 +156,4 @@ protected function getStepsDefinition()
$stepsAfter
);
}
-}
+}
\ No newline at end of file
diff --git a/Product/etc/adminhtml/system.xml b/Product/etc/adminhtml/system.xml
index de75ed2..4a49944 100644
--- a/Product/etc/adminhtml/system.xml
+++ b/Product/etc/adminhtml/system.xml
@@ -21,6 +21,11 @@
Magento\Config\Model\Config\Backend\Serialized\ArraySerialized
Fill configurable attributes with default value, leave blank to take simple product value or variant value if exists
+
+
+ Pimgento\Staging\Model\Source\StagingMode
+ In full mode the product import expects version_from, version_to columns
+
diff --git a/Product/etc/config.xml b/Product/etc/config.xml
index 6d20429..84c308b 100644
--- a/Product/etc/config.xml
+++ b/Product/etc/config.xml
@@ -5,6 +5,7 @@
a:1:{s:18:"_1459928423869_869";a:2:{s:13:"pim_attribute";s:11:"description";s:17:"magento_attribute";s:17:"short_description";}}
a:2:{s:18:"_1464857349718_718";a:2:{s:9:"attribute";s:11:"description";s:5:"value";s:0:"";}s:18:"_1464857373248_248";a:2:{s:9:"attribute";s:4:"name";s:5:"value";s:0:"";}}
+ simple
0
diff --git a/Product/etc/module.xml b/Product/etc/module.xml
index bbb28cf..bf15b5c 100644
--- a/Product/etc/module.xml
+++ b/Product/etc/module.xml
@@ -4,6 +4,7 @@
+
\ No newline at end of file
diff --git a/Product/i18n/fr_FR.csv b/Product/i18n/fr_FR.csv
index 4504dfb..b24ad4d 100644
--- a/Product/i18n/fr_FR.csv
+++ b/Product/i18n/fr_FR.csv
@@ -43,3 +43,5 @@
"Swatch image column code","Code de la colonne pour l'image Swatch"
"Gallery image columns code","Code des colonnes pour l'image Gallery"
"Move files","Déplacer les fichiers"
+"Staging Mode","Mode de Mise en scène"
+"In full mode the product import expects version_from, version_to columns","En mode complet l'import produit s'attend aux colonnes version_from, version_to"
\ No newline at end of file
diff --git a/Staging/Helper/Config.php b/Staging/Helper/Config.php
new file mode 100644
index 0000000..e361217
--- /dev/null
+++ b/Staging/Helper/Config.php
@@ -0,0 +1,77 @@
+
+ * @copyright 2017 Smile
+ * @package Pimgento\Staging\Helper
+ */
+class Config extends AbstractHelper
+{
+ /**
+ * Constants to configuration paths.
+ */
+ const CONFIG_PATH_STAGING_MODE = 'pimgento/%s/staging_mode';
+
+ /**
+ * Constants for different staging modes.
+ */
+ const STAGING_MODE_DISABLED = 'disabled';
+ const STAGING_MODE_LAST = 'last';
+ const STAGING_MODE_CURRENT = 'current';
+ const STAGING_MODE_ALL = 'all';
+ const STAGING_MODE_FULL = 'full';
+
+ /**
+ * @var \Magento\Framework\Module\Manager
+ */
+ protected $moduleManager;
+
+ /**
+ * Constructor
+ *
+ * @param Context $context
+ * @param \Magento\Framework\Module\Manager $moduleManager
+ */
+ public function __construct(
+ Context $context,
+ \Magento\Framework\Module\Manager $moduleManager
+ ) {
+ $this->moduleManager = $moduleManager;
+
+ parent::__construct($context);
+ }
+
+ /**
+ * Check if staging module is enabled for the catalog.
+ *
+ * @return bool
+ */
+ public function isCatalogStagingModulesEnabled()
+ {
+ return $this->moduleManager->isEnabled('Magento_Staging')
+ && $this->moduleManager->isEnabled('Magento_CatalogStaging');
+ }
+
+ /**
+ * Get import staging mode to use.
+ *
+ * @return mixed|string
+ */
+ public function getImportStagingMode($profile)
+ {
+
+ if (!$this->isCatalogStagingModulesEnabled()) {
+ return self::STAGING_MODE_DISABLED;
+ } else {
+ return $this->scopeConfig->getValue(sprintf(self::CONFIG_PATH_STAGING_MODE, $profile));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Staging/Helper/Import.php b/Staging/Helper/Import.php
new file mode 100644
index 0000000..1e5962e
--- /dev/null
+++ b/Staging/Helper/Import.php
@@ -0,0 +1,657 @@
+
+ * @copyright 2017 Smile
+ * @package Pimgento\Staging\Helper
+ */
+class Import extends AbstractHelper
+{
+ /**
+ * @var Config
+ */
+ protected $configHelper;
+
+ /**
+ * @param \Magento\Framework\App\Helper\Context $context
+ * @param Config $configHelper
+ */
+ public function __construct(Context $context, \Pimgento\Staging\Helper\Config $configHelper)
+ {
+ $this->configHelper = $configHelper;
+
+ parent::__construct($context);
+ }
+
+ /**
+ * Add required data columns on the temporary table.
+ *
+ * @param AdapterInterface $connection
+ * @param string $tmpTable
+ */
+ public function addRequiredData(AdapterInterface $connection, $tmpTable)
+ {
+ if ($this->configHelper->isCatalogStagingModulesEnabled()) {
+ $connection->addColumn($tmpTable, '_row_id', 'INT(11)');
+ $connection->addColumn($tmpTable, 'created_in', 'INT(11)');
+ $connection->addColumn($tmpTable, 'updated_in', 'INT(11)');
+ $connection->addColumn($tmpTable, '_new_entity', 'SMALLINT(1) DEFAULT 0');
+ }
+ }
+
+ /**
+ * Update dates in the temporary table with unix timestamp.
+ *
+ * @param AdapterInterface $connection
+ * @param $tmpTable
+ * @param $columns
+ */
+ public function updateDates(AdapterInterface $connection, $tmpTable, $columns)
+ {
+ $set = [];
+ foreach ($columns as $origin => $to) {
+ $set[] = "$to = UNIX_TIMESTAMP(STR_TO_DATE(t.$origin,'%Y-%m-%d'))";
+ }
+
+ $query = "UPDATE $tmpTable t
+ SET " . implode(', ', $set) . "
+ ";
+ $connection->query($query);
+
+ // Drop the original data not to have to bother with it.
+ foreach ($columns as $origin => $to) {
+ $connection->dropColumn($tmpTable, $origin);
+ }
+
+ // Put default values for now for products without version specification.
+ $connection->query("UPDATE $tmpTable t SET created_in = 1 WHERE created_in = 0");
+ $connection->query(
+ "UPDATE $tmpTable t SET updated_in = " . VersionManager::MAX_VERSION . " WHERE updated_in = 0"
+ );
+ }
+
+ /**
+ * Check the dates that were given in the csv file to be sure they can be imported.
+ *
+ * The function returns the error message if there is one, and null if there isn't one.
+ *
+ * @param AdapterInterface $connection
+ * @param $tmpTable
+ * @param $identifier
+ * @return null|string
+ */
+ public function checkStageDates(AdapterInterface $connection, $tmpTable, $entityTable, $identifier)
+ {
+ /**
+ * Check that only one stage of the product is being imported.
+ */
+ $select = $connection
+ ->select()
+ ->from($tmpTable, new Expr('count(*) as nb'))
+ ->group('sku')
+ ->having('nb > 1');
+ $nb = $connection->fetchOne($select);
+
+ if ($nb > 1) {
+ return "You can't import 2 stages of the same product at the same time!";
+ }
+
+ /**
+ * Check for incoherence in the created in and updated_in values.
+ */
+ $select = $connection
+ ->select()
+ ->from($tmpTable, $identifier)
+ ->where('created_in >= updated_in AND updated_in != 0');
+
+ $invalidId = $connection->fetchOne($select);
+ if ($invalidId) {
+ return "Error with '$invalidId' : 'from' date superior or equal to it's 'to' date !";
+ }
+
+ /**
+ * Check that all version to be imported are for future use as we can't modify older versions.
+ */
+ $select = $connection
+ ->select()
+ ->from($tmpTable, $identifier)
+ ->where('created_in < ' . time() . ' AND created_in > 1')
+ ->limit(1);
+
+ $invalidId = $connection->fetchOne($select);
+ if ($invalidId) {
+ return "Error with '$invalidId' : Trying to update an on going or past stage !";
+ }
+
+ /**
+ * Check that for each from value we have a single to value.
+ */
+ $select = $connection
+ ->select()
+ ->from($tmpTable, new Expr('count(DISTINCT updated_in) as nb'))
+ ->where('created_in != 0')
+ ->group('created_in')
+ ->having('nb > 1');
+ $nb = $connection->fetchOne($select);
+
+ if ($nb > 1) {
+ return "Error you can't have 2 stage starting at the same time !";
+ }
+
+ /**
+ * Check If all simple products of configurables have same dates.
+ */
+ if ($connection->tableColumnExists($tmpTable, 'groups')) {
+ $select = $connection
+ ->select()
+ ->from($tmpTable, ['groups'])
+ ->where('groups != ""')
+ ->group('groups')
+ ->having('count(DISTINCT created_in) > 1');
+
+ $nb = $connection->fetchOne($select);
+
+ if ($nb > 1) {
+ return "You can't have different stages for the products of the same configurable !";
+ }
+ }
+
+ /*
+ * Check if created & updated dates are also usable with actual database. In order to do this we check for each
+ * product that we import if they alread
+ */
+ $select = $connection
+ ->select()
+ ->from(['t' => $tmpTable], $identifier)
+ ->joinInner(
+ ['e' => $entityTable],
+ 'e.'. $identifier . ' = t. ' . $identifier,
+ []
+ )
+ ->joinInner(
+ ['u' => $connection->getTableName('staging_update')],
+ 'u.id = e.created_in',
+ []
+ )
+ ->where('u.is_rollback IS NULL')
+ ->where(
+ '(u.id < t.created_in AND (u.rollback_id > t.created_in OR u.rollback_id IS NULL))
+ OR (u.id < t.updated_in AND (u.rollback_id > t.updated_in OR u.rollback_id IS NULL))
+ OR ((u.id BETWEEN t.created_in AND t.updated_in) AND u.id != t.created_in AND u.id != t.updated_in)
+ OR (u.rollback_id IS NOT NULL
+ AND (u.rollback_id BETWEEN t.created_in AND t.updated_in)
+ AND u.rollback_id != t.created_in AND u.rollback_id != t.updated_in
+ )'
+ );
+ $select->setPart('disable_staging_preview', true);
+
+ $invalidId = $connection->fetchOne($select);
+ if ($invalidId) {
+ return "Error with '$invalidId' : 'from' and 'to' dates intersects with existing stages !";
+ }
+
+ return null;
+ }
+
+ /**
+ * Matching the row id's for all our entities using different rules depending on the staging mode.
+ *
+ * @param Entities $entities
+ * @param string $entityTableCode
+ * @param string $code
+ * @param string $stagingMode
+ */
+ public function matchEntityRows(Entities $entities, $entityTableCode, $code, $stagingMode)
+ {
+ $connection = $entities->getResource()->getConnection();
+ $tmpTable = $entities->getTableName($code);
+ $entityTable = $entities->getResource()->getTable($entityTableCode);
+
+ $startTime = time();
+
+ switch ($stagingMode) {
+ case Config::STAGING_MODE_LAST:
+ case Config::STAGING_MODE_ALL:
+ /**
+ * Update row id column.
+ * We are going to update the last version that was created if there is multiple versions
+ *
+ * In full mode we will have another step that will duplicate the content in all the stages.
+ */
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `_row_id` = (
+ SELECT MAX(row_id) FROM `' . $entityTable . '` c
+ WHERE c.entity_id = t._entity_id AND updated_in = ' . VersionManager::MAX_VERSION . '
+ )'
+ );
+
+ break;
+
+ case Config::STAGING_MODE_CURRENT:
+ /**
+ * Update row id column.
+ * We are going to update the current versions.
+ */
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `_row_id` = (
+ SELECT MAX(row_id) FROM `' . $entityTable . '` c
+ WHERE c.entity_id = t._entity_id
+ AND ( ' . $startTime . ' BETWEEN created_in AND updated_in)
+ )'
+ );
+
+ break;
+
+ case Config::STAGING_MODE_FULL:
+
+ $this->createTemporaryStageTable($connection);
+
+ /**
+ * Update row id column with simple rule of getting the rows that can just be updated.
+ */
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `_row_id` = (
+ SELECT MAX(row_id) FROM `' . $entityTable . '` c
+ WHERE c.entity_id = t._entity_id
+ AND c.created_in = t.created_in
+ AND c.updated_in = t.updated_in
+ )'
+ );
+
+ /**
+ * For all remaining products, either we are importing a new product or we are creating
+ * a new stage to an existing product.
+ */
+
+ // First let's handle new products. Simply change created/updated values to default. There is no need
+ // to create multiple versions.
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `created_in` = 1,
+ `updated_in` = ' . VersionManager::MAX_VERSION . ',
+ `_new_entity` = 1
+ WHERE t._entity_id NOT IN (SELECT entity_id FROM ' . $entityTable . ')'
+ );
+
+ // Let's handle all the products that needs to be updated.
+ $this->setStageValues($connection, $tmpTable, $entityTable);
+ }
+
+
+ /**
+ * For existing versions fetch version created_in & updated_in from database.
+ */
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ INNER JOIN `' . $entityTable . '` c ON c.row_id = t._row_id
+ SET t.created_in = c.created_in,
+ t.updated_in = c.updated_in
+ WHERE t.created_in is NULL'
+ );
+
+ /**
+ * For new entities we need to put default created_in & updated_in values.
+ */
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `created_in` = 1,
+ `updated_in` = ' . VersionManager::MAX_VERSION . '
+ WHERE t.created_in is NULL'
+ );
+ }
+
+ /**
+ * When importing a new version for an existing product we will need to update the current stage,
+ * duplicate it in the future and also update the staging_update table. Prepare the temporary tables to
+ * handle this.
+ *
+ * @param AdapterInterface $connection
+ * @param $tmpTable
+ * @param $entityTable
+ */
+ protected function setStageValues(AdapterInterface $connection, $tmpTable, $entityTable)
+ {
+ // Let's find the versions that needs modifying.
+ // TODO need to join with staging_update as well to check that there are no issues.
+ // TODO We are not handling well stages that finishes in MAX_VERSION. Those just need previous version
+ // to be updated.
+ $select = $connection->select()
+ ->from(
+ ['t' => $tmpTable],
+ [
+ 'entity_id' => '_entity_id',
+ 'entity_updated_in' => 'created_in',
+ 'new_entity_created_in' => 'updated_in'
+ ]
+ )->joinInner(
+ ['e' => $entityTable],
+ 't._entity_id = e.entity_id',
+ [
+ 'row_id' => 'row_id',
+ 'entity_created_in' => 'created_in',
+ 'new_entity_updated_in' => 'updated_in'
+ ]
+ )->where('e.created_in <= t.created_in AND e.updated_in >= t.updated_in AND t._row_id IS NULL')
+ // Prevent staging module preview that adds additional conditions
+ ->setPart('disable_staging_preview', true);
+
+ $query = $connection->query($select);
+
+ $toUpdate = [];
+ $toDuplicate= [];
+ $entityIds = [];
+ while ($row = $query->fetch()) {
+
+ $toUpdate[] = [
+ '_entity_id' => $row['entity_id'],
+ '_row_id' => $row['row_id'],
+ 'created_in' => $row['entity_created_in'],
+ 'updated_in' => $row['entity_updated_in'],
+ ];
+
+ if ($row['new_entity_created_in'] != $row['new_entity_updated_in']) {
+ $toDuplicate[] = [
+ '_entity_id' => $row['entity_id'],
+ '_row_id' => $row['row_id'],
+ 'created_in' => $row['new_entity_created_in'],
+ 'updated_in' => $row['new_entity_updated_in'],
+ ];
+ }
+
+ $entityIds[] = $row['entity_id'];
+
+ if (count($toUpdate) > 500) {
+ $this->insertTemporaryStageValues($connection, $tmpTable, $toUpdate, $toDuplicate, $entityIds);
+ $toUpdate = [];
+ $toDuplicate= [];
+ $entityIds = [];
+ }
+ }
+
+ if (count($toUpdate) > 0) {
+ $this->insertTemporaryStageValues($connection, $tmpTable, $toUpdate, $toDuplicate, $entityIds);
+ }
+ }
+
+ /**
+ * Populate the temporary files to allow update & duplication of existing stage informations.
+ *
+ * @param AdapterInterface $connection
+ * @param $tmpTable
+ * @param $toUpdate
+ * @param $toDuplicate
+ * @param $entityIds
+ */
+ protected function insertTemporaryStageValues(
+ AdapterInterface $connection,
+ $tmpTable,
+ $toUpdate,
+ $toDuplicate,
+ $entityIds
+ ) {
+ $tableStageUpdate = $connection->getTableName('tmp_pimgento_entity_stage_update');
+ $tableStageDuplicate = $connection->getTableName('tmp_pimgento_entity_stage_duplicate');
+
+ $connection->insertArray($tableStageUpdate, array_keys($toUpdate[0]), $toUpdate);
+
+ if (!empty($toDuplicate)) {
+ $connection->insertArray($tableStageDuplicate, array_keys($toDuplicate[0]), $toDuplicate);
+ }
+
+ $connection->update(
+ $tmpTable,
+ ['_new_entity' => new Expr('0')],
+ ['_entity_id IN (?)' => $entityIds]
+ );
+ }
+
+ /**
+ * Create temporary tables to handle the staging.
+ *
+ * @param AdapterInterface $connection
+ */
+ protected function createTemporaryStageTable(AdapterInterface $connection)
+ {
+ /**
+ * Table with row id's whose created_in & updated_in needs to be updated.
+ */
+ $tableStageUpdate = $connection->getTableName('tmp_pimgento_entity_stage_update');
+ /**
+ * Table with new stages that needs to be created with the row to use to get the data from.
+ */
+ $tableStageDuplicate = $connection->getTableName('tmp_pimgento_entity_stage_duplicate');
+
+ $connection->dropTable($tableStageUpdate);
+ $connection->dropTable($tableStageDuplicate);
+
+ $table = $connection->newTable($tableStageUpdate);
+ $table->addColumn('_entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('_row_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('created_in', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('updated_in', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $connection->createTable($table);
+
+ $table = $connection->newTable($tableStageDuplicate);
+ $table->addColumn('_entity_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('_row_id', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('created_in', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $table->addColumn('updated_in', \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, 11, []);
+ $connection->createTable($table);
+ }
+
+ public function dropTemporaryStageTable(AdapterInterface $connection)
+ {
+ $connection->dropTable($connection->getTableName('tmp_pimgento_entity_stage_update'));
+ $connection->dropTable($connection->getTableName('tmp_pimgento_entity_stage_duplicate'));
+ }
+
+ /**
+ * When staging mode is enabled we also need to fill up the sequence_product table.
+ *
+ * @param AdapterInterface $connection
+ * @param string $sequenceTable
+ * @param string $tmpTable
+ */
+ public function createEntitiesBefore(AdapterInterface $connection, $sequenceTable, $tmpTable)
+ {
+ if ($this->configHelper->isCatalogStagingModulesEnabled()) {
+ $sequenceValues = ['sequence_value' => '_entity_id'];
+ $parents = $connection->select()->from($tmpTable, $sequenceValues);
+ $connection->query(
+ $connection->insertFromSelect(
+ $parents,
+ $connection->getTableName($sequenceTable),
+ array_keys($sequenceValues),
+ 1
+ )
+ );
+ }
+ }
+
+ /**
+ * Once the catalog_entity table has been filled, we need to get the row id's for all the new
+ * versions so that we can insert the values after.
+ *
+ * @param AdapterInterface $connection
+ * @param string $entityTableCode
+ * @param string $tmpTable
+ * @param string $stagingMode
+ */
+ public function createEntitiesAfter(AdapterInterface $connection, $entityTableCode, $tmpTable, $stagingMode)
+ {
+
+ if ($this->configHelper->isCatalogStagingModulesEnabled()) {
+ $entityTable = $connection->getTableName($entityTableCode);
+
+ $connection->query(
+ 'UPDATE `' . $tmpTable . '` t
+ SET `_row_id` = (
+ SELECT MAX(row_id) FROM `' . $entityTable . '` c
+ WHERE c.entity_id = t._entity_id
+ )
+ WHERE t._row_id IS NULL'
+ );
+
+ if ($stagingMode == Config::STAGING_MODE_FULL) {
+ $tableStageUpdate = $connection->getTableName('tmp_pimgento_entity_stage_update');
+ $tableStageDuplicate = $connection->getTableName('tmp_pimgento_entity_stage_duplicate');
+
+ // We need to update some of the existing entity rows to update the created_in/updated_in dates.
+ $select = $connection->select()
+ ->from(
+ ['t' => $tableStageUpdate],
+ [
+ 'row_id' => '_row_id',
+ 'entity_id' => '_entity_id',
+ 'created_in',
+ 'updated_in',
+ 'updated_at' => new Expr('now()'),
+ ]
+ );
+
+ $query = $connection->insertFromSelect(
+ $select,
+ $connection->getTableName('catalog_product_entity'),
+ ['row_id', 'entity_id', 'created_in', 'updated_in', 'updated_at'],
+ 1
+ );
+ $connection->query($query);
+
+ // We also need to create some new rows.
+ $select = $connection->select()
+ ->from(
+ ['t' => $tableStageDuplicate],
+ [
+ 'entity_id' => '_entity_id',
+ 'created_in',
+ 'updated_in',
+ 'updated_at' => new Expr('now()'),
+ 'created_at' => new Expr('now()')
+ ]
+ )->joinInner(
+ ['u' => $entityTable],
+ 'u.entity_id = t._entity_id',
+ [
+ 'attribute_set_id', 'type_id', 'sku', 'has_options', 'required_options'
+ ]
+ );
+
+ $query = $connection->insertFromSelect(
+ $select,
+ $entityTableCode,
+ [
+ 'entity_id',
+ 'created_in',
+ 'updated_in',
+ 'updated_at',
+ 'created_at',
+ 'attribute_set_id',
+ 'type_id', 'sku',
+ 'has_options',
+ 'required_options'
+ ]
+ );
+ $connection->query($query);
+
+ // Need to edit & insert lines in staging_update.
+ $stagingTable = $connection->getTableName('staging_update');
+ $select = $connection->select()
+ ->from(
+ ['t' => $tmpTable],
+ [
+ 'id' => 'created_in',
+ 'start_time' => new Expr('from_unixtime(created_in)'),
+ 'name' =>
+ new Expr(
+ 'CONCAT(
+ "Pimgento : ",
+ from_unixtime(created_in, GET_FORMAT(DATE,"ISO")),
+ " to ",
+ from_unixtime(updated_in, GET_FORMAT(DATE,"ISO")))'
+ ),
+ 'rollback_id'
+ => new Expr('IF(updated_in = ' . VersionManager::MAX_VERSION . ', NULL, updated_in)'),
+ 'is_rollback' => new Expr('NULL'),
+ ]
+ )->where('created_in != 1');
+ $query = $connection->insertFromSelect(
+ $select,
+ $stagingTable,
+ ['id', 'start_time', 'name', 'rollback_id', 'is_rollback'],
+ 1
+ );
+ $connection->query($query);
+
+ $stagingTable = $connection->getTableName('staging_update');
+ $select = $connection->select()
+ ->from(
+ ['t' => $tmpTable],
+ [
+ 'id' => 'updated_in',
+ 'start_time' => new Expr('from_unixtime(updated_in)'),
+ 'name' => new Expr('"Rollback for Pimgento"'),
+ 'rollback_id' => new Expr("NULL"),
+ 'is_rollback' => new Expr('1'),
+ ]
+ )->where('created_in != 1 AND updated_in != ' . VersionManager::MAX_VERSION);
+ $query = $connection->insertFromSelect(
+ $select,
+ $stagingTable,
+ ['id', 'start_time', 'name', 'rollback_id', 'is_rollback'],
+ 1
+ );
+ $connection->query($query);
+
+ }
+ }
+ }
+
+ /**
+ * Query to get all entity rows(stages) that has not been updated by the current import.
+ *
+ * Used to duplicate the data in all the stages.
+ *
+ * @param AdapterInterface $connection
+ * @param string $entityTable
+ * @param string $tmpTable
+ * @param string $joinCondition
+ *
+ * @return Select
+ */
+ public function getBaseStageDuplicationSelect(
+ AdapterInterface $connection,
+ $entityTable,
+ $tmpTable,
+ $joinCondition = 't._row_id != e.row_id'
+ ) {
+ return $connection->select()
+ ->from(
+ ['e' =>$entityTable],
+ []
+ )
+ // For each row we didn't update of the entities we have updated.
+ ->joinInner(
+ ['t' => $tmpTable],
+ 't._entity_id = e.entity_id AND ' . $joinCondition,
+ []
+ )
+ // Prevent staging module preview that adds additional conditions
+ ->setPart('disable_staging_preview', true);
+ }
+}
\ No newline at end of file
diff --git a/Staging/Model/Source/StagingMode.php b/Staging/Model/Source/StagingMode.php
new file mode 100644
index 0000000..52d1576
--- /dev/null
+++ b/Staging/Model/Source/StagingMode.php
@@ -0,0 +1,71 @@
+
+ * @copyright 2017 Smile
+ * @package Pimgento\Staging\Model\Source
+ */
+class StagingMode implements \Magento\Framework\Option\ArrayInterface
+{
+
+ /**
+ * @var \Pimgento\Staging\Helper\Config
+ */
+ protected $configHelper;
+
+ /**
+ * PHP Constructor to get the module manager to be able to display proper options.
+ *
+ * @param \Pimgento\Staging\Helper\Config $configHelper
+ */
+ public function __construct(
+ \Pimgento\Staging\Helper\Config $configHelper
+ ) {
+ $this->configHelper = $configHelper;
+ }
+
+
+ /**
+ * Retrieve Insertion method Option array
+ *
+ * @return array
+ */
+ public function toOptionArray()
+ {
+ // Allow staging mode only if the catalog staging modules are enabled.
+ if ($this->configHelper->isCatalogStagingModulesEnabled()) {
+ $options = [
+ [
+ 'value' => \Pimgento\Staging\Helper\Config::STAGING_MODE_LAST,
+ 'label' => __('Update Last Created Stage')
+ ],
+ [
+ 'value' => \Pimgento\Staging\Helper\Config::STAGING_MODE_CURRENT,
+ 'label' => __('Update Current Stage')
+ ],
+ [
+ 'value' => \Pimgento\Staging\Helper\Config::STAGING_MODE_ALL,
+ 'label' => __('Update All Stages')
+ ],
+ [
+ 'value' => \Pimgento\Staging\Helper\Config::STAGING_MODE_FULL,
+ 'label' => __('Full - Require "to" and "from" columns)')
+ ]
+ ];
+ } else {
+ $options = [
+ [
+ 'value' => \Pimgento\Staging\Helper\Config::STAGING_MODE_DISABLED,
+ 'label' => __("Disabled - Staging isn't activeted")
+ ],
+ ];
+ }
+
+ return $options;
+ }
+}
\ No newline at end of file
diff --git a/Staging/etc/module.xml b/Staging/etc/module.xml
new file mode 100644
index 0000000..e5f6c49
--- /dev/null
+++ b/Staging/etc/module.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Staging/registration.php b/Staging/registration.php
new file mode 100644
index 0000000..4806c0d
--- /dev/null
+++ b/Staging/registration.php
@@ -0,0 +1,7 @@
+