josbeir / cakephp-templater-defaults
TemplaterDefaults plugin for CakePHP
Installs: 5
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:cakephp-plugin
Requires
- php: >=8.1
- cakephp/cakephp: ^5.0
Requires (Dev)
- cakephp/cakephp-codesniffer: ^5.2
- cakephp/debug_kit: ^5.1
- cakephp/plugin-installer: ^2.0.1
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.1.3 || ^12.0
- rector/rector: ^2.1
README
A custom templater for CakePHP that enables you to define default HTML attributes within CakePHP's string template system, making it easier to integrate with your preferred CSS setups and streamline html markup generated by helpers.
Table of Contents
Features
- 🎯 Default attributes - Define default HTML attributes and values directly in string templates
- 🔄 Smart merging - Seamlessly merge default and runtime attributes (perfect for CSS classes)
- 🎨 CSS framework integration - Improves integration with frameworks like Tailwind CSS, Bootstrap, and others
- ❌ Flexible overrides - Remove or bypass default values when needed
Motivation
CakePHP's default string template system does not provide a straightforward way to define a default set of classes or attributes that align with your CSS framework. Instead of extending helpers or manually adding attributes throughout your application, this plugin offers a clean and maintainable alternative: set default attribute values directly in your template definitions. This approach works with any CakePHP helper that utilizes string templates.
The current way would be defining attributes on your template but when you want to add extra classes to them, things stop working as those attributes are then mostly added to the {{attr}} token.
// Imagine this template ['input' => '<input type="{{type}}" name="{{name}}" class="defaultClass" {{attrs}}>'] // Render with FormHelper $this->Form->input('field', ['class' => 'customClass']);
Renders to
<input type="text" name="field" class="defaultClass" class="customClass" />
As you can see in the example above, the class
is added twice. This is of course not the inteded result.
Requirements
- CakePHP 5.0 or higher
- PHP 8.2 or higher
Installation
Install this plugin into your CakePHP application using Composer:
composer require josbeir/cakephp-templater-defaults
Usage
Configure the Helper
In your AppView.php
, specify the custom template class for any helper you want to use with default attributes:
// In src/View/AppView.php use TemplaterDefaults\View\StringTemplate; $this->loadHelper('Form', [ 'templateClass' => StringTemplate::class, 'templates' => 'my_app_templates', // Your template defintion ]);
Define Templates with defaults
Add default attributes to your templates. For example, to set a default class and a custom data attribute for all input fields:
'templates' => [ // Default behavior. 'inputContainer' => '<div class="{{containerClass}} {{type}}{{required}}">{{content}}</div>', // With default attributes. 'input' => [ 'template' => '<input type="{{type}}" name="{{name}}"{{attrs}}>', 'defaults' => [ 'class' => 'defaultClass', 'data-star' => 'wars', ], ], ],
Now, every input field will automatically include the default class and data attribute:
<?= $this->Form->control('field'); ?>
Renders to
<input type="text" name="field" class="defaultClass" data-star="wars" />
Merging default and runtime values
If you want to merge default values with those provided at runtime (e.g., for classes), use an array for the default value:
'templates' => [ 'input' => [ 'template' => '<input type="{{type}}" name="{{name}}"{{attrs}}>', 'defaults' => [ 'class' => ['firstClass', 'secondClass'], ], ], ],
When you specify a class at runtime, it will be merged:
<?= $this->Form->control('field', ['class' => 'thirdClass']); ?>
Renders to
<input type="text" name="field" class="firstClass secondClass thirdClass" />
Removing default values at runtime
There may be situations where you want to remove the default values. In that case, you can pass false
as the attribute value, and the attribute will then be removed from the element.
Remove entire attribute:
<?= $this->Form->control('field', ['class' => 'false']); ?> // <input type="text" name="field" />
You can also bypass default values and only add runtime values:
<?= $this->Form->control('field', ['class' => '!!there-can-be-only-one']); ?> // <input type="text" name="field" class="there-can-be-only-one" />
Example: Multiple Attributes
You can set defaults for any attribute, including data attributes and ARIA properties:
'templates' => [ 'input' => [ 'template' => '<input type="{{type}}" name="{{name}}"{{attrs}}>', 'defaults' => [ 'class' => ['form-control', 'mb-2'], 'data-toggle' => 'custom', 'aria-label' => 'Custom input', ], ], ],
echo $this->Form->control('username', [ 'class' => 'is-invalid', 'data-toggle' => 'override', ]); ?>
Renders to
<input type="text" name="username" class="form-control mb-2 is-invalid" data-toggle="override" aria-label="Custom input" />
Example: Using a callable
Instead of defining an array with values, you can also use a callable to manipulate the attributes array.
'templates' => [ 'input' => [ 'template' => '<input type="{{type}}" name="{{name}}"{{attrs}}>', 'defaults' => function (array $attributes) { $attributes['custom-attribute'] = 'Hello there'; return $attributes; }, ], ],
Advanced Example: Setting Template Classes at Runtime
Sometimes you need to add attributes to nested template elements, not just the main element. This plugin supports a special syntax to target specific templates within a helper's rendering process.
Example: Adding classes to the input container
Instead of only styling the input field itself:
<?= $this->Form->control('field', ['class' => 'myclass']); ?>
Which renders:
<div class="input text"> <label for="field">Field</label> <input type="text" name="field" class="myclass" id="field" /> </div>
You can target the parent container using the template name prefix (please note the dasherized container name):
<?= $this->Form->control('field', ['input-container:class' => 'custom-container']); ?>
Which renders:
<div class="input text custom-container"> <label for="field">Field</label> <input type="text" name="field" id="field" /> </div>
Multiple template targeting:
You can target multiple templates in a single call:
<?= $this->Form->control('field', [ 'class' => 'field-input', 'inputContainer:class' => 'container-style', 'label:class' => 'label-style' ]); ?>
Which renders:
<div class="input text container-style"> <label for="field" class="label-style">Field</label> <input type="text" name="field" class="field-input" id="field" /> </div>
Syntax:
Use the format template-name:attributeName
where:
template-name
is the dasherized name of the template you want to target (e.g.,inputContainer
,label
,error
)attributeName
is the HTML attribute you want to modify (e.g.,class
,data-toggle
,id
)
This allows you to manipulate the runtime values.
Limitations
- Default attributes obviously only work on html/xml style templates
- Only the top level element will be populated. Nested elements are ignored.
Contributing
Contributions, issues, and feature requests are welcome! Feel free to open a pull request or issue on GitHub. Please follow CakePHP coding standards and ensure all code is properly tested before submitting.