Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pseudoconstants - Fix and improve handling of option callbacks #22730

Merged
merged 3 commits into from
Feb 15, 2022

Conversation

colemanw
Copy link
Member

@colemanw colemanw commented Feb 8, 2022

Overview

Ensures field option lists returned by APIv4 always have a stable id and name and that option matching works in non-English setups.

Before

Pseudoconstant callback functions must always return a flat [key => label] array.
When the API (v3 and v4) matches option names to ids, name is derived from label when converting simple associative to an option list. This would break non-English environments, but our test coverage of those is poor.

After

Pseudoconstant callback functions may return either a flat array or a multidimensional array containing id, name, label, or abbr. This allows more precision when matching.
When a callback returns only a flat array, name is derived from the key instead of the label.

Technical Details

Each item in a field option list contains keys like id, name, label, color, icon, description, abbr.
But some option lists only consist of simple key/value pairs (e.g. everything in CRM_Core_SelectValues). To convert those simple associative arrays into a full option list, the name should be derived from the id, not the label, because machine names are expected to be stable, and labels can be translated.

Switching the way options are matched by the API is is technically a breaking change, and it caused several test regressions in the first version of this PR.

Comments

Switching the way options are matched by the API is is technically a breaking change, and it caused several test regressions in the first version of this PR. The solution is to update callback functions to return the new multidimensional format, as this allows the name to remain the same as before, without passing it through ts() and breaking non-English envs.

@civibot civibot bot added the master label Feb 8, 2022
@civibot
Copy link

civibot bot commented Feb 8, 2022

(Standard links)

Each item in a field option list contains keys like id, name, label, color, icon, description, abbr.
But some option lists only consist of simple key/value pairs. To convert those simple associative arrays
into a full option list, the name should be derived from the id, not the label, because machine names
are expected to be stable, and labels can be translated.

Before: `name` derived from `label` when converting simple associative to an option list
After: `name` derived from `id`.
…option callbacks

Before: Only flat arrays could be returned by a pseudoconstant callback fn.
After: Callbacks can return arrays with id/name/label/abbr.
Previously some API calls relied on a bug which conflated name with label,
that bug has been fixed, causing some test failures. The solution is to
update the option lists with a full multidimensional array with translated labels
and untranslated names.
@colemanw
Copy link
Member Author

colemanw commented Feb 9, 2022

retest this please

@colemanw colemanw changed the title Pseudoconstants - Use id instead of label for name Pseudoconstants - Fix and improve handling of option callbacks Feb 9, 2022
colemanw added a commit to colemanw/civicrm-core that referenced this pull request Feb 10, 2022
Following on civicrm#22730, this removes the 'severity' field from the System::check api
because it's redundant with 'severity_id:name', and does not appear to be used anywhere.
@totten
Copy link
Member

totten commented Feb 15, 2022

We talked about this online. It's a good idea. 👍

For r-run, I grepped xml/schema and pulled out a list of callbacks - then executed all of them and skimmed the result. I confirmed that there was a mix of old/short (key=>value) style and new//long style ([id=>...,name=>...,label=>...]).

This gave a list where I could pick a few differing examples to spot-check. Spot-checked each in the APIv4 Explorer and using some Quickform admin screens that referenced these fields. All of the spot-checks appeared to work in the before+after. 👍

With respect to r-tech compatibility, the main Q for me was how it distinguishes callbacks that return old-style and new-style. The key bit seems to be this:

  private static function formatArrayOptions($context, array &$options) {
    // Already flat; return keys/values according to context
    if (!isset($options[0]) || !is_array($options[0])) { .../* old format: key=>value pairs */ }
    /* else: new format: list of records, with numerical values */

I think this makes sense. It should correctly classify all three of these cases:

  • Old style, string indexes ([r=>Red, g=>Green, b=>Blue])
  • Old style, numerical indexes ([0=>Red, 1=>Green, 2=>Blue])
  • New style, numerical indexes with array data ([0=>[name=>r,label=>Red], 1=>[name=>b,label=>Blue]])

Also, I think there's a lot of implicit test-coverage for these code-paths.

Looks good. Mrrrging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants