Code adjustments for Virtuemart 3

Written by Max Milbers on .

Adjustments to Extensions

The main changes are done in the custom plugins. Shipment and payment plugins just need an updated xml using the J2.5 style for parameter declaration. VM3 uses the JInputFields of Joomla now. If you want that your payment plugins run in VM2 and VM3, you must exchange all JRequest to vRequest or use the new Joomla class JInput. It is a matter of taste.

The other big changes are done for the cart and the checkout process. That means templates and OPCs must be adjusted to use the new cart. Changes for the new cart were made in product handling and the session. The new cart stores in the session only the product id, the quantity and the customproduct data. Getting the cart creates the products with the stored attributes. Now the core is always directly manipulating the products in the cart. So now we can easily change the product attributes through plugins.

The whole redirecting process got overhauled. The system works more based on the conditions of the cart. So the redirects ask the cart if it is checkout, confirm and so on. This makes the redirecting a lot more reliable. Overrides developed for a VM2.6 layout should still work, even old templates without overrides should still work.

Templating and Layouts

In VM3 we introduce a new feature ShopFunctionsF::renderVmSubLayout($layoutName,AssocArray) you can call with it very easy small sublayouts, or minilayouts. They are stored in FE/sublayouts and can be overriden using /templates/yourtemplate/html/com_virtuemart/sublayouts.

The main differences are in the cart layout. We have a new feature which allows us to display shopperfields in the cart. It is very easy to create your own userfields for the cart. The result is stored in the order_userinfo with the BT address. The name of the custom userfield respectivly shopperfield is the name of the layout file. You can easily overwrite it. For example you can overrride the tos layout copying the FE/sublayouts/tos.php to /yourtemplate/html/com_virtuemart/sublayouts. Check the variable $viewData to see what is available. If this is not sufficient, you can still write a userfield plugin of course.

The cart itself has some mandatory fields to work properly. The form can start at begin and end at the bottom. The system works now with buttons and not with links anylonger. The reason is that we always store any changes in the cart, which makes OPC per template a lot easier. To achieve this we use the names of the buttons as action. Therefore the hidden fields of the form are always the same now.

<input type='hidden' name='order_language' value='<?php echo $this->order_language; ?>'/>
<input type='hidden' name='task' value='updatecart'/>
<input type='hidden' name='option' value='com_virtuemart'/>
<input type='hidden' name='view' value='cart'/>

You need for the product rows as input type at least 

<input type="text" name="quantity[<?php echo $pkey; ?>]" value="<?php echo $prow->quantity ?>" />
and as buttons name="updatecart.<?php echo $pkey ?>" respectivly name="delete.<?php echo $pkey ?>"

as checkout Button use echo $this->checkout_link_html;

Also the layout edit_address in the user view need similar adjustments to work properly (check building of the action url and the hidden fields at the end).

For the new ajax reloading of product content it is important to have echo vmJsApi::writeJS(); at end of the override file YOURSITE\templates\YOURTEMPLATE\html\com_virtuemart\productdetails\default.php

Adjustments to Customplugins

The customfield params are now directly attached to the plugin. Furthermore the fields keeping the custom parameters are renamed and reorganised.

Changed database fields

The custom prototyp still uses the field custom_params. Now it is always loaded first and works like a pattern, respectively default values for the customfields attached to products. The fieldname of the product customfield parameter is now customfield_params:
In table #__virtuemart_product_customfields
 custom_value  => customfield_value
 custom_price   => customfield_price
 custom_param => customfield_params

Furthermore in the custom_field_desc is now custom_desc:
In table #__virtuemart_customs
 custom_field_desc => custom_desc

So what does that mean for you

You need a switch in the constructor of your customplugin

if(!defined('VM_VERSION') or VM_VERSION < 3){
  $this->setConfigParameterable ('custom_params', $varsToPush);
} else {
  $this->setConfigParameterable ('customfield_params', $varsToPush);
}

Be aware that these params are handled by the core for you now. You do not need to load them in your functions, now the variables are always accessible by $this->varName respectivly the given method. For example in the trigger plgVmOnDisplayProductFEVM3 it is $group->varName.

Furthermore you need new triggers
function plgVmDeclarePluginParamsCustomVM3(&$data){
  return $this->declarePluginParams('custom', $data);
}
function plgVmGetTablePluginParams($psType, $name, $id, &$xParams, &$varsToPush){
  return $this->getTablePluginParams($psType, $name, $id, $xParams, $varsToPush);
}

another switch in the plgVmOnProductEdit trigger

if(!defined('VM_VERSION') or VM_VERSION < 3){
  $this->parseCustomParams ($field);
  $paramName = 'custom_param';
} else {
  $paramName = 'customfield_params';
}

Changed/added triggers

plgVmOnDisplayProductFE($field, &$idx, &$group) is now plgVmOnDisplayProductFEVM3(&$product,&$group). The trigger plgVmOnDisplayProductVariantFE got completly removed and is now replaced byplgVmOnDisplayProductFEVM3.
We removed the second parameter, it is not necessary any longer, the core handles it. It makes a lot sense to call a unified function, because the rest is almost the same, you can just remove "$this->parseCustomParams ($field);" for VM3.

The triggers
plgVmOnViewCart, plgVmOnViewCartModule, plgVmDisplayInOrderFE and plgVmDisplayInOrderBE also use the suffix VM3, all have different parameters, instead of ($product, $row, &$html) we now have (&$product, &$productCustom, &$html)

The difference is nice to see in the textinput.php:

VM2

function plgVmOnViewCart($product,$row,&$html) {
		if (empty($product->productCustom->custom_element) or $product->productCustom->custom_element != $this->_name) return '';
		if (!$plgParam = $this->GetPluginInCart($product)) return '' ;

		foreach($plgParam as $k => $item){
			if(!empty($item['comment']) ){
				if($product->productCustom->virtuemart_customfield_id==$k){
					$html .=''.JText::_($product->productCustom->custom_title).' '.$item['comment'].'';
				}
			}
		}
		return true;
	}

VM3

function plgVmOnViewCartVM3(&$product, &$productCustom, &$html) {
		if (empty($productCustom->custom_element) or $productCustom->custom_element != $this->_name) return false;

		if(empty($product->customProductData[$productCustom->virtuemart_custom_id][$productCustom->virtuemart_customfield_id])) return false;
		foreach( $product->customProductData[$productCustom->virtuemart_custom_id] as $k =>$item ) {
			if($productCustom->virtuemart_customfield_id == $k) {
				if(isset($item['comment'])){
					$html .=''.JText::_($productCustom->custom_title).' '.$item['comment'].'';
				}
			}
		}
		return true;
	}

So a VM3 customplugin is compared to a VM2 customplugin is more data safe, less load and easier to write. Also nice to see here:

VM2
function plgVmDisplayInOrderBE(&$item, $productCustom, &$html) {
  if(!empty($productCustom)){
    $item->productCustom = $productCustom;
  }
  if (empty($item->productCustom->custom_element) or $item->productCustom->custom_element != $this->_name) return '';
  $this->plgVmOnViewCart($item,$productCustom,$html); //same render as cart
}

VM3
function plgVmDisplayInOrderBEVM3(&$item, &$productCustom, &$html) {
  $this->plgVmOnViewCartVM3($item,$productCustom,$html);
}

The trigger plgVmCalculateCustomVariant ($product, $productCustomsPrice, $selected, $modificatorSum = 0) got renamed to plgVmPrepareCartProduct(&$product, &$customfield, $selected, &$modificatorSum = 0) and is now a more mighty. You can use it to modfiy the product in your cart, price, weight, dimension, text, name, whatever. The parameters have the same name, but different meaning. The variable $selected keeps the data of the right method/plugin coming from the form. Be aware that the variable $modificatorSum has to be modified. This trigger is also called for any cart call and is suitable to change the attributs of your product.

The old variable and name of the array in the form
customPlugin['virtuemart_customfield_id']['pluginname'][name] is replaced by
customProductData['virtuemart_product_id']['virtuemart_custom_id']['virtuemart_customfield_id'][name].

Check your layouts and the code which was in the trigger plgVmOnDisplayProductVariantFEVM3

Adjustments to userfield plugins

Quite similar to the customplugins. Triggers are unchanged, but the params field got renamed to userfield_params. So we need in our constructor

if(!defined('VM_VERSION') or VM_VERSION < 3){ $this->setConfigParameterable ('params', $this->varsToPush); } else { $this->setConfigParameterable ('userfield_params', $this->varsToPush); }

and in our plgVmOnUserfieldDisplay

if(!defined('VM_VERSION') or VM_VERSION < 3){ $this->AddUserfieldParameter ($field->params); } else { $this->AddUserfieldParameter ($field->userfield_params); }

p>and in our plgVmOnBeforeUserfieldSave

if(!defined('VM_VERSION') or VM_VERSION < 3){ $tableClass->setParameterable ('params', $vars); } else { $tableClass->setParameterable ('userfield_params', $vars); }