Joomla 4 "под капотом" претерпела немало изменений относительно предыдущих версий. Её кодовую базу сообщество разработчиков регулярно подтягивают до современных реалий, вводя актуальные технологии в ядро CMS. Так, например, если раньше загрузка классов была вариациями на тему include, то в Joomla 4 появился лоадер, приведённый к PSR-4. Ядро CMS переводится на концепцию сервис-провайдеров, внедрены DI-контейнеры, переработанная система событий для плагинов позволила увеличить производительность при генерации страниц более чем в два раза. Эти изменения влекут за собой изменения в структуре компонентов, модулей и плагинов.  В данной статье пойдёт речь о том, как создать модуль для Joomla 4 с новой структурой файлов и классов. К слову сказать, legacy ещё работает и многие расширения, созданные по канонам Joomla 3 (а не работавшие на Joomla 3, но написанные по канонам Joomla 1.5) ещё долго будут работать на Joomla 4. Я предполагаю, что часть читателей имеет опыт работы с Joomla, но не имеет опыта создания модулей, поэтому постараюсь описать создание модуля как можно подробнее. Статья имеет сугубо прикладной характер, без погружения в теорию ООП и его реализацию в Joomla. Основная цель - подсказать что "делать руками", когда поставлена определённая задача. Рассказывать о создании модуля я буду на примере своего модуля WT Yandex map items - модуля вывода материалов Joomla на Яндекс.карты по координатам из пользовательских полей, который создавался под проект на Joomla 4, поэтому названия файлов, классов и namespace будут содержать название именно этого модуля. При создании своего модуля, естественно, нужно изменить их на свои. Для создания модуля было необходимо как минимум 3 файла:  mod_wtyandexmapitems.xml - описание модуля для установщика расширений Joomla (системное имя, дата, версия, сайт разработчика и т.д.), параметры конфигурации, сервер обновлений и т.д. mod_wtyandexmapitems.php - "точка входа" в модуль. С этого файла начинается работа Вашего кода. tmpl/default.php - макет вывода для модуля. Здесь находится HTML-вёрстка Вашего модуля. При необходимости, можно скопировать и переименовать этот файл, изменить вывод HTML по своему вкусу и выбрать в настройках свой новый макет вывода. Этот же способ позволяет выполнять любой свой PHP-код в нужном месте и в нужное время. Именно такую структуру мы видим в одном из простейших модулей Joomla - mod_custom - "HTML-код". Если наш модуль делает что-то более сложное, чем просто вывод значений из настроек модуля, например: отображает список новых статей на сайте; показывает карусель товаров из компонента интернет-магазина; выводит популярные комментарии или фотографии из фото-галереи ; подтягивает данные модуля по ajax (ajax-корзина товаров, к примеру). то все функции, выполняющие эту работу, помещаются в хелпер модуля. В Joomla 3 он помещался в файл helper.php, находящийся рядом с основным php-файлом. Подключение хелпера было в "точке входа" с помощью JLoader::register('ModWtyandexmapitemsHelper', __DIR__ . '/helper.php'); Для создания модуля в Joomla 4 нужны следующие файлы (с меньшим количеством можно поэкспериментировать спортивного интереса ради):  Этот файл содержит описание модуля для установщика расширений Joomla (системное имя, дата, версия, сайт разработчика и т.д.), параметры конфигурации, сервер обновлений, а также задаёт Namespace модуля и директории для автозагрузки классов.  Также обратите внимание, что для корректной установки и работы модуля нужно указывать атрибут module="mod_wtyandexmapitems" в xml-манифесте. Если в Joomla 3 этот атрибут указывался для файла "точки входа" (<filename module="mod_wtyandexmapitems">mod_wtyandexmapitems.php</filename>), то сейчас он указывается для папки src модуля - <folder module="mod_wtyandexmapitems">src</folder>  . Ещё одно нововведение связано с языковыми файлами: теперь в именах файлов не обязательно дублировать префикс языка - "ru-RU.mod_wtyandexmapitems.ini". Достаточно того, что файл лежит в папке "ru-RU". Файл - сервис-провайдер Вашего модуля. Он сообщает Joomla, что Ваш модуль существует и регистрирует namespace модуля в глобальном пространстве имён. Некоторые модули для Joomla могут быть довольно сложными, использовать дополнительные PHP-библиотеки и SDK, поэтому в папке src модуля могут быть самые разные Namespace, которые можно зарегистрировать в сервис-провайдере, дабы они были доступны глобально. Однако, я предпочитаю библиотеки оформлять отдельными расширениями Joomla и устанавливать их в папку libraries в корне сайта, а обращаться к ним уже по namespace. Это удобно для тех случаев, когда не одно Ваше расширение использует данную библиотеку, а несколько. В таком случае для библиотеки потребуется системный плагин, регистрирующий её namespace в глобальном пространстве имён. Берём на заметку, что в Namespace указывается "клиент" модуля - "Site" или "Administrator". Этот файл используется для того, чтобы передать данные из хелпера модуля в макет (layout). Особый интерес для нас представляет функция getLayoutData(), так как именно в ней мы обращаемся к методам нашего хелпера модуля и помещаем полученные данные в массив $data. Ключ массива $data может быть любым и может быть не единственным. Можно провести параллель с Model в MVC, когда мы из разных мест собираем данные и передаём их для отображения.  В хелпере модуля можно собрать один результирующий массив данных и в диспетчере передать на рендер только его (обратившись только к одному методу хелпера - точке входа в хелпер). А можно обращаться к разным методам хелпера в диспетчере, присваивая данные разным элементам массива $data. Подход к реализации Вы выбираете сами, распределяя функционал между этими двумя файлами.  Хелпер модуля. Имя файла = имя модуля без суффикса "mod_" + Helper (с заглавной буквы).  Namespace хелпера - Joomla\Module\Wtyandexmapitems\Site\Helper. Вместо "Site" может быть "Administrator", если у Вас модуль для панели администратора, например для дашбордов Joomla 4. Имя класса совпадает с именем файла. Внутри - нужные Вам функции. В данном случае в методе getPlacemarks() я получаю с помощью нескольких методов список материалов Joomla 4, их пользовательские поля, выбираю (сообразно настройкам модуля) поле, в котором хранятся координаты, а затем собираю массив со структурой, необходимой для Яндекс карт.  Если Вашему модулю есть что отдать по ajax на фронт, то для этого нужно в хелпере модуля создать метод getAjax().В нашем случае на Яндекс.карты будет загружаться более 100 меток с текстами и картинками. Поэтому целесообразнее получать эти данные по ajax. Согласно документации Joomla по использованию ajax Вы можете в запросе указывать конкретный метод хелпера. В таком случае имя метода должно заканчиваться на "Ajax": например method=mySuperAwesomeMethodToTrigger вызовет метод mySuperAwesomeMethodToTriggerAjax модуля. Пример ajax-запроса, реализованного нативными средствами Joomla (статья-мануал на Хабре).  Здесь по-прежнему находится HTML-вёрстка Вашего модуля. Его по-прежнему можно скопировать в ту же папку или в папку с Вашим шаблоном (сделать переопределение), переименовать и изменить вывод HTML, не отказывая себе в самых страшных извращениях и при этом не опасаясь того, что Ваши изменения будут затёрты при обновлении движка. В файлах макетов вывода, как правило, находится цикл вывода данных foreach.  В Joomla 3 нередко можно было встретить следующую конструкцию: В Joomla 4 в принципе осталось то же самое, за небольшими изменениями: данные мы получаем из хелпера и передаём не в "точке входа" (которой теперь нет по определению), а в файле src/Dispatcher/Dispatcher.php. Те самые $data['placemarks'] в макете вывода становятся просто $placemarks. "рядом" с Вашими переменными передаются следующие: $module - объект модуля. Оттуда Вы можете взять id модуля ($module->id), заголовок модуля, его позицию и т.д. $app - объект приложения. Это значит, что Вам не нужно самостоятельно вызывать Joomla\CMS\Factory::getApplication(). Он уже есть для Вашего удобства. $input - также в макете модуля теперь сразу доступен объект Input (через него мы получаем GET, POST параметры, SERVER и т.д.), который раньше приходилось вызывать самостоятельно. $params - параметры модуля. Получаем их как раньше: $params->get('param_name' , 'default_value_if_value_is_empty'). Эти параметры мы собираем с помощью различных типов полей Joomla в xml-манифесте модуля. $template - параметры настроек стиля текущего шаблона. У шаблонов Joomla есть templateDetails.xml, в которых можно задавать различные параметры шаблона: логотипы, шрифты, пользовательские скрипты в <head> и <body> и всё, что душе угодно. Теперь в модуле Вы имеете возможность без лишних шевелений получить доступ к этим параметрам. Однако, стоит помнить, что многие студийные шаблоны (JoomShaper Helix и иже с ними) не используют стандартное место хранение параметров, поэтому там может оказаться пусто. Как и в Joomla 3, в Joomla 4, если Вам не хватает стандартных типов полей, у Вас есть возможность создавать свои типы полей. Это могут быть нестандартные выборки из базы данных, получение значений списка из сторонних сервисов по API и т.д.  Возможность создавать свои пользовательские типы полей открывает широкие возможности Joomla. Наглядный пример: В Joomla 3 Вам надо было указать свой тип поля и назначить атрибут addfieldpath родительскому <fieldset> или напрямую <field>. Например Php-файл поля находится в папке с модулем modules/mod_wtyandexmapitems/fields. В Joomla 4 атрибут addfieldpath  не работает. Вместо него используется атрибут addfieldprefix, в котором нужно указать namespace для пользовательских полей модуля.  Поля мы складываем в src/Fields. У файлов полей должен быть namespace namespace Joomla\Module\Wtyandexmapitems\Site\Fields. Я использую собственный тип поля, расширяющий тип поля spacer (пробел), для вывода своего логотипа, версии модуля, ссылки на сайт и иногда дополнительной информации. А вот пример использования в Joomla 3. Плагин для двухсторонней интеграции Joomla с CRM Битрикс 24 в настройках показывает информацию об аккаунте, из-под которого создан вебхук на стороне Битрикс 24. Если информация отображается, значит плагин настроен верно. А здесь в настройках плагина отображается список стадий лида (или сделки), получаемый по API из CRM Битрикс 24. Это так же реализовано с помощью пользовательских типов полей (пример из версии для Joomla 3). Если у Вас совсем нет времени, а завести "со шморгалкой" старый модуль на Joomla 4 всё-таки надо, поддерживается (пока что) как старый, так и гибридный вариант структуры модуля. Пока что можно обойтись без сервис-провайдера. Вообще. Тогда нужен файл "точки входа" mod_wtyandexmapitems.php и соответствующая строка в xml-манифесте модуля. Пока что можно обойтись без папки src. И подключать хелпер по старинке через JLoader::register('ModWtyandexmapitemsHelper', __DIR__ . '/helper.php'). Соответственно файл helper.php должен лежать рядом с "точкой входа" в модуль. Можно переместить хелпер в папку src, переименовать файл, назначить ему namespace (и в xml-манифесте модуля тоже) и использовать в "точке входа" просто use Joomla\Module\Wtyandexmapitems\Site\Helper\WtyandexmapitemsHelper  - namespace хелпера. На момент написания статьи большая часть даже стандартных модулей Joomla переделана именно так, с частичным сохранением старой структуры. Полностью новым канонам пока что соответствует лишь модуль панели управления mod_quickicon - иконок быстрого доступа. Из статьи Распространенные ошибки при написании плагинов Joomla 4 Вы должны использовать ТОЛЬКО ДВА метода \Joomla\CMS\Factory в Joomla 4: getContainer() - возвращает контейнер внедрения зависимостей Joomla (DI Container, иногда сокращенно DIC). getApplication() - возвращает текущий объект приложения Joomla, обрабатывающий запрос. Всё. Больше ничего другого использовать не нужно! Всё остальное предоставляется либо через DI-контейнер, либо через сам объект приложения. Чтобы получить документ приложения используйте \Joomla\CMS\Factory::getApplication()->getDocument(). Статья Как правильно подключать JavaScript и CSS в Joomla 4   Статья на хабре Использование WebAssetsManager Joomla 4 и добавление собственных пресетов с помощью плагина. Мы помним, что все CSS и JS файлы должны лежать в папке media. Подробнее в статьях. Многие из этих методов работали ещё со времен Joomla 1.5 (с 2008 года!). JRequest::getUri() заменяем на $uri = Joomla\CMS\Uri::getInstance() и читаем документацию к нему. методы JRequest::getCmd и аналогичные перекочевали в Joomla\Input\Input или (что проще) $app->getInput(). Пока что поддерживается устаревший синтаксис $app->input, но в Joomla 5 (выйдет осенью 2023 года) он может быть удалён (план выпуска релизов и принципы удаления устаревшего кода в Joomla). $app->isAdmin() и $app->isSite() стали $app->isClient('Site') и $app->isClient('Administrator'). Подключение к базе данных: вместо JFactory::getDbo() (или Joomla\CMS\Factory::getDbo) используем $app->getContainer()->get('DatabaseDriver') ('DatabaseDriver' регистрозависимый). Получение объекта пользователя:  вместо JFactory::getUser() (или Joomla\CMS\Factory::getUser()) используем $app->getIdentity() Буду рад, если статья окажется полезной для разработчиков. Также буду признателен замечаниям и исправлениям, если таковые имеются у читателей. форум русской поддержки Joomla. интернет-портал Joomla-сообщества. https://vc.ru/s/1146097-joomla - Сообщество Joomla на VC. Чат сообщества «Joomla! по-русски». Joomla для профессионалов, разработчики Joomla. Новости о Joomla! и веб-разработке по-русски. Вакансии и предложения работы по Joomla: фуллтайм, частичная занятость и разовые подработки. Размещение вакансий здесь. Англоязычный чат сообщества. Новости Joomla! по-английски  Joomla-разработчик (WebTolk). Муж. Отец 3 детей.