首页 > 后端开发 > php教程 > Joomla 艺术中智能搜索的剖析 创建插件 I.

Joomla 艺术中智能搜索的剖析 创建插件 I.

Barbara Streisand
发布: 2024-12-04 22:29:11
原创
365 人浏览过

在上一篇文章中,我们熟悉了Joomla智能搜索组件的功能,讨论了使用CRON进行定时索引的参数和配置。让我们开始为我们自己的插件创建代码。

资源清单

在开始技术部分之前,我将提到一些直接涉及主题的文章。以及一般涵盖 Joomla 4 / Joomla 5 现代架构的插件的创建和/或更新的文章。接下来,我将假设读者已经阅读了它们并且通常了解如何制作工作插件对于 Joomla:

  • 创建智能搜索插件 - Joomla 官方文档。适用于 Joomla 3,但大部分规定仍然适用于 Joomla 4 / Joomla 5
  • 开发智能搜索插件 2012 年 Joomla 社区杂志的文章。
  • Nicholas Dionysopoulos 所著的《Joomla Extensions Development》一书涵盖了 Joomla 的开发! Joomla 版本 4 和 5 下的扩展。
  • 新文档门户 manual.joomla.org 上的数据库部分 - 适用于 Joomla 4 和 Joomla 5。 ## 技术部分。 Joomla 5智能搜索插件的开发 智能搜索组件与数据提供程序插件一起使用,其主要任务保持不变 - 选择数据并将其提供给组件进行索引。但随着时间的推移,重新索引任务也落入了插件的职责范围。在本文中,我们假设我们从管理面板手动运行内容索引。 CLI 的工作在视觉上有所不同,但其本质保持不变。

对于有经验的开发人员,我会说搜索插件扩展了 JoomlaComponentFinderAdministratorIndexerAdapter 类,该类文件位于 administrator/components/com_finder/src/Indexer/Adapter.php。好吧,然后他们会自己解决这个问题。此外,作为示例,您可以在 plugins/finder 文件夹中研究 Joomla 核心智能搜索插件 - 用于文章、类别、联系人、标签等。我为 JoomShopping(Joomla 电子商务组件)和 SW JProjects(您自己的带有更新服务器的 Joomla 扩展目录组件)组件开发了一个智能搜索插件,因此类名称和一些细微差别将与它们相关联。我将使用 JoomShopping 的示例来展示其中的大部分内容。多语言问题的解决方案基于 SW JProjects 的示例。

智能搜索插件的文件结构

Joomshopping 智能搜索插件的文件结构与典型的没有什么不同:

The anatomy of smart search in Joomla art Creating a plugin I.
Joomla 5 智能搜索插件文件结构

文件服务/provider.php

文件provider.php允许您在Joomla DI容器中注册插件,并允许您使用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;
            }
        );
    }
};
登录后复制
登录后复制
登录后复制
登录后复制

插件类文件

这是包含插件主要工作代码的文件。它应该位于 src/Extension 文件夹中。就我而言,插件类 JoomlaPluginFinderWtjoomshoppingfinderExtensionWtjoomshoppingfinder 位于文件 plugins/finder/wtjoomshoppingfinder/src/Extension/Wtjoomshoppingfinder.php 中。该插件的命名空间是 JoomlaPluginFinderWtjoomshoppingfinderExtension。

操作需要最少的类属性和方法集(它们可以被访问,包括由父 Adapter 类访问)。

类的最低要求属性

  • $extension - 是组件的名称,它定义内容的类型。例如,com_content。就我而言,这是 com_jshopping。
  • $context - 是插件的唯一标识符,它设置索引的上下文,在该上下文中将访问插件。事实上,这是插件类(元素)的名称。在我们的例子中,Wtjoomshoppingfinder。
  • $layout - 是搜索结果元素的输出布局的名称。显示搜索结果时使用此布局。例如,如果$layout参数设置为article,那么当您需要显示该类型的搜索结果时,默认视图模式将搜索名为default_article.php的布局文件。如果找不到这样的文件,则将使用名为 default_result.php 的布局文件。 HTML 布局的输出布局位于 components/com_finder/tmpl/search 中。但是,我们应该将布局作为覆盖放置在 html 模板文件夹中 - templates/YOUR_TEMPLATE/html/com_finder/search。在我的例子中,我将布局命名为产品,文件名为 default_product.phpThe anatomy of smart search in Joomla art Creating a plugin I.
  • $table - 是我们正在访问以获取数据的数据库中的表的名称,例如#__content。就我而言,包含 JoomShopping 产品的主表称为 #__jshopping_products。
  • $state_field - 是数据库表中负责索引元素是否发布的字段名称。默认情况下,该字段称为状态。然而,在 JoomShopping 的情况下,该字段称为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;
            }
        );
    }
};
登录后复制
登录后复制
登录后复制
登录后复制

类中最少需要的方法

  • setup() :bool - 是一种用于预配置插件、连接库等的方法。该方法在重新索引期间(reindex() 方法)在 onBeforeIndex 事件上调用。该方法必须返回true,否则索引将被中断。
  • index() : void - 是开始索引本身的方法。它从原始 SQL 查询数据中收集所需结构的对象,然后将其传递给 JoomlaComponentFinderAdministratorIndexerIndexer 类进行索引。该方法针对每个索引元素运行。方法参数是 $item - 数据库查询的结果,在 JoomlaComponentFinderAdministratorIndexerResult 类中格式化。
  • getListQuery() :JoomlaDatabaseDatabaseQuery - 是一种获取索引项列表的方法......

…这里我们开始深入研究细节,因为 getListQuery() 方法并不是真正强制性的,尽管文档和大多数文章都讨论了它。

The anatomy of smart search in Joomla art Creating a plugin I.
任何关于“复杂方案”主题的图片都可以在这里。

深入了解细节。索引元素的数据结构。

令人惊讶的是,有时一些信息或想法在我们注意到并意识到之前就已经在我们身边转了一圈了!很多东西,在我们眼前一年多了,还没有达到认知,需要经过多年的体验,我们的注意力才集中到它们上。

关于 Joomla,由于某种原因,它的组件并没有立即呈现出 Joomla 的某种通用架构特征(尽管这是一个明显的事实)。包括数据库表结构层面。让我们看一下 Joomla 内容表的一些字段。我会保留的是,具体的列名对我们来说并不是那么重要(你可以随时查询 SELECT name AS title),一个索引元素的数据结构是多少:

  • id - 自动增量
  • asset_id - #__assets 表中条目的 ID,其中存储了网站每个元素的组和用户的访问权限:文章、产品、菜单、模块、插件和其他所有内容。 Joomla 使用访问控制列表 (ACL) 模式。
  • title - 元素标题
  • 语言 - 元素语言
  • introtext - 介绍性文本或元素的简短可见描述
  • fulltext - 项目的全文、产品的完整描述等
  • state - 负责发布状态的逻辑标志:项目是否已发布。
  • catid - 项目类别的 ID。 Joomla 不像其他 CMS 那样只有“站点页面”。有些内容实体(文章、联系人、产品等)必须属于某些类别。
  • 创建 - 项目的创建日期。
  • access - 访问权限组id(未授权站点用户(访客)、全部、注册等)
  • metakey - 元素的元关键字。是的,自 2009 年以来,Google 就不再使用它们。但在 Joomla 中,它们历史上仍然存在,因为该字段用于相似文章模块中,以使用指定关键字搜索实际相似的文章。
  • metadesc - 元素元描述
  • publish_up 和publish_down - 元素开始发布和取消发布的日期。这更多的是一种选择,但在许多组件中都可以找到它。

如果我们比较表#__content(Joomla文章)、#__contact_details(联系人组件)、#__tags(Joomla标签)、#__categories(Joomla类别组件),那么我们会发现几乎所有列出的数据类型无处不在。

如果创建智能搜索插件的组件遵循“Joomla方式”并继承其架构,那么您可以在插件类中使用最少的方法。如果开发人员决定不寻找简单的方法而走自己的路,那么您将不得不走困难的路,重新定义 Adapter 类的几乎所有方法。

getListQuery() 方法

此方法在 3 种情况下被调用:

  1. Adapter类的getContentCount()方法是获取索引项的数量(总共有多少篇文章,总共有多少个产品等)。 The anatomy of smart search in Joomla art Creating a plugin I. Joomla 智能搜索索引过程 您可以在调试模式下看到索引项的数量。
  2. Adapter 类的 getItem($id) 方法是通过 id 获取特定索引元素。 getItem() 方法又在重新索引期间在 reindex($id) 方法中调用。
  3. Adapter 类的 getItems($offset, $limit, $query = null) 方法是获取索引元素列表的方法。偏移量和限制是根据组件设置设置的 - “捆绑”中应包含多少个索引元素。 The anatomy of smart search in Joomla art Creating a plugin I. Joomla 5 智能搜索设置索引器批量大小

让我们看一下 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;
            }
        );
    }
};
登录后复制
登录后复制
登录后复制
登录后复制

getListQuery()方法返回一个DatabaseQuery对象,该对象是查询构造函数的对象,其中已经指定了表的名称和选择的字段。在调用它的方法中继续使用它。

如果从 DatabaseQuery $query 对象中的 getContentCount() 调用 getListQuery(),则 select 的设置值将替换为 COUNT(*)。

如果从 getItem($id) 调用 getListQuery(),则条件 $query->where('a.id = ' . (int) $id) 并且仅选择特定元素。在这里我们已经看到父 Adapter 类在查询中包含作为 a.* 的表名称。这意味着我们还应该在 getListQuery() 方法的实现中使用这些前缀。

在从 getItems() 调用 getListQuery() 的情况下,$offset 和 $limit 将添加到我们构建的查询中,以便在元素列表中移动以进行索引。
总结: getListQuery() - 必须包含三个不同 SQL 查询的“工作片段”。 在这里实现 Joomla 并没有什么特别困难的。但是,如果有必要,您可以自己实现 3 个方法,而无需创建 getListQuery()。

非 Joomla 方式: 就 JoomShopping 而言,我发现一个产品可以有多个类别,并且历史上该产品的类别 id (catid) 组件存储在单独的表中。同时,多年来一直无法指定产品的主要类别。收到产品类别后,查询将发送到类别表,其中仅获取第一个查询结果,按默认类别 ID 排序 - 即升序。如果我们在编辑产品时更改类别,则主要产品类别是 ID 号较小的类别。产品的 URL 以此为基础,产品可以从一个类别跳转到另一个类别。

但是,大约 2 年前,这种 JoomShopping 行为已得到修复。由于该组件历史悠久,受众众多,并且不能仅仅破坏向后兼容性,修复是可选的。必须在组件设置中启用指定产品主类别的功能。然后 main_category_id 将被填充到带有产品的表中。

但是这个功能默认是关闭的。而在智能搜索插件中,我们需要获取JoomShopping组件的参数,看看是否启用了指定主商品类别的选项(并且它最近可能启用,并且未指定某些产品的主类别 - 也是一个细微差别...)并生成 SQL 查询以根据组件参数接收产品:或者是一个简单的查询,其中我们添加 main_category_id字段,或以旧的错误方式获取类别 id 的 JOIN 请求。

在此请求中,多语言的细微差别立即凸显出来。根据 Joomla 方式,为网站的每种语言创建一个单独的元素,并在它们之间建立关联。因此,对于俄语 - 一篇文章。同一篇英文文章正在单独创建。然后我们使用语言关联将它们相互连接起来,当在 Joomla 前端切换语言时,我们将从一篇文章重定向到另一篇文章。

这不是 JoomShopping 中的做法:所有语言的数据都与产品存储在同一个表中(好的)。添加其他语言的数据是通过添加带有这些语言后缀的列来完成的(嗯...)。也就是说,我们的数据库中不仅仅有标题或名称字段。但还有 name_ru-RU、name_en-GB 等字段
The anatomy of smart search in Joomla art Creating a plugin I.
Joomla JoomShopping 产品表结构片段
同时,我们需要设计一个通用的 SQL 查询,以便可以从管理面板和 CLI 对其进行索引。同时,使用 CRON 启动 CLI 时选择索引语言也是一项任务。我承认,在撰写本文时,我暂时推迟了对该问题的全面解决方案。使用我们自己的 getLangTag() 方法选择语言,我们可以从 JoomShopping 参数中获取主要语言,也可以使用网站的默认语言。也就是说,到目前为止,该解决方案仅适用于单语言网站。目前还无法进行不同语言的搜索。

但是,3个月后我解决了这个问题,但已经在 SW JProjects 组件的智能搜索插件中了。我会进一步告诉你解决方案。

同时,让我们看看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;
            }
        );
    }
};
登录后复制
登录后复制
登录后复制
登录后复制

检查点

我们创建了一种从 Joomla 查询数据库的方法,并了解了很多有关智能搜索插件如何工作的知识。

在下一篇文章中,我们将创建一个索引内容的方法并完成插件的创建。我们还将熟悉索引项如何存储在数据库中,并理解为什么这很重要,并通过多语言的非标准实现解决多语言组件的索引内容问题。

Joomla 社区资源

  • https://joomla.org/
  • Joomla 社区杂志中的这篇文章
  • Mattermost 中的 Joomla 社区聊天(了解更多)

以上是Joomla 艺术中智能搜索的剖析 创建插件 I.的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板