Voir les différentes positions d’un template Joomla

Position modules Joomla

Sous Joomla, chaque template dispose d’un certain nombre de positions dans lesquelles sont placés les différents modules.

Pour visualiser la position de ces modules, il faut ajouter « tp=1 » dans la querystring.

Voici par exemple, une capture d’écran de http://www.joomla.org/?tp=1

Avec cette technique, il est plus facile d’identifier les modules présents sur un site Joomla.


Modifier la page d’accueil de Joomla

Cette astuce fonctionne sur toutes les versions de Joomla.

Joomla affiche en « page d’accueil », la premier lien publié du menu appelé « main_menu ».

Par défaut, il s’agit d’une vue particulière du composant « com_content » qui va afficher une liste d’articles, à la manière d’un blog.

Pour mettre en page d’accueil, votre boutique VirtueMart, il vous suffit donc de placer en première position du menu main_menu, une entrée pointant vers une page du composant « virtuemart ».

Il n’est pas nécessaire qu’un module affichant le « mainmenu » ne soit publié. Ainsi, cette modification ne sera pas nécessairement visible dans les menus de navigation.

Rendre Virtuemart utilisable en mode offline

Les dernières versions de VirtueMart font appel à des webservices convertisseurs de devises.

Ces webservices ne peuvent malheureusement pas à ma connaissance être désactivés.

De ce fait, lorsque l’on utilise VirtueMart en étant non connectés à Internet (pour développer ou faire une présentation), des messages d’erreurs peuvent s’afficher (ie : « Error: Failed to retrieve the Currency Converter XML document »).

Pour éviter ces désagréments, mais également éventuellement pour supprimer les appels à ces webservices qui peuvent être inutiles et ralentir inutilement le site, il faut créer un fichier dans le répertoire /administrator/components/com_virtuemart/classes/currency/

Appelez le par exemple « convertNOP.php ».

Voici le code source nécessaire :

<?php

if(

!defined( '_VALID_MOS' )

&&

!defined( '_JEXEC' ) )

die( 'Direct Access to '.basename(__FILE__).' is not allowed.' );

class convertNOP {
  function convert( $amountA, $currA='', $currB='' ) {
    return $amountA;
  }
}

?>

Par la suite, il faut activer ce fichier en allant dans le back-office VirtueMart dans « Configuration » > « Configuration générale » > « Paramètres principaux » > « Sélectionner un module convertisseur de devises ».

Présentation de SOLR

Open Source Entreprise Search Server basé sur Lucene SOLR est un serveur de recherche Open Source basé sur Lucene. C’est un logiciel libre réalisé en Java soutenu par la Fondation Apache.

Il permet d’indexer une base de données via Lucene, sans qu’il soit nécessaire de programmer une seule ligne de code.

Il est possible de communiquer avec SOLR via un ensemble de webservices. Il est donc tout à fait possible d’indexer du contenu ou d’effectuer des recherches depuis une application PHP telle que VirtueMart ou Joomla.

Bien entendu, les performances de ce type de moteur de recherche n’ont rien à voir avec celles des moteurs de recherche de Joomla et de VirtueMart.

En outre, SOLR supporte le « faceting » aussi appelé « faceted search », que l’on peut trouver sur de gros sites e-commerce tel que Amazon.

Le « faceting » vous propose de préciser votre recherche en ajoutant des critères supplémentaires, parmis les résultats retournés. Le nombre de résultats correspondant à chaque critère étant ajouté.

facetting_amazon
La navigation par facette sur Amazon

Par exemple, lorsque sur Amazon, vous recherchez « logiciel libre », Amazon affiche une zone vous permettant de naviguer parmi les résultats en précisant votre recherche :

SOLR permet d’implémenter ce genre de filtres sur des champs variés. Il est par exemple tout à fait envisageable de faire un filtre par gamme de prix, couleur et taille.

Le livre Solr 1.4 Enterprise Search Server peut permettre aux développeurs de découvrir SOLR efficacement.

Dans un prochain article, je montrerais qu’il est très facile de faire un lien entre VirtueMart et SOLR, si un système de plugin est mis en place sur VirtueMart.

Améliorer les performances de VirtueMart : Proposition d’une nouvelle table

Voici une proposition permettant d’améliorer le parcours de l’arborescence catégorie de VirtueMart, au détriment de la vitesse d’écriture. Ce billet fait directement suite à mon article « Améliorer les performances de VirtueMart« .

Cette méthode nécessite de créer une nouvelle table que nous appellerons #__{vm}_category_tree, dont voici le schéma :

  • category_id : identifiant de la catégorie
  • category_path : fil d’ariane de la catégorie sous forme d’un liste dénormalisée d’identifiant de catégorie séparée par « , »
  • level : niveau de profondeur de la catégorie étudiée

Par exemple, une catégorie ayant un « fil d’ariane » ayant pour valeur « ,1,6,184, » serait de niveau 4 (étant donné qu’il existe trois niveaux de catégories parentes). Sa catégorie parente serait la catégorie « 184 » qui aurait elle même comme parent « 6 » qui aurait elle même comme parent « 1 » qui n’aurait pas de catégorie parente.

Le choix du séparateur « , » permettra d’utiliser au besoin la colonne presque directement dans une requête SQL via le mot clé « in » si besoin.

Voici le SQL permettant de créer cette table :

CREATE TABLE IF NOT EXISTS `#__{vm}_category_tree` (
`category_id` INT(11) NOT NULL,
`category_path` VARCHAR(255) NOT NULL,
`level` INT(11) NOT NULL,
KEY `category_id` (`category_id`),
KEY `category_path` (`category_path`),
KEY `level` (`level`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Pour alimenter cette table, il faut d’abord executer une fois la requete suivante :

INSERT INTO
`#__{vm}_category_tree`
(category_id,category_path,level)
SELECT
`#__{vm}_category.category_id`,
",",
1
FROM
`#__{vm}_category`
INNER JOIN
`#__{vm}_category_xref`
ON
`#__{vm}_category_xref`.category_child_id=`#__{vm}_category`.category_id
WHERE
`#__{vm}_category`.category_publish="Y"
AND
`#__{vm}_category_xref`.category_parent_id=0;

Il faut ensuite exécuter pour chaque niveau :

INSERT INTO
`#__{vm}_category_tree`
(category_id,category_path,level)
SELECT
`#__{vm}_category`.category_id,
concat(IFNULL(parent_leaf.category_path,""),`#__{vm}_category_xref`.category_parent_id,";"),
IFNULL(parent_leaf.level+1,0)
FROM
`#__{vm}_category`
INNER JOIN
`#__{vm}_category_xref`
ON
`#__{vm}_category_xref`.category_child_id=`#__{vm}_category`.category_id
LEFT OUTER JOIN
`#__{vm}_category_tree` parent_leaf
ON
parent_leaf.category_id=`#__{vm}_category_xref`.category_parent_id
WHERE
`#__{vm}_category`.category_publish="Y"
AND
`#__{vm}_category_xref`.category_parent_id IN (SELECT
category_id FROM `#__{vm}_category_tree` WHERE level=?)

Le remplissage « au fil de l’eau » de cette table, fera l’objet d’autres billets.

Maintenant pour connaître tous les enfants directs et indirects d’une catégorie (« 5 » par exemple), il suffit d’éxécuter la requête suivante:

SELECT category_id, level

FROM `#__{vm}_category_tree`

WHERE category_path LIKE ',5,%';

Une sacré économie de requetes SQL. Le résultat peut-être placée en cache si cela est possible. Il est également possible de complexifier cette requête en y rajoutant par exemple une jointure avec la table #__{vm}_category.

Si vous êtes intéressé par cette modification, merci d’appuyer ma demande sur le forum.

Améliorer les performance de Virtuemart : Constat

Dans le cadre professionnel, il m’est arrivé à moi et à mes collèges de créer des boutiques en ligne, basées sur Joomla / VirtueMart, proposant plus de 15000 produits.

Après avoir réalisé des modifications nous permettant de tenir un tel catalogue, nous avons voulu les proposer à la communauté via le forum officiel de VirtueMart. Malheureusement, étonnement, notre demande a été ignorée pendant plusieurs mois.

L’un des problèmes majeurs, était le nombre de requêtes SQL nécessaires aux parcours de l’arborescence VirtueMart.

L’arborescence produit est stocké dans la table #__vm_category_xref dont voici le schéma simplifié :

  • category_parent_id : identifiant de la catégorie parent
  • category_child_id : identifiant de la catégorie enfant

Imaginons que votre site soit découpé en catégories de la manière suivante :

  1. CMS
    1. Joomla
      1. Joomla 1.0
      2. Joomla 1.5
      3. Joomla 1.6
    2. Drupal
    3. EZ Publish
    4. Typo 3
  2. E-Commerce
    1. VirtueMart
    2. Magento
    3. OS Commerce

Ce découpage est bien en dessous de l’arborescence que nous avons à gérer pour nos sites e-commerce.

Pour récupérer, l’arborescence et l’afficher il nous faut d’abord récupérer les catégories sans enfants (« CMS » et « E-Commerce »). Pour chacune de ces catégories, il faut voir si ces dernières ont des enfants et les afficher. Cet algorithme continue de manière itérative…

On voit bien que le nombre de requêtes SQL nécessaire est exponentiel. Certes, il y a le cache, mais ce dernier n’est pas toujours utilisable (ie : en back office).

Pire, imaginons que la page affichant la catégorie « CMS » doivent afficher l’ensemble des produits contenus dans la catégorie « CMS » et dans ces enfants directs et indirects, le tout trié par prix.

Ce problème explique en partie la lourdeur de l’arborescence du back-office par défaut de VirtueMart , lorsque le catalogue produit est volumineux.

J’expliquerais en détail la méthode que nous souhaitons soumettre à la communauté dans un prochain billet.

Comment rendre VirtueMart plus souple ….

Lorsque je suis amené à travailler sur VirtueMart, solution libre de e-commerce basé sur le CMS Joomla, je suis contraint de modifier directement le code original afin de l’adapter à mes besoins.

Le code perd alors toute compatibilité avec la version communautaire.

En dehors de cela, en raison de cette absence de modularité, il est difficile d’échanger des modifications de Virtuemart à la communauté, sans passer par l’approbation de la communauté VirtueMart. La communauté perd ainsi en « dynamisme ».

En conséquence, j’ai émis une suggestion sur le forum officiel de VirtueMart.

Techniquement, le support de plugin sous VirtueMart ne me semble pas très difficile, la notion de plugin étant déjà présente sous Joomla.

Il faut tout d’abord commencer par créer un nouveau dossier de « plugin ». Pour cela, il suffit de rajouter un dossier « virtuemart » dans le répertoire « plugins » de Joomla.

Par la suite, il faut définir un ensemble d’événements durant lesquels les extensions pourront être déclenchées.

Je propose dans un premier temps (car j’aurai personnellement besoin de ces événements) :

  • insertion de produits (onProductInsert),
  • modification de produits (onProductUpdate),
  • suppression de produits (onProductDelete),
  • insertion de catégories (onCategoryInsert),
  • modification de catégories (onCategoryUpdate),
  • suppression de catégories (onCategoryDelete),
  • passage de commande (onCheckoutProcessed).

Une fois, les événements définies, il faut modifier le code source du composant Virtuemart, pour déclencher les événements des plugins aux moments opportuns.

Par exemple, pour déclencher l’événement « onCategoryInsert », lors de la création d’une catégorie, il faut rajouter le code suivant dans la méthode « add » de la classe « ps_product_category ».

<?php

$vmLogger->info(
  $VM_LANG->_('VM_PRODUCT_CATEGORY_ADDED').
  ': "'.vmGet($d,'category_name').'"'
);

/* Ca commence */
JPluginHelper::importPlugin('virtuemart');
$mainframe->triggerEvent(
  'onCategoryInsert',
  array($category_id)
);
/* Ca termine */

return true;

?>

En fait, la méthode importPlugin, a des fins d’optimisation devrait plutôt être chargée au démarrage du composant virtuemart, en front et en back office.

Ensuite, la création de plugins pour VirtueMart devient possible.

Évidemment, le plugin doit avoir pour « folder » ou « group » la valeur « virtuemart ».

Voici un exemple d’interception de l’insertion de catégories :

<?php
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );

jimport( 'joomla.plugin.plugin' );

/**
* Generate a category tree for performance
* optimization (skeleton of a future plugin)
*
*/

class  plgVirtuemartCategorytree extends JPlugin
{
  function plgVirtuemartCategorytree(&$subject, $config) {
    parent::__construct($subject, $config);
  }

  function onCategoryInsert($category_id)   {
    echo '<h3>'.$category_id.'</h3>';
  }
}

?>

Ce plugin se content d’afficher l’identifiant de la catégorie insérée. Ce plugin, en l’état n’a bien entendu aucun intérêt. C’est juste une démonstration illustrant le fonctionnement des plugins.

Si vous êtes intéressé par cette modification, merci d’appuyer ma demande sur le forum.