Modificare il modulo carrello di default di Virtuemart 3 per aggiungere funzionalità avanzate, come l'eliminazione dei prodotti o la modifica delle quantità via Ajax

 

Ciao Developer,
con questo mio articolo vorrei spiegare come costruire un modulo avanzato in Joomla per la visualizzazione del carrello dell'ecommerce Virtuemart in una pagina.

Premetto che è necessario avere dimestichezza con il modello MVC di Joomla, e sulle metodologie di override dei moduli nei propri template.

Di default, il modulo del carrello viene visualizzato semplicemente inserendo nome dell'articolo, quantità nel carrello, prezzo e tipologia di campi personalizzati (se presenti). Quello che andremo a fare sarà modificare il modulo carrello di default per far sì che visualizzi il pulsante di eliminazione prodotto per ogni riga, e i pulsantini per incrementare o decrementare la quantità. Inoltre, verrà inserito il numero di prodotti presenti nel carrello con uno stile tipo "badge" (per capirci, come le notifiche presenti sopra le icone delle app dei dispositivi mobile.

Bene, prima di tutto ho creato nella cartella del mio template
templates->[nome template]->html la cartella 'mod_virtuemart_cart' (lo stesso nome del modulo del carrello originale di Virtuemart).
A questo punto, occorre copiare nella cartella appena creata questi 2 file:

  • il file "default.php", presente nella cartella "modules->mod_virtuemart_cart->tmpl",
  • il file "update_cart.js" nella cartella "modules->mod_virtuemart_cart->assets->js

Eseguite queste semplici operazioni, andiamo a "sporcarci le dita" sulla tastiera per modificare e modellare il codice dei file secondo le nostre esigenze.
Per una questione di semplicità e di linearità (ADORO le cose semplici, il mondo è abbastanza complicato già di suo), ho adattato il layout del carrello con una visualizzazione mediante la struttura html <table>, in modo da organizzare i campi in righe e colonne.

subito dopo la solita nota dichiarazione

defined('_JEXEC') or die('Restricted access');

ho inserito le seguenti linee di codice:

1 $app = JFactory::getApplication();
2 $path = JURI::base(true).'/templates/'.$app->getTemplate().'/html/'.$module->module;
3 vmJsApi::addJScript('/templates/'.$app->getTemplate().'/html/'.$module->module."/update_cart.js",false,false);
  • Ottengo l'istanza dell'applicazione Joomla con JFactory::getApplication() e la memorizzo nella variabile $app (mi servirà solo per accedere al nome del template in uso, nella seconda linea)
  • compongo il percorso a questo modulo nel formato "http(s)://nomesito.com/templates/[mio template]/html/mod_virtuemart_cart"
  • aggiungo il file javascript update_cart.js presente nel modulo personalizzato.
    La funzione vmJsApi::addJScript richiede 3 parametri:
    a) il percorso (relativo) allo script,
    b) un booleano che indica se il parametro a) contiene un nome di file oppure un codice javascript
    c) un booleano che indica l'attributo "defer" dello script, va impostato su false nel caso in cui si utilizzino librerie come jQuery (quindi questo caso).

a questo punto, creo un file nella cartella del modulo per fornire il css personalizzato al modulo, e lo chiamo "cart-style.css", dopodichè inserisco la seguente linea di codice per importarlo nel modulo

il file cart-style.css conterrà le definizioni, indovinate un pò? Dello stile visivo del carrello ovviamente.

<link rel="stylesheet" href="/<?php echo $path.DS ?>cart-style.css" /> //utilizzo la variabile $path precedentemente creata per accedere al percorso del modulo personalizzato.

 Ora la domanda è:

Come elimino un prodotto, oppure come ne modifico la quantità, senza andare nella pagina del processo d'ordine, ma comodamente dal mio moduletto del carrello?

E' presto detto!
Dobbiamo innanzitutto capire come virtuemart gestisce il modulo del carrello, e come esso venga aggiornato via ajax ogni qualvolta faccio click sul pulsante "Nel Carrello" di un qualsiasi prodotto.
Virtuemart utilizza un hook nel file update_cart.js, al momento del completamento del caricamento documento:

$(document).on("updateVirtueMartCartModule","body",Virtuemart.customUpdateVirtueMartCartModule);

Quando un prodotto viene aggiunto, via javascript viene eseguito un jQuery("body").trigger("updateVirtueMartCartModule"), in questo modo viene aggiornato il carrello.
In questa semplice versione del carrello, proposta in questo articolo, ho pensato di inserire alcune informazioni basiche ma che mettessero in risalto a colpo d'occhio tutti i dati fondamentali del carrello, quindi:

  • L'icona badge sopra l'immagine del carrello
  • L'immagine del prodotto nella riga
  • La quantità presente nel carrello
  • Il pulsante per eliminare una riga dal carrello.

avrò, nel mio modulo carrello, una parte di Header che figurerà più o meno così: 

 

Come potete vedere, il badge di notifica sul carrello, mostra immediatamente quanti prodotti ho già nel carrello.

La riga dell'articolo invece verrà strutturata in questo modo:

 
In questo caso ho anche dei custom fields di Virtuemart che vengono visualizzati, ma potrebbero anche non esserci  

 

 

Ora, devo fare due cose: predisporre il "template" che Virtuemart utilizza per aggiungere righe al carrello via Ajax. il template può essere identificato nel file default.php perchè è inserito all'interno di un DIV con id="hiddencontainer".

il mio template (vuoto) personalizzato sarà il seguente:

	<div class="hiddencontainer" style=" display: none; ">
		<div class="vmcontainer">
			<div class="product_row">
				<table>
				<tr>
				<td><span class="image"></span></td>
				<td>
					<span class="product_name"></span><br/>
					<div class="customProductData"></div>
				</td>
				<td>
				<span class="quantity">
					<span class="quantity-box"></span>
					<span class="quantity-controls js-recalculate">
						<input type="button" class="quantity-controls quantity-plus">
						<input type="button" class="quantity-controls quantity-minus">
					</span>
				</span></td>
				
			<?php if ($show_price and $currencyDisplay->_priceConfig['salesPrice'][0]) { ?>
				<td ><div class="subtotal_with_tax" style="float: right;"></div></td>
				
			<?php } ?>
				<td><div style="float:right;cursor:pointer;width:18px;height:18px;background: url(/images/delete-icon.png) no-repeat 0 0;" class="delete_btn" del-id="0" title="Elimina dal carrello"></div></td>
				</tr>
				</table>
			</div>
		</div>
	</div>

N.B.: Prestate attenzione alle classi di stile  "image", "product_name", "customProductData","quantity",  "subtotal_with_tax": sono chiamate in questo modo per una ragione, che spiegherò appena analizzeremo il file update_cart.js

Come potete vedere quando creiamo il pulsante per eliminare la riga,con classe delete_btn, impostiamo anche un attributo personalizzato del-id a 0. Questo sarà l'indice della riga del carrello da cancellare. A differenza di quanto si può pensare, per eliminare un prodotto dal carrello Virtuemart, è necessario eliminare l'elemento alla posizione in cui è memorizzato il prodotto nel carrello, e non l'id del prodotto da eliminare (Trovare questa informazione ha richiesto grandi ricerche, visto che sulla documentazione hanno ben pensato di ometterlo, o di renderlo alquanto criptico da capire).

Andiamo ora ad analizzare il cuore della procedura, il file "update_cart.js"

	Virtuemart.customUpdateVirtueMartCartModule = function(el, options){
		var base 	= this;
		base.el 	= $(".vmCartModule");
		base.options 	= $.extend({}, Virtuemart.customUpdateVirtueMartCartModule.defaults, options);

		base.init = function(){
			$.ajaxSetup({ cache: false })
			$.getJSON(Virtuemart.vmSiteurl + "index.php?option=com_virtuemart&nosef=1&view=cart&task=viewJS&format=json" + Virtuemart.vmLang,
				function (datas, textStatus) {
					base.el.each(function( index ,  module ) {
						$(module).find(".vm_cart_products").html("");
						if (datas.totalProduct > 0) {
							
							$.each(datas.products, function (key, val) {
								
								var delId = key;
								var updId = key;
								var cloned = $(module).find(".hiddencontainer .vmcontainer .product_row").clone();
								cloned.appendTo( $(module).find(".vm_cart_products") );
								$(module).find(".vm_cart_products .delete_btn").last().attr('del-id', delId);
								$(module).find(".vm_cart_products .quantity-box").last().attr('upd-id', updId);
								$(module).find(".vm_cart_products .quantity-plus").last().attr('upd-id', updId);
								$(module).find(".vm_cart_products .quantity-minus").last().attr('upd-id', updId);
								
								$.each(val, function (key, val) {

									[...]

									var _suffix = '';
									if (key == 'quantity')	
										_suffix = '-box'
									
									
									$(module).find(".vm_cart_products ." + key + _suffix).last().html(val);
								});
							});
						}
						$(module).find(".show_cart").html(		datas.cart_show);
						if (datas.totalProduct > 0){
							$(module).find(".notify-badge").html(datas.totalProduct);
							$(module).find(".notify-badge").css('display','block');
						}
						else
							$(module).find(".notify-badge").css('display','none');

						
						$(module).find(".total").html(		datas.billTotal);
						[...]
					});
				}
			);
		};
		base.init();
	};

Questa è senza dubbio la funzione che svolge gran parte del lavoro, incluso popolare la riga con i dati del prodotto. Viene clonato il template vuoto del file "default.php", quindi viene inoltrata una richiesta ajax al componente Virtuemart con getJSon() per estrapolare il carrello in formato JSON (task=viewJS&format=json). A questo punto viene restituito un oggetto così composto: ID_POSIZIONE_PRODOTTO => OGGETTO_PRODOTTO. Ed è proprio questo ID_POSIZIONE PRODOTTO che dobbiamo salvare nell'attribute "del-id" del pulsante per cancellare la riga!

Viene impostato anche un attributo "upd-id" sulle classi "quantity-box", "quantity-plus" e "quantity-minus". In questo modo, potremo specificare in caso di incremento o decremento della quantità, a quale riga di prodotto ci stiamo riferendo.

Nel ciclo for...each, applicato ad OGGETTO_PRODOTTO, vengono usate la coppia chiave ->valore, dove la chiave corrisponde al nome delle varie classi di stile inserite nel template nel file default.php (capito ora perchè dovevate prestarci attenzione? se si modifica il nome di queste classi, l'associazione automatica fra il valore della proprietà e il campo nel template va a farsi benedire!). Infine si procede ad aggiornare l'icona badge di notifica, e l'impostazione del costo totale.

Virtuemart.customUpdateQtyVirtueMartCartModule = function(el,options){
	var prodId = el.target.getAttribute('upd-id');
	var qtyNum = $(".vmCartModule").find('.vm_cart_products .quantity-box[upd-id="'+prodId+'"]').html();
	if (el.target.hasClass("quantity-plus"))
		qtyNum++;
	else
		qtyNum--;	
	$.ajaxSetup({ cache: false });
var urlToRecall = Virtuemart.vmSiteurl + "index.php?option=com_virtuemart&view=cart&task=updatecart&quantity["+prodId+"]="+qtyNum;
		$.ajax({
				url: urlToRecall,
				type: "POST",
				data: {nosef:"1"},
		}).done(function(datas, textStatus) {
					
					jQuery('body').trigger('updateVirtueMartCartModule');
	});
		
};

 Questa funzione altro non fa che inoltrare una richiesta ajax per variare la quantità di una specifica riga di prodotto. Nell'url passato ad $ajax, potete notare la request var "quantity[POSIZIONE_RIGA], a cui viene assegnato l'incremento o il decremento della quantità. Viene chiamata poi la funzione di update del carrello per aggiornare tutti i dati.

Virtuemart.customDeleteVirtueMartCartModule = function(el, options){

	var prodId = el.target.getAttribute('del-id');
	$.ajaxSetup({ cache: false });
	var urlToRecall = Virtuemart.vmSiteurl + "index.php?option=com_virtuemart&view=cart&task=delete";
		$.ajax({
				url: urlToRecall,
				type: "POST",
				data: {cart_virtuemart_product_id: prodId,
				nosef:"1"},
		}).done(function(datas, textStatus) {
					jQuery('body').trigger('updateVirtueMartCartModule');
		});
		
	};


 In questa funzione altro non si fa che inoltrare una richiesta ajax al componente Virtuemart con task=delete e passando la posizione della riga da eliminare nella request var "cart_virtuemart_product_id. Viene chiamata poi la funzione di update del carrello per aggiornare tutti i dati.

N.B.: dal momento che se viene eliminato un prodotto Virtuemart esegue l'unset della posizione dell'array, non è detto che le posizioni siano contigue, e questo spiega il motivo per cui sono stati usati gli attributi personalizzati "del-id" e "upd-id"

jQuery(document).ready(function( $ ) {
	$(document).off("updateVirtueMartCartModule","body",Virtuemart.customUpdateVirtueMartCartModule);
	$(document).on("updateVirtueMartCartModule","body",Virtuemart.customUpdateVirtueMartCartModule);
	$(document).off("click",".delete_btn",Virtuemart.customDeleteVirtueMartCartModule);
	$(document).on("click",".delete_btn",Virtuemart.customDeleteVirtueMartCartModule);
	$(document).off("click",".quantity-plus",Virtuemart.customUpdateQtyVirtueMartCartModule);
	$(document).on("click",".quantity-plus",Virtuemart.customUpdateQtyVirtueMartCartModule);
	$(document).off("click",".quantity-minus",Virtuemart.customUpdateQtyVirtueMartCartModule);
	$(document).on("click",".quantity-minus",Virtuemart.customUpdateQtyVirtueMartCartModule);

});

Infine, vengono assegnate le funzioni create in precedenza ai vari handler.

 

Insomma, la modifica del carrello di Virtuemart richiede un pò di attenzione per essere personalizzato al meglio, ma offre sicuramente delle possibilità di potenziamento non indifferenti.

Ovviamente, troverete allegato a questo articolo, il file zippato contenente la cartella da copiare nella cartella html del vostro template per poter utilizzare e modificare o personalizzare il modulo del carrello di Virtuemart

... I moduli di gestione avanzata del carrello di Virtuemart, hanno un costo sull mercato da 16$ a 25$ ... In questo articolo oltre a vari spunti per personalizzare il carrello, trovi anche il modulo scaricabile totalmente gratis. Se vuoi supportarmi, nella stesura dei miei articoli, puoi offrirmi un caffè :)

Donazione per 7software.it | 1 EUR

 

Ciao Developer!