1

I've created an extended subform field type called families.php, basically I want to have my own custom layout for this custom field, it makes sense I don't want to mess up everything just for this case.

jimport('joomla.form.formfield');
require_once JPATH_LIBRARIES . '/joomla/form/fields/subform.php';

class JFormFieldFamilies extends JFormFieldSubForm {

    /**
     * Field specs
     */
    protected $type = 'Families';
    protected $formsource;
    protected $layout = 'joomla.form.field.families'; // THIS IS IMPORTANT PART, see below
    protected $groupByFieldset = false;

    /**
     * Cached large array of the families.
     */
    public static $families = array();

    /**
     * Method to attach a JForm object to the field.
     */
    public function setup(SimpleXMLElement $element, $value, $group = null)
    {
        if (!parent::setup($element, $value, $group))
        {
            return false;
        }

        // Get the field families/options, a cache of ::getFamilies();
        if ( empty(static::$families) ) {
            $cache = JFactory::getCache();
            static::$families = $cache->call( array( 'JFormFieldFamilies', 'getFamilies' ) );
        }

        foreach (array('formsource', 'layout', 'groupByFieldset') as $attributeName)
        {
            $this->__set($attributeName, $element[$attributeName]);
        }

        if ($this->value && is_string($this->value))
        {
            // Guess here is the JSON string from 'default' attribute
            $this->value = json_decode($this->value, true);
        }

        if ( !$this->formsource && is_array($this->value) )
        {
            // Set the formsource the content of the static::$families
            $this->formsource = '
<form>
  <field name="family" type="list" label="Family" default="" value="Johnsons">
    '. // a list of static::$families .'
  </field>
  <field name="girls" type="list" multiple="true" label="Girls" default="400">
    '. // a list of static::$families[$family][$girls] .'
  </field>
  <field name="boys" type="list" multiple="true" label="Boys" default="Robert">
    '. // a list of static::$families[$family][$boys] .'
  </field>
</form>';
    }

    // these function are unchanged, perhaps I can remove from this file
    protected function __get() {}
    protected function getInput() {}
    protected function getName($fieldName) {}
    // this one is probably most important for me, changed
    public function __set($name, $value)
    {
        switch ($name)
        {
            case 'formsource':
                $this->formsource = (string) $value;
                break;

            case 'groupByFieldset':
                if ($value !== null)
                {
                    $value = (string) $value;
                    $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0');
                }
                break;

            case 'layout':
                $this->layout = (string) $value;

                // Make sure the layout is not empty.
                if (!$this->layout)
                {
                    // HERE'S THE TRICKY PART
                    // $this->layout = 'templates.MYTEMPLATE.html.layouts.joomla.form.field.families';
                    $this->layout = 'joomla.form.field.families';
                }

                break;

            default:
                parent::__set($name, $value);
        }
    }

    // a full array of families
    static function getFamilies() {}
}

In my templates/MYTEMPLATE/html/layouts/joomla/form/field folder I have families.php, basically a copy of the subform/repeatable-table.php. So far, if I use joomla.form.field.subform.repeatable-table it's working fine, however, my layout file isn't working and I don't know how to handle this, it's beyond me.

Basically I see an empty <div class="controls"> inside my form.

Thanks for any inputs/suggestions.

3
  • 1
    Please improve the description of how your layout "isn't working". Commented May 16, 2019 at 12:43
  • Oh God, I found this, I think I made it work. I will provide an answer as soon as I finish.
    – thednp
    Commented May 16, 2019 at 12:59
  • Great, just be sure to give us great content here. Super clear questions/issues and correct/educational answers make this site a beautiful place for researchers. We love code snippets, screenshots, links to documentation. Give us the how and the why to the best of your ability. Thanks. Commented May 16, 2019 at 13:01

2 Answers 2

1

Add custom paths with getLayoutPaths() in the field:

protected function getLayoutPaths()
{
    return array_merge(
        array(JPATH_ROOT . '/templates/your_template/html/layouts'),
        parent::getLayoutPaths()
    );
}

If you have a field like:

<field
    name="whatever"
    layout="joomla.form.field.families"
    type="families"
/>

This will render templates/your_template/html/layouts/joomla/form/field/families.php.

It's up to you what folder structure to use, but I would not use joomla folder. It's meant for core layouts.

7
  • Thanks for taking the time to answer, will test this right away.
    – thednp
    Commented May 18, 2019 at 11:52
  • '/templates/your_template/html/layouts' that's no good for me unfortunately, I need to have it in templateName/layouts folder to access from templateName/fields/customField.php field so I can easily work with dirname(__FILE__) and avoid quering the database for the templateName.
    – thednp
    Commented May 18, 2019 at 11:58
  • 1
    As I said, you can use any folder structure you want. This just shows how to register layout paths correctly. You can use dirname() or anything else here, no problem. If you need template name, you can also get it from the form using $this->form->getValue('template').
    – Sharky
    Commented May 18, 2019 at 12:22
  • Considering the function you provided and the modifications I proposed based on the folder structure, what would be the string of the layout path? I can imagine it's not going to be anything like joomla.form.fields.myfield.
    – thednp
    Commented May 18, 2019 at 13:13
  • 1
    Layout is relative to available layout paths. If you use dirname(__FILE__) as layout path in templates/templateName/fields/customField.php (which results in templates/templateName/fields) and field layout is joomla.form.field.myfield, the resulting layout file is templates/templateName/fields/joomla/form/fields/myfield.php.
    – Sharky
    Commented May 18, 2019 at 13:28
1

OK so based on the new documentation resource I found, I can actually set an independent layout for anything related to custom form fields, exactly what I wanted really. I don't need to make this field generally available for users via com_fields, just a quick and totally Joomla based tool for building specific admin forms.

So I removed most of the code you see above, I basically re-purposed the getInput() method to just call the layout I need and let it go.

/**
 * Method to get the field input markup.
 */
protected function getInput()
{
    $value = $this->value ? (array) $this->value : array();

    // Prepare data for renderer
    $data    = parent::getLayoutData();
    $tmpl    = null;
    $forms   = array();
    $control = $this->name;

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

        // Prepare the forms for exiting values
        $tmpl->bind($value);
        $forms[] = $tmpl;
    }
    catch (Exception $e)
    {
        return $e->getMessage();
    }

    $data['tmpl']      = $tmpl;
    $data['forms']     = $forms;
    $data['control']   = $control;
    $data['fieldname'] = $this->fieldname;

    static $unique_subform_id = 0;
    $data['unique_subform_id'] = ('sr-' . ($unique_subform_id++));

    // Prepare renderer
    $layout = new JLayoutFile('families', JPATH_ROOT.'/ANY_PATH_TO/layouts');
    $html = $layout->render($data);

    // Render
    return $html;
}

While the original method pointed to an external renderer and a bunch of external layouts, something generic for a more broad use, I've added

 // Prepare renderer
 $layout = new JLayoutFile('families', JPATH_ROOT.'/ANY_PATH_TO/layouts');
 $html = $layout->render($data);

Now this custom field is unique and only uses its own layout for its purpose.

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.