Maison > développement back-end > tutoriel php > L'anatomie de la recherche intelligente dans Joomla art Création d'un plugin I.

L'anatomie de la recherche intelligente dans Joomla art Création d'un plugin I.

Barbara Streisand
Libérer: 2024-12-04 22:29:11
original
359 Les gens l'ont consulté

Dans l'article précédent, nous nous sommes familiarisés avec les capacités du composant de recherche intelligente de Joomla, avons parlé des paramètres et de la configuration de l'indexation planifiée à l'aide de CRON. Commençons par créer le code de notre propre plugin.

Liste des ressources

Avant de commencer la partie technique, je mentionnerai quelques articles qui abordent directement le sujet principal. Ainsi que des articles qui couvrent généralement la création et/ou la mise à jour d'un plugin pour l'architecture moderne de Joomla 4 / Joomla 5. Ensuite, je supposerai que le lecteur les a lus et a généralement une idée sur la façon de créer un plugin fonctionnel. pour Joomla :

  • Création d'un plugin Smart Search - documentation officielle de Joomla. C'est pour Joomla 3, mais la plupart des dispositions sont restées vraies pour Joomla 4 / Joomla 5
  • Développer un plugin de recherche intelligente, un article du Joomla Community Magazine en 2012.
  • Le livre Joomla Extensions Development de Nicholas Dionysopoulos qui couvre le développement de Joomla! extensions sous Joomla versions 4 et 5.
  • La section Base de données sur le nouveau portail de documentation manual.joomla.org - pour Joomla 4 et Joomla 5. ## La partie technique. Développement du plugin de recherche intelligente Joomla 5 Le composant de recherche intelligente fonctionne avec des plugins de fournisseur de données, dont la tâche principale reste la même : sélectionner les données et les transmettre au composant pour indexation. Mais au fil du temps, les tâches de réindexation sont également tombées sous la responsabilité du plugin. Dans l'article, nous supposerons que nous exécutons l'indexation du contenu manuellement à partir du panneau d'administration. Le travail de la CLI est visuellement différent, mais son essence reste la même.

Pour les développeurs expérimentés, je dirai que le plugin de recherche étend la classe JoomlaComponentFinderAdministratorIndexerAdapter, le fichier de classe se trouve dans administrator/components/com_finder/src/Indexer/Adapter.php. Eh bien, alors ils le découvriront par eux-mêmes. En outre, à titre d'exemple, vous pouvez étudier les principaux plugins de recherche intelligente de Joomla - pour les articles, les catégories, les contacts, les balises, etc. - dans le dossier plugins/finder. J'ai travaillé sur un plugin de recherche intelligent pour les composants JoomShopping (composant e-commerce Joomla) et SW JProjects (votre propre composant de répertoire d'extensions Joomla avec serveur de mise à jour), de sorte que les noms de classe et certaines nuances leur seront associés. Je vais en montrer l'essentiel en utilisant l'exemple de JoomShopping. La solution à la question du multilinguisme s'appuie sur l'exemple de SW JProjects.

La structure des fichiers du plugin de recherche intelligente

La structure des fichiers du plugin de recherche intelligente pour Joomshopping ne diffère pas de celle typique :

The anatomy of smart search in Joomla art Creating a plugin I.
Structure des fichiers du plugin de recherche intelligente Joomla 5

Services de fichiers/provider.php

Le fichier provider.php vous permet d'enregistrer un plugin dans un conteneur Joomla DI et vous permet d'accéder aux méthodes du plugin de l'extérieur en utilisant MVCFactory.

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Fichier de classe de plugin

Il s'agit du fichier qui contient le code de travail principal de votre plugin. Il doit se trouver dans le dossier src/Extension. Dans mon cas, la classe du plugin JoomlaPluginFinderWtjoomshoppingfinderExtensionWtjoomshoppingfinder est dans le fichier plugins/finder/wtjoomshoppingfinder/src/Extension/Wtjoomshoppingfinder.php. L'espace de noms du plugin est JoomlaPluginFinderWtjoomshoppingfinderExtension.

Il existe un ensemble minimal de propriétés et de méthodes de classe requises pour le fonctionnement (elles sont accessibles, y compris par la classe Adapter parent).

Les propriétés minimales requises de la classe

  • $extension - est le nom de votre composant, qui définit le type de votre contenu. Par exemple, com_content. Dans mon cas, c'est com_jshopping.
  • $context - est un identifiant unique pour le plugin, il définit le contexte d'indexation dans lequel le plugin sera accessible. En fait, c'est le nom de la classe (élément) du plugin. Dans notre cas, Wtjoomshoppingfinder.
  • $layout - est le nom de la présentation de sortie pour l'élément de résultats de recherche. Cette disposition est utilisée lors de l'affichage des résultats de recherche. Par exemple, si le paramètre $layout est défini sur article, alors le mode d'affichage par défaut recherchera un fichier de mise en page nommé default_article.php lorsque vous devez afficher un résultat de recherche de ce type. Si un tel fichier n'est pas trouvé, alors un fichier de mise en page portant le nom default_result.php sera utilisé à la place. Les mises en page de sortie avec mise en page HTML se trouvent dans components/com_finder/tmpl/search. Cependant, nous devons placer nos mises en page comme remplacements - dans le dossier des modèles HTML - templates/YOUR_TEMPLATE/html/com_finder/search. Dans mon cas, j'ai nommé le produit de mise en page et le fichier s'appelle default_product.php. The anatomy of smart search in Joomla art Creating a plugin I.
  • $table - est le nom de la table de la base de données à laquelle nous accédons pour obtenir des données, par exemple #__content. Dans mon cas, la table principale avec les produits JoomShopping s'appelle #__jshopping_products.
  • $state_field - est le nom du champ dans la table de base de données qui détermine si l'élément indexé est publié ou non. Par défaut, ce champ est appelé état. Cependant, dans le cas de JoomShopping, ce champ est appelé product_publish.
<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Les méthodes minimales requises de la classe

  • setup() : bool - est une méthode de pré-configuration du plugin, de connexion des bibliothèques, etc. La méthode est appelée lors de la réindexation (la méthode reindex()), sur l'événement onBeforeIndex. La méthode doit retourner vrai, sinon l'indexation sera interrompue.
  • index() : void - est la méthode pour démarrer l'indexation elle-même. Il collecte un objet de la structure souhaitée à partir des données brutes de requête SQL, qui est ensuite transmis à la classe JoomlaComponentFinderAdministratorIndexerIndexer pour l'indexation. La méthode est exécutée pour chaque élément indexé. L'argument de la méthode est $item - le résultat d'une requête vers la base de données, formaté dans la classe JoomlaComponentFinderAdministratorIndexerResult.
  • getListQuery() : JoomlaDatabaseDatabaseQuery - est une méthode pour obtenir une liste d'éléments indexés…

... et ici nous commençons à entrer dans les détails, puisque la méthode getListQuery() n'est pas vraiment obligatoire, malgré le fait que la documentation et la plupart des articles en parlent.

The anatomy of smart search in Joomla art Creating a plugin I.
N'importe quelle image sur le thème du « schéma complexe » fera l'affaire ici.

Plongez dans les détails. La structure de données de l'élément indexé.

C'est incroyable combien de fois une information ou une idée nous passe parfois en cercle avant que nous ne la remarquions et ne nous en rendions compte ! Beaucoup de choses, étant sous nos yeux depuis plus d'un an, n'atteignent toujours pas notre conscience, et notre attention ne se concentre sur elles qu'après des années d'expérience.

En ce qui concerne Joomla, pour une raison quelconque, la vision ne vient pas immédiatement que ses composants assument une sorte d'architecture commune caractéristique de Joomla (bien que ce soit un fait évident). Y compris au niveau de la structure des tables de la base de données. Regardons quelques champs de la table de contenu Joomla. Je ferai une réserve sur le fait que les noms de colonnes spécifiques ne sont pas si importants pour nous (vous pouvez toujours interroger SELECT nom AS titre), quelle est la structure des données pour un élément indexé :

  • id - incrémentation automatique
  • Asset_id - l'identifiant de l'entrée dans la table #__assets, où sont stockés les droits d'accès des groupes et des utilisateurs pour chaque élément du site : articles, produits, menus, modules, plugins et tout le reste. Joomla utilise le modèle de liste de contrôle d'accès (ACL).
  • title - le titre de l'élément
  • langage - l'élément langage
  • introtext - texte d'introduction ou une brève description visible de l'élément
  • fulltext - le texte intégral de l'article, la description complète du produit, etc.
  • state - le drapeau logique responsable du statut de publication : si l'article est publié ou non.
  • catid - l'ID de la catégorie d'article. Joomla n'a pas seulement des « pages de site » comme dans les autres CMS. Il existe des entités de contenu (articles, contacts, produits, etc.) qui doivent appartenir à certaines catégories.
  • créé- la date à laquelle l'article a été créé.
  • accès - identifiant du groupe de droits d'accès (utilisateurs non autorisés du site (invités), tous, enregistrés, etc.)
  • metakey - méta-mots-clés pour l'élément. Oui, depuis 2009, ils ne sont plus utilisés par Google. Mais dans Joomla, ils demeurent historiquement, puisque ce champ est utilisé dans le module d'articles similaires pour rechercher des articles réellement similaires à l'aide de mots-clés spécifiés.
  • metadesc - la méta description de l'élément
  • publier_up et publier_down - la date de début de publication et de dépublication de l'élément. Il s'agit plutôt d'une option, mais on la retrouve dans de nombreux composants.

Si l'on compare les tableaux #__content (articles Joomla), #__contact_details (composant contact), #__tags (balises Joomla), #__categories (composant catégorie Joomla), alors nous retrouverons presque tous les types de données répertoriés partout.

Si le composant pour lequel les plugins de recherche intelligente sont créés a suivi la "voie Joomla" et hérite de son architecture, alors vous pouvez vous contenter d'un minimum de méthodes dans la classe plugin. Si les développeurs décident de ne pas chercher de solutions faciles et de suivre leur propre chemin, vous devrez alors emprunter la voie difficile, en redéfinissant presque toutes les méthodes de la classe Adapter.

Méthode getListQuery()

Cette méthode est appelée dans 3 cas :

  1. La méthode getContentCount() de la classe Adapter consiste à obtenir le nombre d'éléments indexés (combien d'articles au total, combien de produits au total, etc.). The anatomy of smart search in Joomla art Creating a plugin I. Processus d'indexation de recherche intelligente Joomla Vous pouvez voir le nombre d'éléments indexés en mode débogage.
  2. La méthode getItem($id) de la classe Adapter consiste à obtenir un élément indexé spécifique par son identifiant. La méthode getItem(), à son tour, est appelée dans la méthode reindex($id) - lors de la réindexation.
  3. La méthode getItems($offset, $limit, $query = null) de la classe Adapter est une méthode permettant d'obtenir une liste d'éléments indexés. Le décalage et la limite sont définis en fonction des paramètres des composants - combien d'éléments indexés doivent être inclus dans le "bundle". The anatomy of smart search in Joomla art Creating a plugin I. Taille du lot de l'indexeur de paramètres de recherche intelligente Joomla 5

Regardons un exemple d'implémentation dans les plugins principaux de Joomla :

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

La méthode getListQuery() renvoie un objet DatabaseQuery, un objet du constructeur de requête, où le nom de la table et les champs de sélection sont déjà spécifiés. Le travail avec lui se poursuit dans les méthodes qui l'appellent.

Si getListQuery() est appelé depuis getContentCount() dans l'objet DatabaseQuery $query, les valeurs définies pour select sont remplacées par COUNT(*).

Si getListQuery() est appelé depuis getItem($id), la condition $query->where('a.id = ' . (int) $id) et seul un élément spécifique est sélectionné. Et déjà ici, nous voyons que la classe parent Adapter contient le nom de la table dans la requête sous la forme a.*. Cela signifie que nous devons également utiliser ces préfixes dans notre implémentation de la méthode getListQuery().

Dans le cas de l'appel de getListQuery() depuis getItems(), $offset et $limit sont ajoutés à la requête que nous avons construite afin de parcourir la liste des éléments à indexer.
Résumé : getListQuery() - doit contenir un "élément de travail" pour trois requêtes SQL différentes. Et il n'y a rien de particulièrement difficile à implémenter Joomla ici. Mais, si nécessaire, vous pouvez implémenter vous-même 3 méthodes sans créer getListQuery().

Façon non Joomla : Dans le cas de JoomShopping, je suis tombé sur le fait qu'un produit peut avoir plusieurs catégories et qu'historiquement, le composant identifiant de catégorie (catid) du produit était stocké dans une table séparée. Dans le même temps, pendant de nombreuses années, il n’a pas été possible de préciser la catégorie principale du produit. Dès réception de la catégorie de produit, une requête a été envoyée à la table avec les catégories, où seul le premier résultat de la requête a été pris, trié par identifiant de catégorie par défaut, c'est-à-dire par ordre croissant. Si nous modifiions les catégories lors de la modification d'un produit, alors la catégorie de produit principale était celle avec le numéro d'identification le plus bas. L'URL du produit était basée sur celle-ci et le produit pouvait passer d'une catégorie à une autre.

Mais il y a presque 2 ans, ce comportement de JoomShopping a été corrigé. Étant donné que le composant a une longue histoire, un large public et ne peut pas simplement rompre la compatibilité descendante, le correctif a été rendu facultatif. La possibilité de spécifier la catégorie principale du produit doit être activée dans les paramètres du composant. Ensuite, le main_category_id sera renseigné dans le tableau avec les produits.

Mais cette fonctionnalité est désactivée par défaut. Et dans le plugin de recherche intelligente, nous devons obtenir les paramètres du composant JoomShopping, voir si l'option pour spécifier la catégorie principale de produits est activée (et elle peut être activé récemment et la catégorie principale pour certains produits n'est pas spécifiée - également une nuance...) et générer une requête SQL pour recevoir le(s) produit(s) en fonction des paramètres du composant : soit une simple requête où l'on ajoute le champ main_category_id , ou un Rejoignez la demande pour obtenir l'identifiant de la catégorie dans le mauvais sens.

Immédiatement, la nuance du multilinguisme apparaît dans cette demande. Selon la méthode Joomla, un élément distinct est créé pour chaque langue du site et des associations sont établies entre eux. Donc, pour la langue russe - un article. Le même article en anglais est créé séparément. Ensuite, nous les connectons entre eux à l'aide d'associations de langues et lors du changement de langue sur le frontend Joomla, nous serons redirigés d'un article à l'autre.

Ce n'est pas ainsi que cela se fait dans JoomShopping : les données pour toutes les langues sont stockées dans le même tableau avec les produits (Ok). L'ajout de données pour d'autres langues se fait en ajoutant des colonnes avec le suffixe de ces langues (hmm...). Autrement dit, nous n’avons pas seulement un champ de titre ou de nom dans la base de données. Mais il y a des champs name_ru-RU, name_en-GB, etc.
The anatomy of smart search in Joomla art Creating a plugin I.
Fragment de structure de table de produits Joomla JoomShopping
Dans le même temps, nous devons concevoir une requête SQL universelle afin qu'elle puisse être indexée à la fois depuis le panneau d'administration et la CLI. Dans le même temps, choisir le langage d'indexation lors du lancement de la CLI à l'aide de CRON est également une tâche. J'avoue qu'au moment de la rédaction de cet article, j'ai reporté pour le moment une solution à part entière à ce problème. La langue est sélectionnée à l'aide de notre propre méthode getLangTag(), où nous prenons soit la langue principale des paramètres de JoomShopping, soit la langue par défaut du site. Autrement dit, jusqu'à présent, cette solution concerne uniquement un site monolingue. La recherche dans différentes langues ne fonctionnera pas encore.

Cependant, 3 mois plus tard, j'ai résolu ce problème, mais déjà dans le plugin de recherche intelligente pour le composant SW JProjects. Je vous parlerai plus loin de la solution.

En attendant, regardons ce qui s'est passé pour JoomShopping

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Point de contrôle

Nous avons créé une méthode pour interroger la base de données de Joomla et avons beaucoup appris sur le fonctionnement du plugin de recherche intelligente.

Dans le prochain article, nous créerons une méthode d'indexation du contenu et terminerons la création du plugin. Nous nous familiariserons également avec la manière dont les éléments indexés sont stockés dans la base de données, comprendrons pourquoi cela est important et résoudrons le problème de l'indexation du contenu pour les composants multilingues avec une implémentation non standard du multilinguisme.

Ressources de la communauté Joomla

  • https://joomla.org/
  • Cet article dans Joomla Community Magazine
  • Chat de la communauté Joomla dans Mattermost (en savoir plus)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal