Jump to content

[Tutorial][PS 1.7+] Adding prestashop products programmatically


Recommended Posts

  • 7 months later...
  • 3 weeks later...

if you use multiple languages, you'll encounter problems displaying images unless you add this line to addProduct() function:

$product->link_rewrite = createMultiLangField(Tools::str2url($name));

along with all other $product-> lines.

without it images are nicely uploaded and even visible from backend product list, but links to them are corrupted on frontend if seo-friendly urls setting is on. Cost me half of day to figure out. result is hiding in table "_product_lang" column "link_rewrite" and used in urls generation (both for product url and images url), so very unobvious problem.

Tools::str2url used here is prestashop built-in tool to convert any language texts to urls.

(current PS 1.7.7.4, not sure about earlier or later versions)

in all other means - perfect tutorial, very helpful, THANK YOU!

Edited by mfdenis (see edit history)
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, mfdenis said:

if you use multiple languages, you'll encounter problems displaying images unless you add this line to addProduct() function:


$product->link_rewrite = createMultiLangField(Tools::str2url($name));

along with all other $product-> lines.

without it images are nicely uploaded and even visible from backend product list, but links to them are corrupted on frontend if seo-friendly urls setting is on. Cost me half of day to figure out. result is hiding in table "_product_lang" column "link_rewrite" and used in urls generation (both for product url and images url), so very unobvious problem.

Tools::str2url used here is prestashop built-in tool to convert any language texts to urls.

(current PS 1.7.7.4, not sure about earlier or later versions)

in all other means - perfect tutorial, very helpful, THANK YOU!

Hello @mfdenis

Thank you for your contribution!

I will look into you suggested update and add it to the code if it is working correctly.

I was not aware of this issue with SEO.

Kind regards,

Jaimy

Link to comment
Share on other sites

Another minor change I'd offer is 

$product->price = round($price, 6);

instead of simple $product->price = $price;

because this is price tax excluded and most likely it will be float, which doesn't pass prestashop's FieldValue check. (db value expects decimal with 6 figures after coma, while float in php has much longer 'tail')

(probably this depends on store's settings - how it calculates final price - if it stores price tax included or tax excluded in DB. In my case it caused script crash on non-integer values, so I've applied this fix.)

yet again - current PS 1.7.7.4, not sure about earlier or later versions

  • Thanks 1
Link to comment
Share on other sites

8 minutes ago, mfdenis said:

Another minor change I'd offer is 


$product->price = round($price, 6);

instead of simple $product->price = $price;

because this is price tax excluded and most likely it will be float, which doesn't pass prestashop's FieldValue check. (db value expects decimal with 6 figures after coma, while float in php has much longer 'tail')

(probably this depends on store's settings - how it calculates final price - if it stores price tax included or tax excluded in DB. In my case it caused script crash on non-integer values, so I've applied this fix.)

yet again - current PS 1.7.7.4, not sure about earlier or later versions

Hey @mfdenis

thank you for your contributions!

For the $product-price part i prefer using the following:

$product->price = number_format($price_incl, 6, '.', '');

When using my auto-importer i have noticed that some company's uses a comma instead of a point in there prices, this will then result in a import failure.

I did not update this code in this tutorial, so i added this today, thank you for pointing this out!

Link to comment
Share on other sites

  • 3 weeks later...
3 minutes ago, Inter Svetainė said:

Line


$product->price = number_format($price_incl, 6, '.', '');

uses $price_incl, but in arguments is $price.

You are correct! Its a copy paste mistake from my side.

Thank for points this typo out!

  • Thanks 1
Link to comment
Share on other sites

one more copy-paste mistake is in this part:

// 1. Check if 'feature value name' exist already in database
                $FeatureValueId = Db::getInstance()->getValue('SELECT id_feature_value FROM webshop_feature_value WHERE id_feature_value IN (SELECT id_feature_value FROM webshop_feature_value_lang WHERE value = "' . pSQL($attributeValue) . '") AND id_feature = ' . $FeatureNameId);
           

where all "webshop_" parts should be replaced with ' . _DB_PREFIX_ . ' I suppose.

  • Thanks 1
Link to comment
Share on other sites

12 hours ago, mfdenis said:

one more copy-paste mistake is in this part:


// 1. Check if 'feature value name' exist already in database
                $FeatureValueId = Db::getInstance()->getValue('SELECT id_feature_value FROM webshop_feature_value WHERE id_feature_value IN (SELECT id_feature_value FROM webshop_feature_value_lang WHERE value = "' . pSQL($attributeValue) . '") AND id_feature = ' . $FeatureNameId);
           

where all "webshop_" parts should be replaced with ' . _DB_PREFIX_ . ' I suppose.

You are correct! Thank for points this out 😉

Link to comment
Share on other sites

4 hours ago, Inter Svetainė said:

There is no limit to upgrade Your already perfect code :)
for example add (int)
into  this line


$image_obj = new Image($id_image);

and it will look like


$image_obj = new Image((int)$id_image);

to be sure that id of image will be for sure an integer ;) 

Thank you for your contribution, i have added it to the code.

Have a nice day!

  • Thanks 1
Link to comment
Share on other sites

8 hours ago, Crezzur said:

You are correct! Thank for points this out 😉

Hey @Crezzur, there are TWO mentions of db tables in that long SQL query, I noticed you've fixed only one in code, second is still there 😅

(nonetheless one of the most useful tutorials over whole forum, just can't stop praise it!)

Link to comment
Share on other sites

16 minutes ago, mfdenis said:

Hey @Crezzur, there are TWO mentions of db tables in that long SQL query, I noticed you've fixed only one in code, second is still there 😅

(nonetheless one of the most useful tutorials over whole forum, just can't stop praise it!)

My mistake! changed now.

When I started creating a script for programmatically adding products, I quickly found that little has been written on this subject. You'll find little bits of code all over the prestashop forums that often also contained errors. After a month of searching and testing I finally had a working script. It therefore seemed fairest to me to share it with the community where I was able to find parts of the script.

Then I wrote a module (https://crezzur.com/en/prestashop-modules/16-crezzur-dst) with this script that works quickly and without errors. The script has already been tested with an import of more than 10,000 products and has more than proven its effectiveness.

I am glad to hear that you enjoy it as much as i do 😉

Edited by Crezzur (see edit history)
Link to comment
Share on other sites

Hi guys, 

 

First, thank you for your really cool scrypt !

1 ) here is a solution for a problem , i encounter on stock. (after a lot of research)

this line is not working on my prestashop (1.7.7), stock is always 0 :

$product->quantity = $qty; 

you need to set stock like this after adding product

StockAvailable::setQuantity($product->id, null, $qty);

 

2) i have a problem with image

upload works fine, in Admin the image is set cover no problem.

But on front , image is not displaying (empty/broken)

when i go on my product admin file and validate it .... front product file is working.

 

Have you any idear what is missing ? 

 

thank you !! 

 

  • Thanks 2
Link to comment
Share on other sites

45 minutes ago, seblyon said:

Hi guys, 

 i have a problem with image : <
upload works fine, in Admin the image is set cover no problem.
But on front , image is not displaying (empty/broken)
when i go on my product admin file and validate it .... front product file is working.

Have you any idear what is missing ?

 

First of all, thank you for your contribution! I have checked and confirmed that this was an issue. We have updated our tutorial with your suggestions which is working fine.

For you image problems lets try to trouble shout the issue. Normally there are no issues with the importer (checked and still working)

Can you add this code:

    $img = $product->getCover($product->id);
    echo 'Product IMAGE: ' . Context::getContext()->link->getImageLink($product->link_rewrite[1], $img['id_image']);

under the line:

echo 'Product added successfully (ID: ' . $product->id . ')';

 

Now you will receive the image url, does this url work correctly?

Link to comment
Share on other sites

  • 2 weeks later...

Hi,
I met what I think is an error, in the  features code :

// 1. Check if 'feature name' exist already in database
$FeatureNameId = Db::getInstance()->getValue('SELECT id_feature FROM ' . _DB_PREFIX_ . 'feature_lang WHERE name = "' . pSQL($attributeName) . '"');
// If 'feature name' does not exist, insert new.
if (empty($getFeatureName)) {

As it is, it duplicates the existing features.

That should be  (change on last line) :
 

// 1. Check if 'feature name' exist already in database
$FeatureNameId = Db::getInstance()->getValue('SELECT id_feature FROM ' . _DB_PREFIX_ . 'feature_lang WHERE name = "' . pSQL($attributeName) . '"');
// If 'feature name' does not exist, insert new.
if (empty($FeatureNameId )) {

Thanks a lot for your code !

  • Thanks 1
Link to comment
Share on other sites

  • 5 months later...
  • 3 weeks later...

Hi there and thanks for the so usefull script!

It's one of the more core features but it was still lacking of info around the forums..

 

One question in case you have something in mind is how to modify so it can be used for product with combinations..

Does anyone has done something similar?

Link to comment
Share on other sites

On 1/21/2022 at 1:31 PM, Alicebfeskjfghj said:

Hello,

Thank you a lot for this !

I would like to add a file in FTP (like the import file for example) so that it could automatically import the products. In the step 7 you add a product manually. Is it possible to make it call a file directly ?

 

Thank you ! 🙂

You can read your .csv file and then within a loop use the addProduct function

Edited by orotoi (see edit history)
Link to comment
Share on other sites

1 hour ago, orotoi said:

Hi there and thanks for the so usefull script!

It's one of the more core features but it was still lacking of info around the forums..

 

One question in case you have something in mind is how to modify so it can be used for product with combinations..

Does anyone has done something similar?

just a portion of my code to give you some idea where to look for (don't expect this code to work out of the box, it requires you to set up Attributes Group and to know its ID):

// look for $id_attribute_group in your Prestashop BO

$attributes = AttributeGroup::getAttributes(1, $id_attribute_group);

$product = new Product($product_id);

foreach ($attributes as $attribute) {

        if (!$first_run) {
            $set_as_default = 1;
        } else {
            $set_as_default = 0;
        }

	// calculate your $calculated_price


	$idProductAttribute = $product->addCombinationEntity(
            0, //$wholesale_price
            $calculated_price,
            0, //$weight
            0, //$unit_impact
            0, //$ecotax
            0, //$quantity
            '', //$id_images
            '', //$reference
            '', //$id_supplier
            '', //$ean13
            $set_as_default //$default
        );

        Db::getInstance()->execute('
		INSERT IGNORE INTO ' . _DB_PREFIX_ . 'product_attribute_combination (id_attribute, id_product_attribute)
		VALUES (' . (int) $attribute["id_attribute"] . ',' . (int) $idProductAttribute . ')', false);

        $first_run = true; // not the most elegant solution, but just works for me

}

 

 

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...
  • 2 months later...
  • 4 weeks later...

I also offer to change first step (where config file is included).

If "add-product-file.php" will be created not in root/public_html then this will not work.
 

<?php
    // Check if _PS_ADMIN_DIR_ is defined
    if (!defined('_PS_ADMIN_DIR_')) {
        // if _PS_ADMIN_DIR_ is not defined, define.
        define('_PS_ADMIN_DIR_', getcwd());
    }
    // Setup connection with config.inc.php (required for database connection, ...)
    include(_PS_ADMIN_DIR_.'/../config/config.inc.php');

cause getcwd(); returns current folder of file, not root.

For example if I create "add-product-file.php" in public_html/manual-code/add-item/add-product-file.php.

So getcwd() returns public_html/manual-code/add-item.

Defined _PS_ADMIN_DIR_ value will be wrong.
Later on including _PS_ADMIN_DIR_ .'/../config/config.inc.php' will throw error. Full path to file becomes "public_html/manual-code/config/config.inc.php". Which, of course, is wrong.

I suggest to not rely on/don't use getcwd(). Instead use "$_SERVER['DOCUMENT_ROOT']".

Full code snippet would be

<?php
    // Check if _PUBLIC_HTML_DIR_ is defined
    if (!defined('_PUBLIC_HTML_DIR_')) {
        // if _PUBLIC_HTML_DIR_ is not defined, define.
        define('_PUBLIC_HTML_DIR_', $_SERVER['DOCUMENT_ROOT']);
    }
    // Setup connection with config.inc.php (required for database connection, ...)
    include(_PUBLIC_HTML_DIR_.'/config/config.inc.php');

So does not matter where custom add item code will be placed it will always will be using root project dir/public_html dir to start looking for core files.

Please note I changed const name.

Edited by Inter Svetainė
added not of changed const name (see edit history)
Link to comment
Share on other sites

  • 3 weeks later...

The code is great and does the work but wanted to suggest another usefull tip:

Search::indexation(false, $productId);

This code will help to do reindex in the end because after programmatical creation the product search was not working for me (even after manual reindex in the admin panel)

Thanks,
Michael

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...
  • 3 months later...
On 7/15/2022 at 12:31 AM, Cezar Lacatus said:

How do you add more images using this piece of code?

 

Thank you.

You can try something like this:

		function addImageToPresta($idProduct, $urlToImage) {
			$absoluteUrlToImg = Context::getContext()->shop->getBaseURL(true) . $urlToImage;
			$absoluteUrlToImg = preg_replace("/^https:/i", "http:", $absoluteUrlToImg);
			$image = new Image();
			$image->id_product = $idProduct;
			$image->position = Image::getHighestPosition($idProduct) + 1;
			if (strpos($urlToImage, '_cover') !== false) {
				$image->cover = true;
			} else {
				$image->cover = false;
			}
			$image->add();
		}

 

Edited by Inter Svetainė (see edit history)
Link to comment
Share on other sites

1 hour ago, Bonza Views said:

This thread is fantasic, thanks to everyone who contributed.

Does anyone know how to add "Attached files" to a product with PHP?

 

I use the following code where $attachmentsArray is just comma-separated URLs of my PDF files for each product:
 

        $attachmentsArray = explode(',', $attachmentsUrls);
        $i = 0;
        foreach ($attachmentsArray as $attachmentPath) {
            $i++;
            $number = '';
            if($i > 1) {
                $number = '_' . $i;
            }
            $fileName = 'Rules_'.$ref.$number.'.pdf';
            try {
                $arrContextOptions=array(
                    "ssl"=>array(
                        "verify_peer"=>false,
                        "verify_peer_name"=>false,
                    ),
                );
                copy($attachmentPath, _PS_DOWNLOAD_DIR_ . $fileName, stream_context_create($arrContextOptions));
            }catch (Exception $exception){
                echo $exception->getMessage() . '<br>';
                continue;
            }

            $attachment = new Attachment();
            $attachment->file = $fileName;
            $attachment->mime = 'application/pdf';
            $attachment->file_name = $fileName;

            // Create name and description for each language
            $languages = Language::getLanguages(false);
            foreach ($languages as $language)
            {
                $attachment->name[(int)$language['id_lang']] = $fileName;
                $attachment->description[(int)$language['id_lang']] = '';
            }

            // Put to DB
            $attachment->add();

            // Attach to product
            $attachment->attachProduct($product->id);
        }
  • Thanks 1
Link to comment
Share on other sites

  • 4 months later...
10 hours ago, aydam2728 said:

PrestaShop version : 8.0.1

Hi all,

The Crezzur code working partly.

The product is created but is not appear in the category page.

But I see him in the new products page, and it is in the products sheet.

To show him in the category page, I need to save the product (gui) without any change. Then the miracle happens, the product appears.

Do you have any idea to fix that ?

Thanks in advance

Please create another product/item with this script and inspect database tables such as `ps_category_product` do item is added into this table.
And etc...
Just more info is needed.

Link to comment
Share on other sites

19 hours ago, aydam2728 said:

Thanks for your response.

I was very focused on the problem without double-check the new object Crezzur with wrong category id in my case.

it's my fault, sorry for the disagreement

 

Everything is fine. Write here again if something does not work with the product creation programmatically.

Link to comment
Share on other sites

  • 4 months later...
  • 3 weeks later...

@Fer Poblete I know you asked not me, but just in case: yeah, there are model classes Feature and FeatureValue, but there are no methods in those classes which would do simple job like Feature::addFeatureToProduct($product_id, $feature_id, $feature_value_id) to create necessary relation between product, feature and feature value. So the simplest way was just do it directly via DB/sql.

If you have good proposal - just put your code here, contributions are always welcome!

(probably, good starting point would be Product->addFeatureProductImport)

(which also consists of single SQL query 😅)

Edited by mfdenis
additions (see edit history)
Link to comment
Share on other sites

  • 5 months later...

Hello everyone, and congratulations on this fantastic thread. I just have a couple of doubts regarding two things: 1. With this script, I add products automatically, but what happens if I want to only update the quantity of an existing product? 2. What if I want to add different sizes and colors for the product along with their respective availability? For example: White T-shirt - qty 10 - price €20; Black T-shirt - qty 5 - price €20

Link to comment
Share on other sites

On 1/25/2024 at 2:17 PM, Daniele Esposito said:

what happens if I want to only update the quantity of an existing product?

on old versions (like 1.6..1.7) you'd just make something like:
 

$my_product = new Product($id); // $id is id number of product to update
$my_product->quantity = $quantity; // $quantity is your desired quantity to set for this product
$my_product->save;

but product->quantity property was deprecated since version 8 or a but earlier, so, you should use StockAvailable:

StockAvailable::setQuantity(
		$product->id, // id of product
		null, //  this is for attributes
		(int)$quantity // desired quantity
);

But I'd recommend to read this part of documentation first:
https://docs.prestashop-project.org/v.8-documentation/user-guide/selling/managing-catalog/managing-stock/stock-overview
https://docs.prestashop-project.org/v.8-documentation/user-guide/selling/managing-catalog/managing-stock/stock-movements
 

Because it became overcomplicated now..

Link to comment
Share on other sites

On 1/25/2024 at 2:17 PM, Daniele Esposito said:

What if I want to add different sizes and colors for the product along with their respective availability?

First you have to create all these attributes. (probably, manually)

Then when you should apply required attributes to product (suspect that this is also better perform manually, it's called "combinations")

Then when every product's combination has its own reference number, you can easily update them like:

$comb_product = new Product($id);
$combinations = $comb_product->getAttributeCombinations();

foreach ($combinations as $combination) {

if ($combination['reference'] == $your_reference_for_this_combination) {

    $current_comb = new Combination($combination["id_product_attribute"]);

    // updating price of specific combination
    $current_comb->price = $your_price_of_this_combination;
    $current_comb->save();

    // updating stock of specific combination
    StockAvailable::setQuantity(
    $id, 
    $combination["id_product_attribute"],
    $quantity
    );
  }
}

This is just some guide code, you should set your reference number and price and test it.

  • Like 1
Link to comment
Share on other sites

Thank you for the response.

This integration with the code is very interesting, but it seems we are assuming that we precisely know both the references of the various product combinations and the product ID, which in most cases does not reflect reality.

The most realistic use case, or at least the one I am dealing with, is as follows: Integration with a corporate ERP: The products are exported in CSV format from the management system and sent to a directory on the website. This CSV file will be one for the parent products, which we'll call product.csv, and another will be combinations.csv. The first file will definitely have a unique reference, for example: A0GCTG00, which cannot be the ID that PrestaShop has assigned to the product upon its first upload, as the two systems are different. Therefore, the e-commerce products will be identified by the reference code, and that must be used to retrieve the product.

As for the size and color combinations in the combitions.csv file, there will definitely be the reference of the product A0GCTG00, and it will have as many rows as the color and size variants and quantities that the ERP system tells me, for example: A0GCTG00, Navy, 38, 10 pieces; A0GCTG00, Navy, 40, 5 pieces, and so on.

I believe that surely someone must have had experience with this use case, or at least I think it is the most common one. In this case, how do you think it should be approached?

Link to comment
Share on other sites

10 minutes ago, Daniele Esposito said:

but it seems we are assuming that we precisely know both the references of the various product combinations and the product ID, which in most cases does not reflect reality.

yep, that's why real reference number is always stored in product->reference property. Same about combinations. Each combination has exactly one reference number, which is the same that you have in your ERP. The rest is simple techniques to get data from cvs and save data to prestashop.

(to get product ID from its reference, use Product::getIdByReference() static method..)

Edited by mfdenis (see edit history)
Link to comment
Share on other sites

11 minutes ago, Daniele Esposito said:

As for the size and color combinations in the combitions.csv file, there will definitely be the reference of the product A0GCTG00, and it will have as many rows as the color and size variants and quantities that the ERP system tells me, for example: A0GCTG00, Navy, 38, 10 pieces; A0GCTG00, Navy, 40, 5 pieces, and so on.

Didn't you studied your ERP data structure yet? I think, that's the good point to start from. Then your "will definitely be" going to become "I have following data ...., and here is the problem:...." which would be more realistic to solve 😉

Link to comment
Share on other sites

1 minute ago, mfdenis said:

Didn't you studied your ERP data structure yet? I think, that's the good point to start from. Then your "will definitely be" going to become "I have following data ...., and here is the problem:...." which would be more realistic to solve 😉

Thank you for your response. My "will definitely be" was meant to generalize my discussion.

The real problem is that I do not have a precise reference to the product combination, or at least I find myself in a situation where the only information exported by the management system is the product reference to which this combination is connected, without a unique key for that combination. At this point, therefore, things evolve into two possible solutions, but I have a question. Let's start with the question: Is it possible to assign a reference code to a product combination just as it is done with products? In this case, would it also be possible to directly retrieve the size and color combination from the reference code?

Now let's consider what the two solutions might be:

  1. For each retrieved product, at each update, all combinations should be deleted and created again with all those available, as without a reference code of the combination itself, it is not possible to query and update it only.
  2. Having the reference code of the product combination, I believe everything becomes easier. Assuming that there is a method to directly query the combination through a unique code, or simply using the above-mentioned code and checking the reference code within the foreach.

Another question arises: if the main product to which the combinations are linked has pre-selected one of the combinations as the default, is it also possible to update this information? As it might happen that the one previously selected is no longer available.

Link to comment
Share on other sites

@Daniele Esposito Did you even installed any Prestashop? Did you open backend? Did you open sample products with combinations? Just start there, and read my posts above, because there are all answers to your questions, like:

5 minutes ago, Daniele Esposito said:

I have a question. Let's start with the question: Is it possible to assign a reference code to a product combination just as it is done with products?

 

3 hours ago, mfdenis said:

Then when every product's combination has its own reference number, you can easily update them like:

 

Link to comment
Share on other sites

58 minutes ago, mfdenis said:

@Daniele Esposito Did you even installed any Prestashop? Did you open backend? Did you open sample products with combinations? Just start there, and read my posts above, because there are all answers to your questions, like:

 

 

Ok, thank you very much.

I apologize for the repetitions, but I was simply trying to clarify and take note of your advice. I hope these comments can help others to clarify everything.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...