Obtain product object in onBeforeCalculateProductPriceForQuantity event handler

  • Posts: 25
  • Thank you received: 2
2 weeks 5 days ago #343569

-- HikaShop version -- : 4.6.1
-- Joomla version -- : 4.1.5
-- PHP version -- : 8.0
-- Browser(s) name and version -- : FireFox 100.0.2 (64-bit)
-- Error-message(debug-mod must be tuned on) -- : trim(): Argument #1 ($string) must be of type string, array given

Hi Nicolas,

Before explaining the problem I encounter, let me first explain what I am trying to do.

In the onBeforeCalculateProductPriceForQuantity() event handler of a system plugin, I try to construct a product object, including all related information. I need such an object, including any associated custom fields and other data, to do some non standard price calculations. The way I try to obtain that product object is as follows:

  1. Get a reference to the product class: $productClass = hikashop_get('class.product')
  2. Load the product using the product_id in the method parameter: $productClass->getProducts([$product->product_id])
  3. Load additional product data using the now available product(s) in $productClass: $productClass->loadProductsListingData([$productClass->products[(string) $product->product_id]], )
  4. Load product options in a similar fashion: $productClass->loadProductOptions(([$productClass->products[(string) $product->product_id]])

I feel very uncomfortable with the way I managed to get what I need, but in the old, complicated and confusing, non-modular codebase that is HikaShop is, I saw no other way. It gave me the product object I need though and everything seems to work just fine, up to the point that I go to the checkout page and change the ordered amount of a product. This also triggers my onBeforeCalculateProductPriceForQuantity() method, which should be fine, but apparently isn't. This is what I see happening:
  1. When changing the product quantity, an AJAX call is made which eventually calls hikashopCartClass::loadFieldsForProducts(), which in turn calls hikashopFieldClass::getData().
  2. That getData() method fills a static variable with product data. Items in that data collection contain an attribute field_categories, which contains a comma separated string of category id's.
  3. Somewhat later, my onBeforeCalculateProductPriceForQuantity() event handler is called.
  4. The call I make to hikashopProductClass::loadProductsListingData() (see 2. above) in turn calls hikashopProductClass::loadCustomItemFieldsForProductsListing().

That last function call is where the problem starts. For some reason, it turns the comma separated string of category id's mentioned above into an array. Then, when hikashopCartClass::loadFieldsForProducts() is called a second time around as part of the same AJAX call, an error occurs because an attempt is made to trim() an array, which obviously is not allowed. This results in the following message in the shopping cart area: trim(): Argument #1 ($string) must be of type string, array given.

What must I do to resolve this?

Last edit: 2 weeks 4 days ago by pjdevries.

Please Log in or Create an account to join the conversation.

  • Posts: 76141
  • Thank you received: 11914
2 weeks 4 days ago #343589


I suppose that between step 2 and 3, the field's field_categories is changed and since the returned field data from the hikashopFieldClass::getData is returned as a reference,it modifies the data of field_categories in the static cache variable of hikashopFieldClass::getData

So there are two things to do:
1. Before calling hikashopProductClass::loadProductsListingData you can run this code:

$fieldClass = hikashop_get('class.field');
$fieldClass->getData('reset', 'item');
That will reset the cache data of the custom item fields and thus it will prevent the error.

2. On our end, we'll improve the code of hikashopProductClass::loadProductsListingData so that it can properly handle field_categories regardless of whether it is a string or an array.

Please Log in or Create an account to join the conversation.

  • Posts: 25
  • Thank you received: 2
2 weeks 4 days ago #343592

Hi Nicolas,

Thanx for your reply and proposed solution. It seems to work, but just a bit different. Because all my code, following the hikashopProductClass::loadProductsListingData() call, currently relies on the categories attribute being an array, I call the reset not before but after loadProductsListingData(). That way, both my code and the core HikaShop code keeps working, hopefully. Please make sure to document any changes you make to this logic, so we keep an eye on it before updating without consideration.

As for improving the HikaShop code base, I suggest you start working towards a cleaner, better maintainable and less complex code base. Get rid of (most) statics and of data passed and manipulated by reference. In many cases, for arrays and objects, a reference it is not even necessary. Refactor all your classes, starting with a well designed Product class, which should be and do more than be a Product :) Turn factory methods, that currently manipulate static objects or objects passed by reference, into methods that return properly typed objects. For instance, turn hikashopFieldClass::getData() into something like ProductFactory::createProduct(), which returns an object of type (class) Product. Keep things simple. Do not create methods of several hundreds, sometimes 1000 or more lines of code. Keep the SOLID principles in the back of your head ( en.wikipedia.org/wiki/SOLID ).

I think cleaning up your code base is very important, because PHP is getting more critical and less forgiving with every new release. I'm afraid HikaShop will crash completely by the time we reach PHP 9.

The following user(s) said Thank You: nicolas

Please Log in or Create an account to join the conversation.

Time to create page: 0.059 seconds
Powered by Kunena Forum