Challenges faced while migrating a ECommerce store from Joomla extension-JoomShopping to PrestaShop.
Challenge: 1
All the Products and Categories from the JoomShopping needed to be migrated into PrestaShop 1.7.3 CMS. We searched through the internet, there was no tool found. So we made a standalone script to migrate the entries from JoomShopping to PrestaShop.
This is how we implemented the standalone script:
- We analyzed the Product and Category table structure of JoomShopping and PrestaShop, PrestaShop have possibility to store all the details of Product and Category from JoomShopping. Managing details of Product and Categories are same in both and only difference is table structure.
- For migration process, we created a standalone PHP script, for every run it will migrate all the future Products and Categories which means it will not migrate already added entries.
First step:
- We need the following JoomShopping module database tables for migration process:
- joom_jshopping_attr
- joom_jshopping_attr_values
- joom_jshopping_categories
- joom_jshopping_products
- joom_jshopping_products_attr2
- joom_jshopping_products_images
- joom_jshopping_products_to_categories
- The script imported all the above tables into the PrestaShop CMS
Second step:
- Once Category migration starts, the following code may be used for adding a new Category in PrestaShop :
$category = new Category; $category->active = joom_jshopping_categories::category_publish; $category->id_parent = joom_jshopping_categories::category_parent_id;(Here, parent category ID from Prestashop) $category->id_shop_default = ; $category->name = joom_jshopping_categories::name_de-DE; $category->link_rewrite = joom_jshopping_categories::alias_de-DE; $category->meta_title = joom_jshopping_categories::meta_title_de-DE; $category->meta_description = joom_jshopping_categories::meta_description_de-DE; $category->meta_keywords = joom_jshopping_categories::meta_keyword_de-DE; $category->description = joom_jshopping_categories::description_de-DE; $category->date_add = joom_jshopping_categories::category_add_date; $category->add();
// For adding category images AdminImportController::copyImage( $category->id, null, joom_jshopping_categories::category_image, 'categories' );
Third step:
- After Category migration, the Product migration will start. For adding a New Product in PrestaShop use the following code:
$product = new Product(); $product->name = joom_jshopping_products::name_de-DE; $product->link_rewrite = joom_jshopping_products::alias_de-DE; $product->description = joom_jshopping_products::description_de-DE; $product->description_short = joom_jshopping_products::short_description_de-DE; $product->meta_description = joom_jshopping_products::meta_description_de-DE; $product->meta_keywords = joom_jshopping_products::meta_keyword_de-DE; $product->meta_title = joom_jshopping_products::meta_title_de-DE; $product->price = joom_jshopping_products::product_price; $product->quantity = joom_jshopping_products::product_quantity; $product->wholesale_price = joom_jshopping_products::product_buy_price; $product->date_add = joom_jshopping_products::product_date_added; $product->date_upd = joom_jshopping_products::date_modify;
// To assign multiple Categories $product->addToCategories(); $product->add();
//set Product quantity StockAvailable::setQuantity($product->id, 0, joom_jshopping_products::product_quantity);
// For adding Product images In JoomShopping, Product images are stored in the table joom_jshopping_products_images.
Use the following code to add a Product images
$shops = Shop::getShops(true, null, true); $image = new Image(); $image->id_product = $product->id; $image->cover = boolean: true or false; $image->position = Image::getHighestPosition($product->id) + 1; if (($image->validateFields(false, true)) === true && ($image->validateFieldsLang(false, true)) === true && $image->add() ) { $image->associateTo($shops); if (!AdminImportController::copyImage( $product->id, $image->id, joom_jshopping_products_images::image_name, 'products' )) { $image->delete(); } }
Fourth step:
- After Product migration is completed, Product attribute migration will start off.
- Add a Product attribute group and attribute in PrestaShop by using the following code:In JoomShopping.
- Product attribute groups are stored in the table ‘joom_jshopping_attr’ and Product attribute are stored in the table ‘joom_jshopping_attr_values’.
// For adding attribute groups $attributeGroup = new AttributeGroup(); $attributeGroup->name = joom_jshopping_attr::name_de-DE; $attributeGroup->public_name = joom_jshopping_attr::name_de-DE; $attributeGroup->group_type = joom_jshopping_attr::attr_type; $attributeGroup->add();
// For adding a attribute $attribute = new Attribute(); $attribute->name = joom_jshopping_attr_values::name_de-DE; $attribute->id_attribute_group = joom_jshopping_attr_values::group_id; $attribute->position = joom_jshopping_attr_values::value_ordering; $attribute->add();
// For adding attribute images AdminController::uploadImage($attribute->id, joom_jshopping_attr_values::image, 'co/');
Fifth step:
- After Product attribute migration is completed, migration of Product combination will get started. In JoomShopping, combinations are stored in the table ‘joom_jshopping_products_attr2’.
// For adding Product combination in Prestashop by using the following code: $product = new Product($productId, true, 1, 1); $options = array('' => array('')); SpecificPriceRule::disableAnyApplication(); $combinations = array_values(AdminAttributeGeneratorController::createCombinations(array_values($options))); $combinationsValues = array_values(array_map(function () use ($product) { return array( 'id_product' => $product->id ); }, $combinations)); $product->generateMultipleCombinations($combinationsValues, $combinations, false); Product::updateDefaultAttribute($product->id);
Sixth step:
- After migration of all the Product and Attribute combinations, we have to update combination quantity and its price using the following code:
// Code for updating combination price $combinationObj = new Combination(); $combinationObj->setFieldsToUpdate(array('price' => true)); $combinationObj->price = ; $combinationObj->save();
// Code for updating combination quantity $combination = new Combination(); $combination->quantity = ; StockAvailable::setQuantity($product->id, , ); $combination->update();
Final step:
- After successful migration, ensure that you remove all the JoomShopping tables remove from PrestaShop.
Note:
In PrestaShop,- Category details are stored in the table ‘ps_category’ and ‘ps_category_lang’.
- Product details are stored in the table ‘ps_product’ and ‘ps_product_lang’.
- Product category relations are stored in the table ‘ps_category_product’.
- Product images are stored in the table ‘ps_image’ and ‘ps_image_lang’.
- Product combinations are stored in table ‘ps_product_attribute’ and ‘ps_product_attribute_combination’.
- Product attribute are stored in the table ‘ps_attribute’ and ‘ps_attribute_lang’.
- Product attribute group are stored in the table ‘ps_attribute_group’ and ‘ps_attribute_group_lang’.
Problem: 2
If a Product has more than one Category then the Product URL is not displaying properly. For example, Product A is assigned to two Categories C1 and C2. If we select Category C1 and choose Product A (OR) select the Category C2 and select the Product A, in both cases, it goes to the Product details page with the same URL which is based on the Category C1 because the Product’s default Category is C1(this is a default functionality of PrestaShop). But we don’t want the same URL, it should be as follows:
‘http://www.example.com/C1/A’,
‘http://www.example.com/C2/A’.
To achieve this, we have customize the core files.
Solution: 2
In PrestaShop, the Product link generation is done by the default Category, This is the reason we are getting the same URL if we enter into the both Category C1 and C2. Instead of taking the default Category, we will find the user entered categoryId and generate Product link based on that Category ID. To achieve this we have to customize the file classes/Link.php.
Code:
In the method Link::getProductLink():
if ($dispatcher->hasKeyword('product_rule', $idLang, 'categories', $idShop)) { $product = $this->getProductObject($product, $idLang, $idShop); $parentCatId = $this->getCategoryIdFrmUri($product, $idLang); $params['category'] = !$parentCatId ? ((!$category) ? $product->category : $category) : $parentCatId; $cats = array(); foreach ($product->getParentCategories($idLang, $parentCatId) as $cat) { if (!in_array($cat['id_category'], Link::$category_disable_rewrite)) { //remove root and home category from the URL $cats[] = $cat['link_rewrite']; } } $params['categories'] = implode('/', $cats); } protected function getCategoryIdFrmUri($product, $idLang) { $uriSplits = explode('/', $_SERVER['REQUEST_URI']); if (empty($uriSplits) || !$product) { return null; } $latestSplit = $uriSplits[count($uriSplits) - 1]; $latestParentAlias = explode('?', $latestSplit)[0]; if ($product->link_rewrite === $latestParentAlias) { $latestParentAlias = isset($uriSplits[count($uriSplits) - 2]) ? $uriSplits[count($uriSplits) - 2] : ''; } if (!empty($latestParentAlias)) { $parentCat = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow( 'SELECT * FROM `' . _DB_PREFIX_ . 'category_lang` WHERE `id_lang` = ' . $idLang . ' AND `id_shop` = 1 AND `link_rewrite` = "' . pSQL($latestParentAlias) . '"' ); if (!empty($parentCat) && !empty($parentCat['id_category']) && in_array($parentCat['id_category'], $product->getCategories())) { return $parentCat['id_category']; } } return null; }
Problem: 3
In PrestaShop, if any of the Product and its Category have same rewrite link then the Product link will not work and it always points to that Category. But in our case, if Product and Category have same rewrite link, that Product and Category link have to work.
Solution: 3
To fix this issue, we have customize the file classes/Dispatcher.php.
For example: Assume a Product and its Category have rewrite link ‘sample’. The Product URL like as www.example.com/sample/sample. The Category URL will be www.example.com/sample. If we load both URLs, both redirect to Category page. But actually if we load the Product URL it should redirect to Product page and for Category URL it should redirect to Category page.
Code:
Add the below code in the Dispatcher::getController() method:
// If the category and Product slug both are same and user enter into Product with that case then we have to set controller as product $uriSplits = explode('/', $this->request_uri); if ( !empty($uriSplits) && !preg_match('/[0-9a-zA-Z\-\/\_\s]*\.[jpg|png|js|css]+$/', $this->request_uri) ) { $totalSplits = count($uriSplits); $latestSplit = $uriSplits[$totalSplits - 1]; $lastParentAlias = explode('?', $latestSplit)[0]; $previousParentAlias = isset($uriSplits[$totalSplits - 2]) ? $uriSplits[$totalSplits - 2] : ''; if ( !empty($previousParentAlias) && ($previousParentAlias === $lastParentAlias) ) { $product = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow( 'SELECT * FROM `' . _DB_PREFIX_ . 'product_lang` WHERE `id_lang` = 1 AND `id_shop` = 1 AND `link_rewrite` = "' . pSQL($lastParentAlias) . '"' ); if ($product) { $controller = 'product'; $_POST['id_product'] = $product['id_product']; } } } Note: We have used the module 'pretty URLS' to avoid the Ids from the Product URL.