0

Note This is a follow up question upon Create Custom Form via SimpleXMLElement


The first issue has been solved, the subform gets rendered, here is my CustomField Code:

use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\SubformField;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormFactoryInterface;
use Joomla\Database\DatabaseInterface;
use SimpleXMLElement;

defined('_JEXEC') or die;

class OfficialsPositionsField extends SubformField {

    protected $type = 'OfficialsPositions';
    protected function getInput()
    {
        return parent::getInput(); // TODO: Change the autogenerated stub
    }

    /**
     * @throws \Exception
     */
    public function loadSubForm()
    {
        $positions = $this->getPositionsForOfficials($this->getCategoryIdForOfficials());

        $form = Factory::getContainer()->get(FormFactoryInterface::class)->createForm("officials");
        $form->load('<?xml version="1.0" encoding="utf-8"?><form> </form>');

        // Add the fields to the form.
        foreach ($positions as $position){
            $xml = new SimpleXMLElement('<field name="custom_official_'.$position->id.'" type="officials" label="'.$position->title.'" />');
            // Add the field to the form
            $form->setField($xml, $group = null, $replace = true, $fieldset = 'default');
        }


        return $form;
    }

    protected function loadSubFormData(Form $subForm)
    {
        echo '<pre>' . var_export($this->value, true) . '</pre>'; // << This is okey the field value (if given) gets loaded from db
        return parent::loadSubFormData($subForm); // TODO: Change the autogenerated stub
    }

    protected function getCategoryIdForOfficials()
    {
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);
        $query->select('id');
        $query->from('#__categories');
        $query->where('alias = ' . $db->quote('official'));
        $db->setQuery($query);
        $categoryId = $db->loadResult();
        return $categoryId;
    }

    protected function getPositionsForOfficials($categoryId)
    {
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);
        $query->select('id, title');
        $query->from('#__footballmanager_positions');
        $query->where('catid = ' . $categoryId);
        $query->order('ordering ASC');
        $db->setQuery($query);
        $positions = $db->loadObjectList();

        return $positions;
    }


}

Subform creates Selection for each Position that can be generated in another view

The subform gets displayed in the form by loading the field via XML (name="officials") into the component backend item edit view, but on save of the element there is no key "officials" in the $data array. It's the first time I extend the SubformField so is there a method to call to get the Values? I tough this would work "magically"

Update Based on Robbie's answer I've checked my console and have done an error_log in my Model inside the save public function: error_log:

[11-Jan-2024 12:26:55 UTC] Array
(
    [id] => 1
    [home_team_id] => 1
    [away_team_id] => 1
    [league_id] => 1
    [season_id] => 1
    [phase_id] => 1
    [location_id] => 1
    [kickoff] => 2024-01-04 22:04:17
    [home_score] => 0
    [away_score] => 0
    [home_touchdowns] => 0
    [away_touchdowns] => 0
    [tickets_link] => 
    [game_finished] => 0
    [game_postponed] => 0
    [new_game_id] => 
    [game_canceled] => 0
    [matchday] => 
    [alias] => 1-1-2024-01-04-22-04-17
    [published] => 1
    [created_at] => 2024-01-09 21:04:01
    [access] => 1
    [description] => 
    [head_referee_id] => 3
    [sponsors] => Array
        (
        )

    [catid] => 108
    [tags] => Array
        (
        )

)

The error_log statement is placed nearly at start of method in the code:

public function save($data)
    {
        $app   = Factory::getApplication();
        $input = $app->getInput();
        $user  = $app->getIdentity();

        // new element tasks
        if (!isset($data['id']) || (int) $data['id'] === 0)
        {
            $data['created_by'] = $user->id;
        }

        error_log('save');
        error_log(print_r($data, true));
...

And what my console showed as post: Console POST jform

So does these fields got stripped out because they are not mentioned in the XML? its also not the structure I've awaited - but okey this could be handeled if they are not stripped out. The field that holds this subform (with this fields) is defined as "officials" see row 242: https://github.com/marcorensch/com_footballmanager_2/blob/main/administrator/components/com_footballmanager/forms/game.xml

Btw. The Repo is public here: https://github.com/marcorensch/com_footballmanager_2

2 Answers 2

2

I think this might be because you haven't got your officials fields within the 'jform' array.

When you create a form via your model, you'll call

$form = $this->loadForm(
        'com_something.xxx',
        'myname',
        array(
            'control' => 'jform',
            'load_data' => $loadData
        )
    );

The 'control' option is then used as the name of the array into which the form fields are inserted in the HTTP POST generated by the form submit. If you don't specify anything here then it takes 'jform' as the default.

However when you create a Form manually then it doesn't provide a default and you have to call:

    $form = Factory::getContainer()->get(FormFactoryInterface::class)->createForm("officials", array("control" => "jform"));

(or whatever name you specified for the 'control' option).

You can check this in your browser devtools. Look at the Request of your HTTP POST, and ensure that in the payload the "name" field of your officials fields is of the same format as your other fields.

1
  • Thanks for this hint - please see my updated information above... i dont understand whats going on here :-)
    – Marco
    Jan 11 at 12:42
0

Okey now it works, I've checked the main subform code, created an empty XML file and combined it like so:

subform_officials.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Repeatable Subform -->
<form>
</form>

game.xml (where the subform field gets loaded:

...
<field
                name="officials"
                type="OfficialsPositions"
                label="COM_FOOTBALLMANAGER_FIELD_OFFICIALS_LABEL"
                min="1"
                max="1"
                layout="joomla.form.field.subform.repeatable"
                multiple="false"
                formsource="administrator/components/com_footballmanager/forms/subform_officials.xml"
        />
...

OfficialsPositionsField

...
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\SubformField;
use Joomla\CMS\Form\Form;
use Joomla\Database\DatabaseInterface;
use SimpleXMLElement;

defined('_JEXEC') or die;

class OfficialsPositionsField extends SubformField {

    protected $type = 'OfficialsPositions';

    /**
     * @throws \Exception
     */
    public function loadSubForm()
    {
        $positions = $this->getPositionsForOfficials($this->getCategoryIdForOfficials());
        $control = $this->name;

        // Prepare the form template
        $formname = 'subform.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $this->name);
        $tmpl     = Form::getInstance($formname, $this->formsource, ['control' => $control]);

        // Add the fields to the form.
        foreach ($positions as $position){
            $xml = new SimpleXMLElement('<field name="custom_official_'.$position->id.'" type="officials" label="'.$position->title.'" />');
            // Add the field to the form
            $tmpl->setField($xml, $group = null, $replace = true, $fieldset = 'default');
        }
        
        return $tmpl;
    }

    protected function getCategoryIdForOfficials()
    {
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);
        $query->select('id');
        $query->from('#__categories');
        $query->where('alias = ' . $db->quote('official'));
        $db->setQuery($query);
        $categoryId = $db->loadResult();
        return $categoryId;
    }

    protected function getPositionsForOfficials($categoryId)
    {
        $db = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);
        $query->select('id, title');
        $query->from('#__footballmanager_positions');
        $query->where('catid = ' . $categoryId);
        $query->order('ordering ASC');
        $db->setQuery($query);
        $positions = $db->loadObjectList();

        return $positions;
    }
}

I'm now testing on how to get rid of this empty xml file.

Edit - Without empty xml:

...
        // Prepare the form template
        $formname = 'subform.' . str_replace(['jform[', '[', ']'], ['', '.', ''], $this->name);
//      $form     = Form::getInstance($formname, $this->formsource, ['control' => $control]); // not longer needed see next lines
        $form = Factory::getContainer()->get(FormFactoryInterface::class)->createForm($formname, ['control' => $control]);
        $form->load('<?xml version="1.0" encoding="utf-8"?><form> </form>');
...

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.