Search code examples
phpcakephpwidgetcakephp-3.x

Where can I store, and how can i load Widget string templates in CakePHP 3.9?


I want to create a CakePHP Widget in order to create a custom form control. The end goal is to make it a plugin, but for now I am trying to determine the general structure of a Widget. I have created a file in src/View/Widget/DateTimeWidget.php containing

<?php
namespace App\View\Widget;

use Cake\View\Form\ContextInterface;
use Cake\View\Widget\WidgetInterface;

class DateTimeWidget implements WidgetInterface
{

    protected $_templates;

    public function __construct($templates)
    {
        $this->_templates = $templates;
    }

    public function render(array $data, ContextInterface $context)
    {
        $data += [
            'name' => '',
        ];
        return $this->_templates->format('DateTime', [
            'name' => $data['name'],
            'attrs' => $this->_templates->formatAttributes($data, ['name'])
        ]);
    }

    public function secureFields(array $data)
    {
        return [$data['name']];
    }
}
?>

I load the Widget in a View with the code

$this->Form->addWidget(
    'datetime',
    ['DateTime']
);

and then create a form control with it using

echo $this->Form->control('end_time', ['type' => 'datetime']);

However, I get the error Cannot find template named 'DateTime'.

I have created the basic template code

<?php
$this->Form->setTemplates([
    'DateTime' => '<p {{attrs}}>Test template</p>'
]);

But I have no idea where in the folder structure to put it? In most plugins I have looked at it is in a helper file, but I wonder if this is the default way to do it? What are my options? And how do i tell CakePHP to load it? What is the preferred way of doing this?

Thank you!


Solution

  • If you want your widget to come with default string templates, then you could for example define them in the widget itself, by adding it to the string template instance that is being passed to the widget's constructor. You'd do it in the widget's render() method though, it wouldn't work properly in the constructor, as widget instances are being reused, ie they are only being constructed once, for example:

    public function render(array $data, ContextInterface $context)
    {
        if (!array_key_exists('customDateTime', $this->_templates->getConfig())) {
            $this->_templates->add([
                'customDateTime' => '<p {{attrs}}>Test template</p>',
                // ...
            ]);
        }
    
         // ...
    }
    

    Another option is to put the string templates in a config file:

    // in path_to_your_plugin/config/form_helper_templates.php
    <?php
    return [
        'customDateTime' => '<p {{attrs}}>Test template</p>',
        // ...
    ];
    

    and ask the users to load the form helper string templates in their view templates when they want to use your widgets:

    $this->Form->templater()->load('YourPluginName.form_helper_templates');
    

    Both options will integrate properly with the form helper, so that users can still override the templates by setting custom templates either via FormHelper::setTemplates(), StringTemplate::load()/add(), or the templates option for FormHelper::control().