Загрузка
Все статьи

Joomla: MVC-компонент с нуля — архитектура без легаси

Создаём MVC-компонент для Joomla с чистой архитектурой, современным подходом к моделям и видам — без копипаста из документации 2014 года.

Когда мне сказали, что нужно написать компонент для Joomla, я вздрогнул. Последний раз я работал с Joomla 2.5, и воспоминания были болезненные: JModelLegacy, глобальные переменные, HTML в PHP-файлах. Но Joomla 4+ изменилась кардинально — и я решил подойти к компоненту так, как подобает в 2026 году: чистый MVC, PSR-4 автозагрузка, DI-контейнер и нормальное разделение ответственности.

Архитектура MVC в Joomla

Joomla реализует классический MVC с небольшими нюансами. Модель (Model) работает с данными — через Table-классы или напрямую с JDatabase. Вид (View) отвечает за рендеринг — он получает данные от модели и передаёт их в шаблон. Контроллер (Controller) определяет, какое действие выполнить, и диспетчеризирует запрос.

В отличие от Laravel или Symfony, где роутинг конфигурируется отдельно, в Joomla он строится на соглашениях: URL /index.php?option=com_mycomp&view=items&id=42 автоматически вызывает контроллер, который загружает вид items и передаёт ему ID 42.

Структура директорий

Вот структура нашего компонента com_tasktracker:

com_tasktracker/
├── tasktracker.xml            # Манифест компонента
├── src/
│   ├── Controller/
│   │   ├── DisplayController.php
│   │   └── TaskController.php
│   ├── Model/
│   │   ├── TasksModel.php
│   │   └── TaskModel.php
│   ├── View/
│   │   └── Tasks/
│   │       └── HtmlView.php
│   ├── Table/
│   │   └── TaskTable.php
│   └── Extension/
│       └── TasktrackerComponent.php
├── tmpl/
│   └── tasks/
│       └── default.php        # Шаблон
└── sql/
    └── install.mysql.utf8.sql

Joomla 4+ поддерживает PSR-4 автозагрузку из папки src/ — namespace автоматически мапится по имени компонента.

Контроллер

Базовый контроллер обрабатывает задачу display по умолчанию:

app->getIdentity()->authorise('core.view', 'com_tasktracker')) {
            throw new \Exception('Доступ запрещён', 403);
        }

        return parent::display($cachable, $urlparams);
    }
}

Модель и Table

Модель получает данные через Table-класс, который инкапсулирует работу с конкретной таблицей базы данных:

getDatabase();
        $query = $db->getQuery(true);

        $query->select($this->getState('list.select', 't.*'))
              ->from($db->quoteName('#__tasktracker_tasks', 't'))
              ->where($db->quoteName('t.state') . ' = 1')
              ->order($db->quoteName('t.created') . ' DESC');

        // Фильтр по статусу
        $status = $this->getState('filter.status');
        if ($status) {
            $query->where($db->quoteName('t.status') . ' = :status')
                  ->bind(':status', $status);
        }

        return $query;
    }
}

Обратите внимание: мы не используем JModelLegacy — это устаревший класс из Joomla 3. Вместо него ListModel и ItemModel из нового пространства имён Joomla\CMS\MVC\Model. Параметры запроса биндятся через bind() — это защищает от SQL-инъекций без ручного экранирования.

Вид и шаблон

Вид получает данные от модели и передаёт их в шаблон. В Joomla 4 шаблоны по-прежнему PHP-файлы, но мы используем Layout-систему для переиспользования:

items = $this->get('Items');
        $this->pagination = $this->get('Pagination');

        // Проверяем, что данные загружены
        if (count($this->items) === 0) {
            $this->app->enqueueMessage('Задачи не найдены', 'info');
        }

        parent::display($tpl);
    }
}

Маршрутизация

В Joomla 4 добавили поддержку SEF-роутинга через файл services/provider.php. Мы настроили ЧПУ так:

registerView($tasks);

        $task = new RouterViewConfiguration('task');
        $task->setKey('id')->setParent($tasks, 'catid');
        $this->registerView($task);

        parent::__construct($app, $menu);
    }
}

Теперь URL /tasks/42-my-task автоматически резолвится в контроллер и вид.

Сравнение с современными PHP-фреймворками

Честно говоря, после Laravel и Symfony подход Joomla ощущается архаично. Нет middleware-концепции, DI-контейнер есть, но его использование опционально и мало кто им пользуется. Шаблоны — это PHP без шаблонизатора (Twig не интегрирован из коробки, хотя есть плагины). Роутинг завязан на имя компонента и view.

Но у Joomla есть преимущество: её админка, ACL, мультиязычность и экосистема расширений работают из коробки. Если вам нужно добавить кастомную функциональность к существующему Joomla-сайту — писать компонент гораздо логичнее, чем поднимать отдельное Laravel-приложение рядом.

Мой совет: если проект на Joomla — пишите компонент по правилам платформы, но используйте современные классы (CMS\MVC\* вместо Legacy), PSR-4 автозагрузку и биндите параметры запросов. Это не Symfony, но и не тот ужас, который был в Joomla 2.5.

Поделиться статьёй

Комментарии

0
0 / 2000

Пока нет комментариев

Будьте первым, кто оставит комментарий к этой статье

Читайте также

Joomla: кастомные поля и кэш — как я потерял 2 дня на одном неинспектируемом null

Создал кастомное поле для com_content в Joomla 4, значение сохраняется в админке, но при выводе на фронтенде — null. Оказалось, Joomla кэширует вывод модулей и плагинов, а кастомные поля в кэш не попадают. Разбираю, как работает система кэширования кастомных полей и как правильно получать их значения в overrides.

6 мая 2026 7 мин

Сложности с разделами каталога в Битрикс24 через REST API: игра с деревом

Обновляете товар через API, передаете SECTION_ID, но он не появляется в нужной папке. В этой статье я объясняю разницу между простым SECTION_ID и сложным деревом инфоблоков, как правильно выстроить иерархию и почему метод crm.product.update не умеет создавать папки.

5 июля 2026 1 мин

CMake: модульная архитектура для большого C++ проекта — без ad-hoc include путей

Как мы реорганизовали CMake-структуру проекта на 15+ модулей, избавились от хаотичных include_directories и перешли на современный CMake с target-based зависимостями.

20 июня 2026 10 мин