Upgrade Magento z 2.1.x do 2.2.10

Dawno nic nie pisałam o naszych Magentach, ale ostatnio dłubałam troszkę przy upgradzie i napotkałam problem, który troszkę nerwów mi napsuł. Rzecz dotyczy błędu braku tabel catalog_category_product_index_replica i catalog_category_product_index_store1.

Całość problemu dotyczy tego, że tabela catalog_category_product_index_store1 tworzy się podczas reindexacji (Magento v. 2.2.10) poleceniem:

php bin/magento indexer:reindex catalog_category_product

Natomiast tabela catalog_category_product_index_replica dodana została w UpgradeSchema.php modułu Magento_Catalog w wersji 2.2.0.
Mało tego, aby poprawnie wykonała się reindexacja, musimy mieć jeszcze w bazie dwie inne tabele (catalog_category_product_index i catalog_category_product_index_tmp). Magento tworzy je za pomocą pliku InstallSchema, czyli w momencie instalacji modułu.

No i mamy tutaj inpas, ponieważ aby wykonać polecenie:

php bin/magento setup:upgrade

potrzebujemy tabeli catalog_category_product_index_store1, w przeciwnym razie otrzymujemy błąd:

Module 'Magento_Catalog':
Upgrading schema.. SQLSTATE[42S02]: Base table or view not found: 1146 Table 'magento.catalog_category_product_index_store1' doesn't exist, query was: CREATE TABLE IF NOT EXISTS `catalog_category_product_index_replica` LIKE `catalog_category_product_index_store1

Natomiast, gdy próbujemy wykonać reindex:

php bin/magento indexer:reindex catalog_category_product

otrzymujemy błąd:

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'magento.catalog_category_product_index_replica' doesn't exist, query was: DESCRIBE `catalog_category_product_index_replica`

Rozwiązać to możemy na dwa sposoby, stworzyć PATCH albo ręcznie dodać tabelę do bazy.

Wykorzystanie PATCH’a

Aby móc zaaplikować PATCH’a do naszego Magento, możemy użyć narzędzia composer-patches, lub też skorzystać z możliwości samego composera. Poniższą instrukcje należy dodać do pliku composer.json:

{
    "scripts": {
        "post-install-cmd": "patch -p0 < path/to/your.patch"
    }
}

Poniżej znajduje się plik Patcha:

--- vendor/magento/module-catalog/Setup/UpgradeSchema.php    2019-11-12 16:43:39.884234228 +0100
+++ vendor/magento/module-catalog/Setup/UpgradeSchema.php    2019-11-12 16:00:03.338854758 +0100
@@ -37,6 +37,7 @@
             $this->addUniqueKeyToCategoryProductTable($setup);
         }
         if (version_compare($context->getVersion(), '2.1.4', '<')) {
+            $this->addCatalogCategoryProductIndex($setup);
             $this->addSourceEntityIdToProductEavIndex($setup);
         }
         if (version_compare($context->getVersion(), '2.1.5', '<')) {
@@ -841,4 +842,139 @@
             $setup->getIdxName('catalog_product_index_price_tmp', ['min_price'])
         );
     }
+
+    /**
+    * @param SchemaSetupInterface $setup
+    */
+    private function addCatalogCategoryProductIndex(SchemaSetupInterface $setup)
+    {
+        /**
+         * Create table 'catalog_category_product_index'
+         */
+        $table = $setup->getConnection()
+            ->newTable($setup->getTable('catalog_category_product_index'))
+            ->addColumn(
+                'category_id',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'],
+                'Category ID'
+            )
+            ->addColumn(
+                'product_id',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'],
+                'Product ID'
+            )
+            ->addColumn(
+                'position',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['unsigned' => false, 'nullable' => true, 'default' => null],
+                'Position'
+            )
+            ->addColumn(
+                'is_parent',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+                'Is Parent'
+            )
+            ->addColumn(
+                'store_id',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'primary' => true, 'default' => '0'],
+                'Store ID'
+            )
+            ->addColumn(
+                'visibility',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false],
+                'Visibility'
+            )
+            ->addIndex(
+                $setup->getIdxName(
+                    'catalog_category_product_index',
+                    ['product_id', 'store_id', 'category_id', 'visibility']
+                ),
+                ['product_id', 'store_id', 'category_id', 'visibility']
+            )
+            ->addIndex(
+                $setup->getIdxName(
+                    'catalog_category_product_index',
+                    ['store_id', 'category_id', 'visibility', 'is_parent', 'position']
+                ),
+                ['store_id', 'category_id', 'visibility', 'is_parent', 'position']
+            )
+            ->setComment('Catalog Category Product Index');
+        $setup->getConnection()->createTable($table);
+
+        /**
+         * Create table 'catalog_category_product_index_tmp'
+         */
+        $table = $setup->getConnection()
+            ->newTable(
+                $setup->getTable('catalog_category_product_index_tmp')
+            )
+            ->addColumn(
+                'category_id',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+                'Category ID'
+            )
+            ->addColumn(
+                'product_id',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+                'Product ID'
+            )
+            ->addColumn(
+                'position',
+                MagentoFrameworkDBDdlTable::TYPE_INTEGER,
+                null,
+                ['nullable' => false, 'default' => '0'],
+                'Position'
+            )
+            ->addColumn(
+                'is_parent',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+                'Is Parent'
+            )
+            ->addColumn(
+                'store_id',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+                'Store ID'
+            )
+            ->addColumn(
+                'visibility',
+                MagentoFrameworkDBDdlTable::TYPE_SMALLINT,
+                null,
+                ['unsigned' => true, 'nullable' => false],
+                'Visibility'
+            )
+            ->addIndex(
+                $setup->getIdxName('catalog_category_product_index_tmp', ['product_id', 'category_id', 'store_id']),
+                ['product_id', 'category_id', 'store_id']
+            )
+            ->setOption(
+                'type',
+                MagentoFrameworkDBAdapterPdoMysql::ENGINE_MEMORY
+            )
+            ->setComment(
+                'Catalog Category Product Indexer Temp Table'
+            );
+        $setup->getConnection()
+            ->createTable($table);
+
+        $setup->endSetup();
+    }
 }

Zostanie on zaaplikowany po wykonaniu polecenia:

composer install

Po poprawnym zaaplikowaniu patcha polecenie php bin/magento setup:upgrade nie powinno zrzucać już błędów.

Ręczne tworzenie tabel

Alternatywą dla wykorzystania Patch’a jest ręczne stworzenie tabel. Poniżej znajduje się create schema:

CREATE TABLE `catalog_category_product_index` (
  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Category ID',
  `product_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Product ID',
  `position` int(11) DEFAULT NULL COMMENT 'Position',
  `is_parent` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Is Parent',
  `store_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Store ID',
  `visibility` smallint(5) unsigned NOT NULL COMMENT 'Visibility',
  PRIMARY KEY (`category_id`,`product_id`,`store_id`),
  KEY `CAT_CTGR_PRD_IDX_PRD_ID_STORE_ID_CTGR_ID_VISIBILITY` (`product_id`,`store_id`,`category_id`,`visibility`),
  KEY `CAT_CTGR_PRD_IDX_STORE_ID_CTGR_ID_VISIBILITY_IS_PARENT_POSITION` (`store_id`,`category_id`,`visibility`,`is_parent`,`position`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Catalog Category Product Index';

CREATE TABLE `catalog_category_product_index_tmp` (
  `category_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Category ID',
  `product_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Product ID',
  `position` int(11) NOT NULL DEFAULT '0' COMMENT 'Position',
  `is_parent` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Is Parent',
  `store_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT 'Store ID',
  `visibility` smallint(5) unsigned NOT NULL COMMENT 'Visibility',
  PRIMARY KEY (`category_id`,`product_id`,`store_id`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8 COMMENT='Catalog Category Product Indexer temporary table';

Należy zaznaczyć, że nie jest to ładne rozwiązanie i może stwarzać same problemy w przypadku wieloosobowego zespołu, podczas stawiania środowiska pracy. Także nie jest to rekomendowana przeze mnie metoda. Warto jednak wiedzieć jak można zrobić to ręcznie aby chociażby sprawdzić czy dodanie tych tabel wyeliminuje nasz podstawowy problem.

Dziękuję, że poświęciłeś swój czas na przeczytanie tego artykułu, jeśli masz jeszcze chwilę, podziel się swoimi wrażeniami i >>zostaw komentarz<< w wątku do tego posta.