Proposal - create fields array standard & generic field template/s for it & for fields, work towards generic entity form template
We have a generalised issue where we want people to write extensions to alter CiviCRM but in many cases the way CiviCRM is structured makes that difficult and unmaintainable. As part of pushing people out to extensions we also need to work to improve core code to support that purpose.
In the 5.x series we have made it easier for extension writers to store entity specific data by extending custom fields to (almost) all entities via the api*. However, it is still very difficult for extension writers to inject these fields, or other fields, into forms or to remove or re-order them. In general our approach has been to make things more metadata driven but we have not really standardised how to do this for templates. Conversely we already have examples in our code of templates that have been re-written to support output being modified by a php hook - (notably it is possible to alter the Billing fields on the payment form & the output columns in the contribution search from a php hook because metadata is assigned to the template & processed in the template) but we have not standardised those approaches into a recommendation.
To be clear - this proposal is NOT about going through all our forms and altering them to be more editable - it's about agreeing what we are working towards when we ARE editing forms. Currently it is possible to say
- "we are working towards replacing jcalendar.tpl with datepicker fields and when we touch date fields we should attempt to convert them"
- "we are working towards replacing non-metadata ways of adding fields to a form with addField and when we touch fields not added that way we should attempt to convert them"
Conversely we would say "if you are trying to fix the way a datefield is handling date defaults and the field is a jcalendar field we expect the fix to be converting it to a datepicker field rather than adding extra mangling for the legacy method".
In this case we would say
- "we are working towards assigning a standardised array of fields to a form & a tpl way of handling them and when we are working on a form we should seek to convert them"
(Generally we do require that PRs are improving the consistency & quality of our codebase & compliance with code-directions when we merge them)
I would note that complex forms like Contribution & Event forms might make poor candidates for tpl standardisation but a lot of our forms are simple settings forms or editing basic entities and these are good candidates for being generic & standardised. (This is allied with fixing the forms such that IF custom data is enabled on the entity THEN it can be altered via the form - which I will cover separately)
There are 2 open PRs which revolve around making tpls more generic, more metadata driven & more form-alterable
https://github.com/civicrm/civicrm-core/pull/12128/files#diff-1f76de158e02dfb7afa76303ef60e9ebR27 and https://github.com/civicrm/civicrm-core/pull/12078
The proposal would be that where it is possible to iterate through fields we assign them to the tpl in an array that looks like
$this-assign('entityFields', [
'field_1' => [
'name' => 'field_1',
'description' => ts("description to show below field"),
'help' => ['id' => 'id-field_1, 'file' => 'CRM/Contact/Form/Contact',
'is_add_translate_dialog' => 1,
],
'field_1' => ['name' => 'field_1'],
'money_field' => ['name' => 'money', 'formatter' => 'crmMoney'],
'weird_looking_field' => [
'name' => 'weird_looking_field',
'template' => CRM/Member/Form/MembershipType/weird_looking_field.tpl',
In practice some helpers would be involved in building the array so that fields like is_add_translate_dialog are derived from existing metadata. That would also potentially apply to keys like description, help & formatter. It is expected that if a field were to use a field specific template it would be under the path of the form.
The form template would then work towards looking more like
{foreach from=$entityFields item=fieldSpec}
{assign var=fieldName value=$fieldSpec.name}
<tr class="crm-{$entityInClassFormat}-form-block-{$fieldName}">
{include file="CRM/Core/Form/Field.tpl"}
</tr>
{/foreach}
With the tpl being
{if $fieldSpec.template}
{include file=$fieldSpec.template}
{else}
<td class="label">{$form.$fieldName.label}
{if $fieldSpec.help}{assign var=help value=$fieldSpec.help}{capture assign=helpFile}{if $fieldSpec.help}
{$fieldSpec.help}
{else}''{/if}
{/capture}{help id=$help.id file=$help.file}{/if}
{if $action == 2 && $fieldSpec.is_add_translate_dialog}{include file='CRM/Core/I18n/Dialog.tpl' table=$entityTable field=$fieldName id=$entityID}{/if}
</td>
<td>{if $form.$fieldName.html}{if $fieldSpec.formatter === 'crmMoney'}{$form.$fieldName.html|crmMoney}{else}{$form.$fieldName.html}{/if}{else}{$fieldSpec.place_holder}{/if}<br />
{if $fieldSpec.description}<span class="description">{$fieldSpec.description}</span>{/if}
</td>
{/if}
In some cases it might be too hard to make the form iterate through an array but individual fields could be converted - either in order to make that field hook alterable - (ie. the hook could then change the description metadata, or suppress a field) or as part of chipping away at the conversion
{assign var=fieldName value='my_converted_field'}
<tr class="crm-{$entityInClassFormat}-form-block-{$fieldName}">
{include file="CRM/Core/Form/Field.tpl"}
</tr>
- Note to enable custom data for a new type in an extension use code like the following
$optionValues = civicrm_api3('OptionValue', 'get', [
'option_group_id' => 'cg_extend_objects',
'name' => 'civicrm_relationship_type'
]);
if (!$optionValues['count']) {
civicrm_api3('OptionValue', 'create', [
'option_group_id' => 'cg_extend_objects',
'name' => 'civicrm_relationship_type',
'label' => ts('Relationship Type'),
'value' => 'RelationshipType',
]);
}