1

Note: Question fully updated with the latest findings:

I want to load a com_contact contact view inside a template override of an article. I have a contact item id and trying to load the record and then trying to find a way to load and render its form.

Following @Irata answer, I did try to use the MVCFactory class, to load/render the contact's view. Unfortunately, I can not find a way to populate the Contacts View with the proper data, so I thought I should try with the Contacts Display Controller. But this also has problems.

So:

$comContactMVCFactory = \Joomla\CMS\Factory::getApplication()
    ->bootComponent('com_contact')
    ->getMVCFactory();

So, now it's time for $comContactMVCFactory->createController(). The method requires 5 parameters. The last one $input, is of Joomla\Input\Input type. So, I need to pass that type of data there.

$app->input->set('option', 'com_contact');
$app->input->set('view', 'contact');
$app->input->set('id', 25);
$app->input->set('Itemid', 2091);
$app->input->set('layout', 'default');

$input = $app->input;

$comContactMVCFactory = \Joomla\CMS\Factory::getApplication()
->bootComponent('com_contact')
->getMVCFactory();

$config = [
    'name' => 'contact',
    "base_path" => JPATH_SITE . "/components/com_contact",
    'model_path' => JPATH_SITE . "/components/com_contact/forms",
    'view_path' => JPATH_SITE . "/components/com_contact/forms",
];

$controller = $comContactMVCFactory->createController('Display', 'Site', $config, $app, $input);
$controller->display();

Here there is one major problem at first. Τhe $controller input data is reverted back to the Factory::getApplication()->input. I checked the constructor of the Display Controller and it contains the following code, which makes the $input variable that we are enforced to pass, as irrelevant, since it always defaults to the current $app->input.

In simple words - for the Contacts Controller, it's impossible to feed it with custom $input data.

DisplayController Contructor of Contacts

public function __construct($config = [], MVCFactoryInterface $factory = null, $app = null, $input = null)
    {
        // Contact frontpage Editor contacts proxying.
        $input = Factory::getApplication()->getInput();

But let's say that we comment out that line of code and try to see how to achieve this (for experimental causes).

I commented out that line that sets again the $input variable inside the constructor (just for the shake of experiment).

Then the above code produces:

Form::loadForm could not load file

This is the backtrace:

  1. JROOT/libraries/src/MVC/Model/FormBehaviorTrait.php:120
  2. Joomla\CMS\MVC\Model\FormModel->loadForm() JROOT/components/com_contact/src/Model/ContactModel.php:107

The path for the contact.xml form is not set correctly and at the moment I can not find a way to set properly the form paths.

1 Answer 1

1

With Joomla 4 you can load the view of another component by using the createView command of the MVCFactory class. You 'boot' the com_contact component and then request the view you want from that component.

$fred = $app->bootComponent('com_contact')->getMVCFactory();

$view = $fred->createView('contact', 'Site', 'html', $config);

There is a not a lot of documentation available that I can find about the method and I am only just starting to use it myself, however you can find the method in ..\libraries\src\MVC\Factory\MVCFactory.php around line #156 if you want to read the code.

I would expect that you could pass the id of the contact you want through the $config param however that would be dependant on the code in the com_contact View class. Otherwise perhaps pass the id through the State.

There are also createController and createModel methods available that might be useful to you as an alternative to get the data into the view you want.

This is should help you, https://www.dionysopoulos.me/book/com-mvcfactory.html

Update 4th September

I have spent much the day trying to get something to work and I have managed to get a com_contact Edit form to appear in my calling component. It still needs work on the language fields that aren't loading but I can edit a specified contact record and save it, however the save will leave the calling component and re-display the com_contact edit form.

In practice it is possible to display a View from another component however to be useful in a production sense the View needs to be written with the expectation that it is going to be injected into another component as some of the methods and techniques used by the core aren't quite ready for being injected into another component. For simple Views and layouts I think it could be useful.

An example of the "aren't quite ready" and the biggest speed hump for me was the Form processing which has issues in the setting of the Paths to find the required Form. In formBehaviours doing the path setting it uses the JPATH_COMPONENT constant to prefix the directory structure which is set to the calling component and not the called component whose form we are looking for.

My interest is to share some Views from my components and for that purpose I think it will be a useful feature if I can write my View correctly. I am reusing Models all over the place and it works well, but Views need a bit more effort.

Thanks to the updated original post I was able to get past some of the other issues thrown up reasonably quickly, so thank you.

For now this sample will work in allowing you to edit a Contact and if I sort out the loading of the language files it might look prettier. Note that you will have to update the contactModel file to use the addFormPath shown in the comments unless someone has a idea for how I can set the path from my example, anything I tried failed.

class HtmlView extends BaseHtmlView
{
    public function display($tpl = null): void
    {
        //This is needed to find the layout
         $config = ['base_path' => JPATH_ADMINISTRATOR . "/components/com_contact"];

        $record_id = 1;

        $app      = Factory::getApplication();

        $app->input->set('id', $record_id);     
        $input = $app->input;

        $fred           = $app->bootComponent('com_contact')->getMVCFactory();
        $view           = $fred->createView('contact', 'administrator', 'html', $config);
        $view->setModel($fred->createModel('contact', 'Administrator', config: $config), true);

        $barney         = $view->getModel('contact');
        // $Barney needs an addFormField included here somehow, otherwise needs to be coded in the contactModel
        //        Form::addFormPath(JPATH_ADMINISTRATOR . '/components/com_contact/forms');

        $view->setLayout('edit');    //If not set will look for file called default.php        
        $view->document = $this->document;      // Needed to find webassets etc in the template
        $view->display();

        // parent::display($tpl);
    } 
5
  • Thanks for this input. Unfortunately, I am still struggling to find the way to render the contact form for the contact I want. I tried with creating the view, but I couldn't find a way to pass the $item to the view. I then tried with creating the controller and execute its display method. The createController method requires 5 parameters, the last one ($input) must be of type Joomla\Input\Input. I am passing an input object with my data, but it defaults to the current $app->input data... because its constructor does so. I am updating my question above with more details.
    – FFrewin
    Aug 3, 2023 at 11:32
  • I have updated my answer with a sort of working sample of code.
    – Irata
    Aug 4, 2023 at 6:19
  • Hey Irata! Thanks for the update. I am trying to call and load the com_contact contact item through a Joomla article template override. Most of the issues you mentioned in your solution, are issues that I also faced - and I spent almost two days experimenting with this staff. In my opinion, it shouldn't take that much to find a solution for a kinda simple problem - It's not worth to have to spend that much time researching for something, going all over the places in the codebase to try to understand how things work, and at the end, to realise that things will not work exactly as you would ...
    – FFrewin
    Aug 4, 2023 at 12:53
  • ... have wished for. Two many obstacles to overcome and problems to solve in order to make this thing work and no customer would ever pay for all this effort. I didn't charge my customer for this... but I insisted in order to see what it's possible and with the hope that something like this could work flawlessly. But, after so many hours, I decided to stop trying to accomplish that and to find another more productive way to complete the task...
    – FFrewin
    Aug 4, 2023 at 12:53
  • ... I will go with a custom RSForm pro form, which I will render at the bottom of the article and will be able to get values related to the com_contact item I want, and stored within that article object, and have the form programmed to perform the required tasks based on those values.
    – FFrewin
    Aug 4, 2023 at 12:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.