Архитектурно такое делается системным плагином или плагином группы user. Плагин на событие onContentPrepareForm должен проверить форму и добавить туда свой xml.
public function onContentPrepareForm(Event $event): void
{
$form = $event->getArgument(0);
$formName = $form->getName();
// Проверяем имя формы, чтобы не добавить таб в материалы или ещё куда-нибудь
if ($formName === 'com_users.user')
{
Form::addFormPath(JPATH_SITE . '/plugins/user/wtamocrmusersync/form');
// fields - это имя файла в указанной папке - fields.xml
$form->loadFile('amocrm', false);
// грузим языковые константы для формы
$lang = $this->getApplication()->getLanguage();
$extension = 'lib_webtolk_amocrm';
$base_dir = JPATH_SITE;
$lang->load($extension, $base_dir);
}
}
В xml-файле форма с кастомным полем:
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset name="wtamocrm" label="PLG_WTAMOCRMUSERSYNC_WTAMOCRM_FIELDSET_LABEL">
<field
name="amocrm_user_info"
addfieldprefix="Webtolk\Amocrm\Fields"
type="amocrmcontactinfo"
joomlauseridsource="id"
showtags="true"
label="PLG_WTAMOCRMUSERSYNC_USER_PROFILE_AMOCRM_INFO"
/>
</fieldset>
</form>
Само поле состоит из класса поля и, желательно, лейаута (макета вывода). Класс поля у меня выглядит так, так как я использую лейаут для вывода разметки.
<?php
/**
* @package WT Amocrm Library
* @version 1.3.0-alpha2
* @Author Sergey Tolkachyov, https://web-tolk.ru
* @copyright (c) 2022 - May 2025 Sergey Tolkachyov. All rights reserved.
* @license GNU/GPL3 http://www.gnu.org/licenses/gpl-3.0.html
* @since 1.0.0
*/
namespace Webtolk\Amocrm\Fields;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\FormField;
use Joomla\CMS\Language\Text;
use Webtolk\Amocrm\Amocrm;
use Webtolk\Amocrm\Helper\UserHelper as AmocrmUserHelper;
class AmocrmcontactinfoField extends FormField
{
protected $type = 'Amocrmcontactinfo';
protected $layout = 'libraries.webtolk.amocrm.fields.amocrmcontactinfo';
/**
* Method to get the data to be passed to the layout for rendering.
*
* @return array
*
* @since 1.3.0
*/
protected function getLayoutData(): array
{
$layoutData = parent::getLayoutData();
$joomlauseridsource = (!empty($this->element['joomlauseridsource'])) ? (string)$this->element['joomlauseridsource'] : '';
// `joomlauserid` param has a higher priority
if(!empty($this->element['joomlauserid'])) {
$joomlauserid = (int)$this->element['joomlauserid'];
} elseif(!isset($this->element['joomlauserid']) && !empty($joomlauseridsource)) {
$joomlauserid = Factory::getApplication()->getInput()->getInt($joomlauseridsource, 0);
}
// Тут ещё куча проверок и условий.
// Сокращаем...
$layoutData['has_amocrm_contact_id'] = true;
$amocrm = new Amocrm();
$result_amo_crm = $amocrm->contacts()->getContactById($contact_id);
if (!empty($result_amo_crm->error_code))
{
$layoutData['label'] = '<span class="badge bg-danger"><i class="fa-solid fa-triangle-exclamation"></i></span> '.$layoutData['label'];
$layoutData['has_error'] = true;
$layoutData['amocrm_error'] = $result_amo_crm;
} else {
$layoutData['contact_info'] = $result_amo_crm;
$layoutData['contact_link'] = $amocrm->getRequest()->getAmoCRMHost()->setPath('/contacts/detail/'.$contact_id);
}
return $layoutData;
}
}
Если вёрстка простая, то иногда можно вместо
getLayoutData() всё делать в методе
getInput(), но это уже хардкод и не будут поддерживаться переопределения макетов. Так лучше не делать. В
getLayoutData() можно сделать нужные запросы в базу, но лучше использовать модели ядра Joomla. Если же это кастомные таблицы, то свой код лучше оформить в виде какого-нибудь расширения Joomla - либо плагин, либо библиотеку. Пример с библиотекой дан выше. Методы плагинов Joomla можно использовать извне
следующим образом:
use Joomla\CMS\Factory;
// Указываем группу плагина и element
$myPlugin = Factory::getApplication()->bootPlugin('system', 'myplugin');
// И вызываем нужный нам метод плагина "Сделать хорошо"
$result = $myPlugin->doItWell();
А уже в плагине сделать нужные методы, но при этом не подключать никакие события Event Dispather.
Результат выглядит так: