Реализовать категории в пользовательском компоненте довольно просто. Вот краткое изложение того, что вам нужно сделать.

Укажите столбец идентификатора категории в таблице базы данных вашего компонента.

Все данные о категориях хранятся в таблице Categories; вам просто нужно указать внешний ключ для соответствующей записи категории, поэтому вам придется создать дополнительный столбец в таблице компонентов для хранения ключа. Обычно вашу колонку называют «catid», но это не обязательно. Он соответствует полю «id» таблицы  Категории Categories.

Добавление поля для подключения категории

Если вы разрешаете пользователям добавлять новые записи или редактировать существующие записи, вам потребуется расширить форму, чтобы охватить связанную категорию. Вы можете добавить в XML-файл что-то вроде того, что показано ниже, для добавления или редактирования записей компонентов:

<field
    name="catid"
    type="category"
    extension="com_yourcomponent"
    class="inputbox"
    default=""
    label="JCATEGORY"
    required="true"
    >
    <option value="0">JOPTION_SELECT_CATEGORY</option>
</field>

При условии, что вы вставите в поле name  имя поля совпадающее с именем столбца вашей базы данных, Joomla перенесет данные из отправленной формы в таблицу базы данных для вас.

Добавление записей в подменю администратора

Вам следует добавить запись в подменю вашего компонента, чтобы администраторы могли управлять данными категории. Вы можете сделать это в администраторе. <submenu> теги в XML-файле манифеста вашего компонента:

<administration>
    <submenu>
        <menu link="option=com_categories&extension=com_yourcomponent">JCATEGORIES</menu>
    </submenu>
</administration>

Установка списков управления доступом для категорий

Вам необходимо получить доступ к некоторым записям в вашем приложении access.xml, например

<?xml version="1.0" encoding="utf-8" ?>
<access component="com_yourcomponent">
    <section name="component">
            <action name="core.admin" title="JACTION_ADMIN"/>
            <action name="core.manage" title="JACTION_MANAGE"/>
            <action name="core.create" title="JACTION_CREATE"/>
            <action name="core.delete" title="JACTION_DELETE"/>
            <action name="core.edit" title="JACTION_EDIT"/>
            <action name="core.edit.state" title="JACTION_EDITSTATE"/>
    </section>
    <section name="category">
            <action name="core.create" title="JACTION_CREATE"/>
            <action name="core.delete" title="JACTION_DELETE"/>
            <action name="core.edit" title="JACTION_EDIT"/>
            <action name="core.edit.state" title="JACTION_EDITSTATE"/>
    </section>
</access>

Краткое описание категории по состояниям публикации

В форме Категории администратора обычно отображается для каждой записи категории количество записей вашего компонента, которые находятся в состоянии Published / Unpublished / Archived / Trashed. Для реализации этого com_categories загружает ваш компонент и вызывает функцию countItems() (определенная в Joomla \ CMS \ Categories \ CategoryServiceInterface) в экземпляре вашего класса расширения. Вы можете либо определить функцию countItems() напрямую или (если вызывается ваше поле категории catid) используйте Joomla \ CMS \ Categories\CategoryServiceTrait и переопределите

  • getTableNameForSection – возвращает табличное имя вашего компонента,
  • getStateColumnForSection – возвращает столбец, содержащий состояние (например, "Published").

Затем com_categories выполнит запрос к самой таблице вашего компонента.

Создать некатегоризированную категорию

Возможно, вы заметили, что при первой установке Joomla для каждого из основных компонентов Joomla есть запись категории без рубрикации (Uncategorised), которая использует категории. Также, что catid поле в их таблицах базы данных не допускает значения NULL – для каждого элемента компонента должна быть соответствующая запись категории.

Вы должны следовать этому шаблону, настроив запись категории без рубрикации вашего компонента в вашем сценарии установки. В противном случае вы, вероятно, столкнетесь с трудностями в некоторых областях (например, с многоязычными ассоциациями), потому что код библиотеки Joomla иногда предполагает, что связанная категория не является необязательной.

Другие изменения

Существует множество других изменений, которые вы, возможно, пожелали бы внести, в зависимости от функциональности вашего компонента, например:

  • если у вас есть форма, которая отображает элементы вашего компонента и их атрибуты, вы, вероятно, захотите расширить ее, включив категорию
  • также включите категорию в список полей фильтра для этой формы
  • пакетные операции, связанные с категориями

Получение CategoryFactory

Если вы планируете поддерживать SEF-маршруты в своем компоненте, то вам понадобится пользовательский маршрутизатор, и это потребует использования Categories API для построения и анализа сегментов categories SEF-маршрута. Вы также можете захотеть использовать Categories API по другим причинам, например, для отображения подробной информации о категории в представлении категории вашего компонента.

Предпочтительным способом получения доступа к объекту Categories является создание его экземпляра из объекта CategoryFactory и получение этого объекта CategoryFactory через контейнер внедрения зависимостей (DIC). Для этого вы можете использовать следующий подход.

services/provider.php Приведенный ниже файл предназначен для компонента com_example, с пространством имен Mycompany\Component\Example (как определено в файле xml-манифеста). Строки, относящиеся к получению CategoryFactory, были прокомментированы, чтобы объяснить, что происходит.

<?php

defined('_JEXEC') or die;

use Joomla\CMS\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory as ComponentDispatcherFactoryServiceProvider;
use Joomla\CMS\Extension\Service\Provider\CategoryFactory as CategoryFactorServiceProvider;
use Joomla\CMS\Extension\Service\Provider\MVCFactory as MVCFactoryServiceProvider;
use Joomla\CMS\Extension\Service\Provider\RouterFactory as RouterFactoryServiceProvider;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\CMS\Categories\CategoryFactoryInterface;
use Joomla\CMS\Component\Router\RouterFactoryInterface;
use Mycompany\Component\Example\Administrator\Extension\ExampleComponent;
use Joomla\Database\DatabaseInterface;


return new class implements ServiceProviderInterface {
    
    public function register(Container $container): void 
    {
        
        /* The line below will call register() in libraries/src/Extension/Service/Provider/CategoryFactory.php
            * That function will create an entry in our component's child DIC with:
            *   key = 'Joomla\CMS\Categories\CategoryFactoryInterface'
            *   value = a function which will 
            *      1. create an instance of CategoryFactory, instantiated with the namespace string passed in
            *      2. call setDatabase() on that CategoryFactory instance, so that it's got access to the database object
            *      3. return the CategoryFactory instance
            */
        $container->registerServiceProvider(new CategoryFactorServiceProvider('\\Mycompany\\Component\\Example'));
        $container->registerServiceProvider(new MVCFactoryServiceProvider('\\Mycompany\\Component\\Example'));
        $container->registerServiceProvider(new ComponentDispatcherFactoryServiceProvider('\\Mycompany\\Component\\Example'));
        $container->registerServiceProvider(new RouterFactoryServiceProvider('\\Mycompany\\Component\\Example'));
        $container->set(
            ComponentInterface::class,
            function (Container $container) {
                // The next line creates an instance of our com_example component Extension class
                $component = new ExampleComponent($container->get(ComponentDispatcherFactoryInterface::class));
                $component->setMVCFactory($container->get(MVCFactoryInterface::class));
                /* The line below will get from the DIC the entry with key 'Joomla\CMS\Categories\CategoryFactoryInterface'
                    * The CategoryFactory instance will be returned, and we'll save a reference to it in our component by
                    * calling setCategoryFactory(), passing it in as the parameter
                    */
                $component->setCategoryFactory($container->get(CategoryFactoryInterface::class));
                $component->setRouterFactory($container->get(RouterFactoryInterface::class));
                $component->setDatabase($container->get(DatabaseInterface::class));

                return $component;
            }
        );
    }
};

И чтобы соответствовать этому, вам нужно иметь несколько ключевых строк в вашем классе расширения:

<?php

namespace Mycompany\Component\Example\Administrator\Extension;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Categories\CategoryServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Psr\Container\ContainerInterface;
use Joomla\CMS\Component\Router\RouterServiceTrait;
use Joomla\CMS\Component\Router\RouterServiceInterface;
use Joomla\Database\DatabaseAwareTrait;

    
class ExampleComponent extends MVCComponent implements 
    CategoryServiceInterface, RouterServiceInterface, BootableExtensionInterface
{
    use CategoryServiceTrait;
    use RouterServiceTrait;
    use DatabaseAwareTrait;
    
    // Use a static variable to store the Categories instance
    public static $categories;

    public function boot(ContainerInterface $container)
    {
        self::$categories = $this->categoryFactory->createCategory();
    }
    // ...

Используемый  CategoryServiceTrait содержит функцию setCategoryFactory, которая использовалась в services/provider.php для сохранения возвращаемой CategoryFactory. Функция сохраняет его в переменной экземпляра $categoryFactory.

Всякий раз, когда Joomla хочет выполнить компонент, она запускает файл services/provider.php (который создает экземпляр расширения) и затем вызывает boot() на этом объекте расширения. Код в boot() выше обращается к хранящемуся CategoryFactory и вызывает createCategory() на нем для создания экземпляра Categories. Результат этого хранится в статической переменной, к которой можно получить доступ в любом месте компонента, используя

`$categories = ExampleComponent::$категории;`

Код CategoryFactory createCategory() будет использовать пространство имен, которое было передано в него (первоначально в services/provider.php выше) и попытается создать экземпляр класса <namespace>\Site\Service\Category, вызывая исключение, если оно не существует. . Поэтому вам необходимо предоставить файл в области вашего сайта src/Service/Category.php, аналогичный следующему:

<?php

namespace Mycompany\Component\Example\Site\Service;

use Joomla\CMS\Categories\Categories;

\defined('_JEXEC') or die;

class Category extends Categories
{

    public function __construct($options = array())
    {
        $options['table']     = '#__example';
        $options['extension'] = 'com_example';

        parent::__construct($options);
    }
}

Таким образом, определяется множество опций для API категорий . (Вы можете передать эти опции в качестве параметра в createCategory() позвоните, но вы все равно должны предоставить свой файл класса Category.php ).

Поля с несколькими категориями

С вашим компонентом может быть связано несколько различных типов категорий. Например, если у вас есть компонент com_product, вы можете иметь

  • категория размера, которая может быть маленькой, средней, большой и т.д., и,
  • ценовая категория, которая может быть дешевой, средней, дорогой и т.д.

Для поддержки этого Joomla позволяет вам иметь разделы в разделе вашего компонента таблицы Categories, и записи вашего компонента в этой таблице будут иметь extension установите значение поля "com_product.size" или "com_product.price".

Чтобы реализовать это в вашем компоненте, вам, очевидно, понадобятся 2 столбца категории в таблице базы данных вашего компонента, например catid1, catid2.

Есть некоторые другие изменения, которые вам также необходимо будет реализовать.

Подменю администратора

Вам нужно будет иметь что-то вроде:

<menu link="option=com_categories&extension=com_product.size">Size categories</menu>
<menu link="option=com_categories&extension=com_product.price">Price categories</menu>

чтобы создать 2 категории menuitems в подменю администратора для вашего компонента.

Элемент редактирования администратором

В xml-форме редактирования продукта администратором (где вы определяете каждый продукт и связанные с ним размерные и ценовые категории) ваши поля для отображения категорий должны различать их, например

<field name=catid1 type="category" extension="com_product.size" …/>
<field name=catid2 type="category" extension="com_product.price" …/>

и аналогично для любых полей фильтра в filter_products.xml для фильтрации списка продуктов, отображаемого в форме продукты администратора.

Краткое описание категории.

В вашем классе расширения вам нужно будет предоставить функцию countItems() , которая находится в трэйте Joomla\CMS\Categories\CategoryServiceTrait. com_categories вызовет эту функцию, проходящую в категории section, и если вы используете ContentHelper::countRelations() чтобы предоставить сводку, вам нужно будет установить соответствующее поле идентификатора категории для каждого раздела – например, "catid1" для раздела "размер" и "catid2" для раздела "цена". Пример приведен ниже.

public function countItems(array $items, string $section)
{
    $config = (object) [
        'related_tbl'   => $this->getTableNameForSection($section),
        'state_col'     => $this->getStateColumnForSection($section),
        'group_col'     => $section == "size" ? 'catid1' : 'catid2',
        'relation_type' => 'category_or_group',
    ];

    ContentHelper::countRelations($items, $config);
}