Jump to content

Add additional fields on product combination


Recommended Posts

Howdy everyone !

I'm trying to add additional fields on my product combinations in order to have custom meta titles for the combinations. 

 

My module definition:

class CombinationCustomFields extends Module
{

    public function __construct()
    {
        $this->name = 'combinationcustomfields';
        $this->tab = 'administration';
        $this->author = '';
        $this->version = '1.0';
        $this->need_instance = 0;
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Combination Custom Fields');
        $this->description = $this->l('Add additional fields on combinations');
        $this->ps_versions_compliancy = array('min' => '1.7.1', 'max' => _PS_VERSION_);
    }

    public function install()
    {
        if (!parent::install() || !$this->_installSql()
            || !$this->registerHook('displayAdminProductsCombinationBottom')
            || !$this->registerHook('actionAttributeCombinationSave')
        ) {
            return false;
        }

        return true;
    }

    public function uninstall()
    {
        return parent::uninstall() && $this->_unInstallSql();
    }

    /**
     * Add database fields
     * @return boolean
     */
    protected function _installSql()
    {
        $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "product_attribute "
            . "ADD meta_title_fr VARCHAR(255) NULL, "
            . "ADD meta_title_en VARCHAR(255) NULL;";

        $returnSql = Db::getInstance()->execute($sqlInstall);

        return $returnSql;
    }

    /**
     * Remove database fields
     * @return boolean
     */
    protected function _unInstallSql()
    {
        $sqlInstall = "ALTER TABLE " . _DB_PREFIX_ . "product_attribute "
            . "DROP meta_title_fr, "
            . "DROP meta_title_en";

        $returnSql = Db::getInstance()->execute($sqlInstall);

        return $returnSql;
    }

    public function hookDisplayAdminProductsCombinationBottom($params)
    {
        $combination = new Combination($params['id_product_attribute']);
        $this->context->smarty->assign(array(
				'id_product_attribute' => $params['id_product_attribute'],
                'meta_title_fr' => $combination->meta_title_fr,
                'meta_title_en' => $combination->meta_title_en,
            )
        );


        return $this->display(__FILE__, 'views/templates/hook/combinationfields.tpl');
    }


    public function hookActionAttributeCombinationSave($params)
    {
        $combination = new Combination($params['id_product_attribute']);
        $this->context->smarty->assign(array(
				'id_product_attribute' => $params['id_product_attribute'],
                'meta_title_fr' => $combination->meta_title_fr,
                'meta_title_en' => $combination->meta_title_en,
            )
        );

        $combination->meta_title_fr = Tools::getValue('meta_title_fr');
        $combination->meta_title_en = Tools::getValue('meta_title_en');

        $combination->save();

        return $this->display(__FILE__, 'views/templates/hook/combinationfields.tpl');

    }
}

 

My HTML for the admin hook:

<div class="m-b-1 m-t-1">
    <h2>{l s='Combination Custom Fields' mod='combinationcustomfields'}</h2>
    <fieldset class="form-group">
        <div class="col-lg-12 col-xl-4">
            <label class="form-control-label" for="combination_{$id_product_attribute}_attribute_meta_title_fr">{l s='Meta title FR' mod='combinationcustomfields'}</label>
            <input class="form-control" name="combination_{$id_product_attribute}[attribute_meta_title_fr]" id="combination_{$id_product_attribute}_attribute_meta_title_fr" type="text" value="{if $meta_title_fr != ''}{$meta_title_fr}{/if}">
            <br />
        </div>
        <div class="col-lg-12 col-xl-4">
            <label class="form-control-label" for="combination_{$id_product_attribute}_attribute_meta_title_en">{l s='Meta title EN' mod='cmp_combination_customfields'}</label>
            <input class="form-control" name="combination_{$id_product_attribute}[attribute_meta_title_en]" id="combination_{$id_product_attribute}_attribute_meta_title_en" type="text" value="{if $meta_title_en != ''}{$meta_title_en}{/if}">
            <br />
        </div>
    </fieldset>
    <div class="clearfix"></div>
</div>

 

My override of the combination class:

class Combination extends CombinationCore
{
    public $meta_title_fr;
    public $meta_title_en;
    /**
     * @see ObjectModel::$definition
     */
    public static $definition = [
        'table' => 'product_attribute',
        'primary' => 'id_product_attribute',
        'fields' => [
            'id_product' => ['type' => self::TYPE_INT, 'shop' => 'both', 'validate' => 'isUnsignedId', 'required' => true],
            'location' => ['type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'size' => 64],
            'ean13' => ['type' => self::TYPE_STRING, 'validate' => 'isEan13', 'size' => 13],
            'isbn' => ['type' => self::TYPE_STRING, 'validate' => 'isIsbn', 'size' => 32],
            'upc' => ['type' => self::TYPE_STRING, 'validate' => 'isUpc', 'size' => 12],
            'mpn' => ['type' => self::TYPE_STRING, 'validate' => 'isMpn', 'size' => 40],
            'quantity' => ['type' => self::TYPE_INT, 'validate' => 'isInt', 'size' => 10],
            'reference' => ['type' => self::TYPE_STRING, 'size' => 64],
            'supplier_reference' => ['type' => self::TYPE_STRING, 'size' => 64],
            'meta_title_fr' => ['type' => self::TYPE_STRING, 'size' => 64],
            'meta_title_en' => ['type' => self::TYPE_STRING, 'size' => 64],

            /* Shop fields */
            'wholesale_price' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'size' => 27],
            'price' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20],
            'ecotax' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'size' => 20],
            'weight' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isFloat'],
            'unit_price_impact' => ['type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isNegativePrice', 'size' => 20],
            'minimal_quantity' => ['type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId', 'required' => true],
            'low_stock_threshold' => ['type' => self::TYPE_INT, 'shop' => true, 'allow_null' => true, 'validate' => 'isInt'],
            'low_stock_alert' => ['type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'],
            'default_on' => ['type' => self::TYPE_BOOL, 'allow_null' => true, 'shop' => true, 'validate' => 'isBool'],
            'available_date' => ['type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'],
        ],
    ];

}

 

But when I save the product, the fields are in the request:

 

image.png.57c73da1d400ca772480b14b298fdd50.png.b62dbcb1c642edea5f43c8d1a92382d9.png

But I have a 400 Error:

image.png.32b1bcbf1e41db2b08f2b0808d6c687b.png.58019077044a974d95ce785e77defb8d.png


 

Any idea of my mistake here ?

 

All the best and thanks in advance for you help :) 

Link to comment
Share on other sites

  • 2 weeks later...
  • 2 months later...
On 7/20/2021 at 1:03 PM, korvent said:

No one has an answer ?

This problem comes from the smarty validator, or something like that, as I understand it.
The bottom line is that when we save the page, we have undeclared fields for the form.
Since the error is related to symphony, I tried to modify /src/PrestaShopBundle/Form/Admin/Product/ProductCombinationBulk.php

public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'allow_extra_fields' => false // adding this 
        ]);
    }

Unfortunately, this did not solve the problem. Although they write on the Internet what should help, maybe you tried to register in the wrong place?
Perhaps you have already found a solution to this task?

Link to comment
Share on other sites

  • 2 years later...

Any new thouughts on issue ?

ForPresta 8.1 and > it is explained in docs (with example) -> https://devdocs.prestashop-project.org/8/modules/sample-modules/extend-product-page/

but what about 8.0.1 ? How to make proper override in PRoduct and in Combination class?

Override of Product:

 * Override Class ProductCore
 * @param string|null $combination_erp_id
 * @param string|null $link
 */
class Product extends ProductCore {
    public $combination_erp_id;
    public $link;


    public function updateAttribute(
        $id_product_attribute,
        $wholesale_price,
        $price,
        $weight,
        $unit,
        $ecotax,
        $id_images,
        $reference,
        $ean13,
        $default,
        $location = null,
        $upc = null,
        $minimal_quantity = null,
        $available_date = null,
        $update_all_fields = true,
        array $id_shop_list = [],
        $isbn = '',
        $low_stock_threshold = null,
        $low_stock_alert = false,
        $mpn = null

        ){

        $combination = new Combination($id_product_attribute);

        if (!$update_all_fields) {
            $combination->setFieldsToUpdate([
                'price' => null !== $price,
                'wholesale_price' => null !== $wholesale_price,
                'ecotax' => null !== $ecotax,
                'weight' => null !== $weight,
                'unit_price_impact' => null !== $unit,
                'default_on' => null !== $default,
                'minimal_quantity' => null !== $minimal_quantity,
                'reference' => null !== $reference,
                'ean13' => null !== $ean13,
                'upc' => null !== $upc,
                'isbn' => null !== $isbn,
                'mpn' => null !== $mpn,
                'available_date' => null !== $available_date,
                'low_stock_threshold' => null !== $low_stock_threshold,
                'low_stock_alert' => null !== $low_stock_alert,
                'id_shop_list' => !empty($id_shop_list),
                'combination_erp_id' => !empty($combination_erp_id),
                'link' => !empty($link),
            ]);
        }

        $price = (float) str_replace(',', '.', (string) $price);
        $weight = (float) str_replace(',', '.', (string) $weight);

        $combination->price = $price;
        $combination->wholesale_price = (float) $wholesale_price;
        $combination->ecotax = (float) $ecotax;
        $combination->weight = $weight;
        $combination->unit_price_impact = (float) $unit;
        $combination->reference = pSQL($reference);
        $combination->ean13 = pSQL($ean13);
        $combination->isbn = pSQL($isbn);
        $combination->upc = pSQL($upc);
        $combination->mpn = pSQL($mpn);
        $combination->default_on = (bool) $default;
        $combination->minimal_quantity = (int) $minimal_quantity;
        $combination->low_stock_threshold = empty($low_stock_threshold) && '0' != $low_stock_threshold ? null : (int) $low_stock_threshold;
        $combination->low_stock_alert = !empty($low_stock_alert);
        $combination->available_date = $available_date ? pSQL($available_date) : '0000-00-00';

        $combination->combination_erp_id = pSQL($combination_erp_id);
        $combination->link = pSQL($link);

        if (!empty($id_shop_list)) {
            $combination->id_shop_list = $id_shop_list;
        }

        $combination->save();

        if (is_array($id_images) && count($id_images)) {
            $combination->setImages($id_images);
        }

        $id_default_attribute = (int) Product::updateDefaultAttribute($this->id);
        if ($id_default_attribute) {
            $this->cache_default_attribute = $id_default_attribute;
        }

        // Sync stock Reference, EAN13, ISBN, MPN and UPC for this attribute
        if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && StockAvailable::dependsOnStock($this->id, Context::getContext()->shop->id)) {
            Db::getInstance()->update('stock', [
                'reference' => pSQL($reference),
                'ean13' => pSQL($ean13),
                'isbn' => pSQL($isbn),
                'upc' => pSQL($upc),
                'mpn' => pSQL($mpn),
            ], 'id_product = ' . $this->id . ' AND id_product_attribute = ' . (int) $id_product_attribute);
        }

        Hook::exec('actionProductAttributeUpdate', ['id_product_attribute' => (int) $id_product_attribute]);
        Tools::clearColorListCache($this->id);

        return true;
    }

}

and combination override:

class Combination extends CombinationCore
{
    public $combination_erp_id;
    public $link;
    /**
     * @see ObjectModel::$definition
     */

    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null){


        Combination::$definition['fields']['combination_erp_id'] = [
            'type' => self::TYPE_STRING,
            'required' => false, 'size' => 255
        ];

        Combination::$definition['fields']['link'] = [
            'type' => self::TYPE_STRING,
            'required' => false, 'size' => 255
        ];
        parent::__construct($id_product, $full, $id_lang, $id_shop, $context);
    }

}

However the module still does not save data to SQL...

 

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...