Harry
Harry

Reputation: 167

Update Product Features via PHP in Prestashop

I would like to change the value from custom features (Product Features) (Artikeleigenschaften) in my own class.

To change product values is very simple:

$productObj = new Product($produktId);
$productObj->setFieldsToUpdate(array('ean13'));
$productObj->ean13 = "johndoo";
$productObj->save();

But is there a similar way to change the Product Features?

Upvotes: 4

Views: 4065

Answers (5)

tsw
tsw

Reputation: 1

None of these suggestions worked for me. I had few requirements for this:

  1. import should be able to run multiple times without duplicating a feature in product or feature values list
  2. custom values should not be deleted even if they have the same "size" feature
  3. no need to handle languages or multistore (thanks to datasource for only supporting one language and one shop)

This worked for me

    /**
     * Add or customize feature value to product feature and assign it to product
     *
     * Creates the feature and the value if they dont exists already
     */
    private function _upsertFeatureValueToProduct($product, $featureName, $featureValue, $custom = false)
    {
        $id_lang = null;

        // this creates the feature if required and always returns the id_feature
        $id_feature = (int) \Feature::addFeatureImport($featureName);

        if ($custom) {
            // create or find a custom value
            $id_feature_value = (int) \FeatureValue::addFeatureValueImport($id_feature, $featureValue, $product->id, $id_lang, $custom);
            // change the existing value to new
            $feature_value = new \FeatureValue($id_feature_value);
            // in all languages (source only gives one language data)
            $feature_value->value = array_fill_keys(\Language::getIDs(false), $featureValue);
            $feature_value->update();
            // assign the feature to the product (not strictly required if this was a update but doesn't hurt)
            Product::addFeatureProductImport($product->id, $id_feature, $id_feature_value);
        } else {
            // non custom values. we first need to add the value and then assign it
            // prestashop uses lang to find the existing feature. use the default language
            $id_lang = \Configuration::get('PS_LANG_DEFAULT');

            // add or get the feature id for this value
            $id_feature_value = (int) \FeatureValue::addFeatureValueImport($id_feature, $featureValue, null, $id_lang, $custom);

            // NOTE: product can have the same feature multiple times. But in this case we only get one value from source and want to update the feature
            // so before adding the featurevalue delete existing feature assignments
            $this->_deleteProductFeatureValueAssignments($product->id, $id_feature);

            // assign the feature to the product
            Product::addFeatureProductImport($product->id, $id_feature, $id_feature_value);
        }
        // required? doesn't hurt :)
        \Feature::cleanPositions();
    }

    /**
     * Delete product feature value assingments
     *
     * There is no api for this.. idea stolen from
     *
https://github.com/PrestaShop/PrestaShop/blob/2c059b93062ce25bbde22355982e651215544e81/classes/Product.php#L2673
     *
     * TODO: open feature request, and remove this when prestashop has an api for this.
     */
    private function _deleteProductFeatureValueAssignments($id_product, $id_feature)
    {
        $sql = '
        DELETE fp
        FROM `' . _DB_PREFIX_ . 'feature_product` fp
        LEFT JOIN `' . _DB_PREFIX_ . 'feature_value` fv
            ON fv.`id_feature_value` = fp.`id_feature_value`
        WHERE
            fp.`id_product` = ' . (int) $id_product .
            ' AND fp.`id_feature` = ' . (int) $id_feature .
            ' AND `custom`=0
        ';

        return \Db::getInstance()->execute($sql);
    }

now I can call this all day long without getting duplicate values in features or in the products

$this->_upsertFeatureValueToProduct($product, 'size', 'L');
$this->_upsertFeatureValueToProduct($product, 'size', 'XL');
$this->_upsertFeatureValueToProduct($product, 'size', 'custom', true); // one custom size just for fun
$this->_upsertFeatureValueToProduct($product, 'size', 'L');

Leaves me with two ready made feature values L and XL and a product with size=L and size=custom

screenshot of product features

There might still be bugs, just got this working :D

Upvotes: 0

Nishan than
Nishan than

Reputation: 69

Product::addFeatureProductImport($productObjc->id, $id_feature, $id_feature_value); 

This will assign the feature and its value to the product. But we need the id of the feature and values. The below code is working for me.

  $feature = new Feature();
            $feature->name = [
                $lang_id => "string"
            ];
            $feature->position = Feature::getHigherPosition() + 1;
            $feature->add();
            $featurevalue = new FeatureValue;
            $featurevalue->id_feature = $feature->id;
            $featurevalue->value = [
                $lang_id => "string"
           ];
            $featurevalue->add();
$product->addFeatureProductImport($product->id, $feature->id, $featurevalue->id);

Upvotes: 0

Eryk Wróbel
Eryk Wróbel

Reputation: 445

I created this function based upon ImportControlloer. Add this function to Product.php override and use it

 /**
 * Adds a feature value to a Product. If You want to have feature selected from dropdown leave $custom to false.
 * @param int|$id_product
 * @param int|$id_feature_group
 * @param string|$feature_value
 * @param bool|true $custom
 * @param int|$id_lang
 * @return bool
 */
public static function setFeatureValueToProduct($id_product, $id_feature_group, $feature_value, $custom = false, $id_lang)
{
    $group_feature_obj = Feature::getFeature($id_lang, $id_feature_group);

    if ($group_feature_obj){
        $feature_name = $group_feature_obj['name'];
        $feature_value = $feature_value ? trim($feature_value) : '';
        $position = false;

        if (!empty($feature_name) && !empty($feature_value)) {
            $id_feature = (int)FeatureCore::addFeatureImport($feature_name, $position);

            $id_feature_value = (int)FeatureValue::addFeatureValueImport($id_feature, $feature_value, $id_product, $id_lang, $custom);
            if (Product::addFeatureProductImport($id_product, $id_feature, $id_feature_value)){
                Feature::cleanPositions();
                return true;
            } else {
                return;
            }
        }
    } else {
        return;
    }
}

Example of use:

Product::setFeatureValue(1597, 15,  'Dracarys', false, 1); 

It will upload Dropdown value "Dracarys" into product with ID 1597 and group feature with ID 15. If You want to make it custom value (not-predefined) just set 4th parametr to true.



Upvotes: 1

Harry
Harry

Reputation: 167

Here my solution: To Update Product Features you have to update 2 tables in your Database: ps_feature_value and ps_feature_value_lang

Do this with:

$id_feature_value = (int)FeatureValue::addFeatureValueImport
( 
  (int)$feature['id_feature'],
  $newFeatureValue,
  (int)$produktId,
  $this->context->language->id,
  1 // $custom
);
Product::addFeatureProductImport(
    $produktId, (int)$feature['id_feature'], $id_feature_value
);

To Update Features with NOT custom Values use:

$arr = FeatureValue::getFeatureValueLang($id_dropdown);
foreach ( $arr AS $item)
    if ($item['id_lang'] == $this->context->language->id)
    {
        $feature_val_name = $item['value'] ;
    }

$id_feature_value = (int)FeatureValue::addFeatureValueImport
( 
   (int)$feature['id_feature'],
   $feature_val_name,
   $product->id,
   $this->context->language->id, 
   false 
);
$product->addFeatureProductImport( $product->id , (int)$feature['id_feature'], $id_feature_value );

Upvotes: 2

elPresta
elPresta

Reputation: 648

Mh, Ive never tried to do this, but you could try this:

Product::addFeatureProductImport($productObjc->id, $id_feature, $id_feature_value); 

Upvotes: 1

Related Questions