Prestashop has a great importer that allows you to import products directly from a CSV file. The problem with the default importer is that it always overwrites the product names. That means that if you import your products and then customize the product names you will lose that customization if you try to re-import the products again (i.e. to only update product prices).
What we want to achieve with this modification:
- Initially import all the store products from a CSV file.
- Be able to frequently update product prices through the same CSV importer without losing changes done to the product name.
- Detects new products in the CSV and add them to the database.
- Optionally allow the user to rewrite all products names if desired.
1Required changes
- We are going to add a new parameter called "keep_names" to the importer. This will allow the user to choose if wants to overwrite or keep product names. The default behaviour will be the same that by default uses Prestashop (overwir.
- Modify the import process to inherit the product name when "keep_names" is selected or the product name is empty.
The rest of the article explains the changes done to the files. If you don't want to go on reading you can:
2Add a parameter to the Prestashop import form
Basically we will override the default admin files:
admin/themes/default/template/controllers/import/helpers/form/form.tpl
admin/themes/default/template/controllers/import/helpers/view/view.tpl
Replace "admin" with the name of your admin folder.
To override them we have copied them into:
override/controllers/admin/templates/import/helpers/form/form.tpl
override/controllers/admin/templates/import/helpers/view/view.tpl
To keep this article as clean as possible while I have also uploaded the full code to a gist. This way you can fastly see the code.
In the form.tpl file we have added the new parameter with:
We also added the JS logic to hide the field when not importing products:
if ($("#entity > option:selected").val() == 1) { $("label[for=match_ref],#match_ref").show(); $("label[for=keep_names],#keep_names").show(); } else { $("label[for=match_ref],#match_ref").hide(); $("label[for=keep_names],#keep_names").hide(); }
In the view.tpl we have added the code to receive and propagate the keep_names as a hidden field:
{if $fields_value.keep_names} {/if}
3Override import controller
This is the file when the import occurres. What we are going to do is to copy the methods that we need to override from:
controllers/admin/AdminImportController.php
to the override class at:
override/controllers/admin/AdminImportController.php
As we only need to override two methods of the original file renderView() and productImport() we are not going to copy the full file. This way we can use the core methods if some bugs affect them.
You can see the final AdminImportController.php file at the gist.
In the renderView() method we have received the keep_names receive value to propagate it to the view:
'keep_names' => Tools::getValue('keep_names'),
We also hade to modify the call to the parent method as we are going to call now the Grandparent (AdminController) renderView method:
return AdminController::renderView();
In the productImport() method we will replace this code:
$field_error = $product->validateFields(UNFRIENDLY_ERROR, true); $lang_field_error = $product->validateFieldsLang(UNFRIENDLY_ERROR, true); if ($field_error === true && $lang_field_error === true) { // check quantity if ($product->quantity == null) $product->quantity = 0; // If match ref is specified && ref product && ref product already in base, trying to update if (Tools::getValue('match_ref') == 1 && $product->reference && $product->existsRefInDatabase($product->reference)) { $datas = Db::getInstance()->getRow(' SELECT product_shop.`date_add`, p.`id_product` FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE p.`reference` = "'.$product->reference.'" '); $product->id = (int)$datas['id_product']; $product->date_add = pSQL($datas['date_add']); $res = $product->update(); } // Else If id product && id product already in base, trying to update else if ($product->id && Product::existsInDatabase((int)$product->id, 'product')) { $datas = Db::getInstance()->getRow(' SELECT product_shop.`date_add` FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE p.`id_product` = '.(int)$product->id); $product->date_add = pSQL($datas['date_add']); $res = $product->update(); } // If no id_product or update failed if (!$res) { if (isset($product->date_add) && $product->date_add != '') $res = $product->add(false); else $res = $product->add(); } }
with:
$preloadProduct = false; $preloadById = ($product->id && Product::existsInDatabase((int)$product->id, 'product')); $preloadByreference = (Tools::getValue('match_ref') == 1 && $product->reference && $product->existsRefInDatabase($product->reference)); if ($preloadByreference || $preloadById) { $preloadProduct = true; $sql = 'SELECT product_shop.`date_add`, p.`id_product`, pl.`name` FROM `'._DB_PREFIX_.'product` p LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product AND pl.`id_lang` = ' . (int) $default_language_id . ') ' . Shop::addSqlAssociation('product', 'p'); if ($preloadByreference) { $sql .= 'WHERE p.`reference` = "' . $product->reference . '" '; } if ($preloadById) { $sql .= 'WHERE p.`id_product` = ' . (int) $product->id; } $datas = Db::getInstance()->getRow($sql); $product->id = (int)$datas['id_product']; $product->date_add = pSQL($datas['date_add']); // Check quantity if ($product->quantity == null) { $product->quantity = 0; } // Inherit product name if required or not set if (Tools::getValue('keep_names') || !isset($product->name) || empty($product->name)) { $product->name = pSQL($datas['name']); } } $field_error = $product->validateFields(UNFRIENDLY_ERROR, true); $lang_field_error = $product->validateFieldsLang(UNFRIENDLY_ERROR, true); if ($field_error === true && $lang_field_error === true) { if ($preloadProduct) { $res = $product->update(); } // If no id_product or update failed if (!$res) { if (isset($product->date_add) && $product->date_add != '') $res = $product->add(false); else $res = $product->add(); } }
The code does a preload of the products when needed. This way products can inherit names when required.