-
-
Notifications
You must be signed in to change notification settings - Fork 824
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
dev/core#2747 - Reconcile fields. Consolidate/simplify loading of APIv4-style tokens. #21079
Conversation
This adds the last fields that were in legacy tokens but not scheduled reminders and now the same code is deriving the list for each. I wound up UI testing rather than unit testing that custom fields are still advertised without this line as the test uses rollback & adding custom fields would break that.
…cale(pickLocale(...))
…le']` Before: Runs `setLocale()` and then executes the entire pipeline for `TokenProcessor` After: Leaves the global locale alone. Instead, rely on `TokenProcessor` to switch locale as it visits each recipient.
This block purports to catch any exceptions of type `TokenException`. However, if you grep the source tree, you will find that it is never thrown.
Overview: `ActionSchedule::sendMailings()` fetches a batch of pending reminders (per some specific schedule/rule). This improves support for prefetching related data. Before: For each item in the batch, it makes a new `TokenProcessor`. This means that the `TokenProcessor` only sees one pending reminder -- and it cannot meaningfully fetch batched data. After: It creates one `TokenProcessor` and adds rows for each pending reminder. This means that `TokenProcessor` can fetch batched data.
This will help us to consolidate the prefetching logic in `CRM_*_Tokens`. Currently, `CRM_*_Token::alterActionScheduleQuery()` updates the query to select all fields for the entity. This is regardless of how many fields there are, whether they are needed, etc. This is also prone to naming conflicts. With this patch, `CRM_*_Token::alterActionScheduleQuery()` only needs to select one field (`tokenContext_fooId`). This will then propagate to the `TokenProcessor` which can do its own optimized data-loading.
* All data-loading uses `prefetch()` and an ID field (eg `contributionId`). * All special APIv4 values (pseudoconstants/joins/etc) are loaded by `civicrm_api4()`. No need to duplicate this stuff. * When using `TokenProcessor`, columns will only be prefetched if they are actually needed. * `alterActionScheduleQuery()` no longer prefetches data. Instead, it populates `tokenContext_contributionId` which feeds into `prefetch()`. * Properly declare `abstract` methods. * Subclasses (i.e. `CRM_Contribute_Tokens`) should primarily use these methods: - `getApiTokens()` - List of visible/exported fields. - `getAliasTokens()` - List of funny aliases for API fields. This adds support for the legacy token `{contribution.campaign}`, and it also * The special `currency` rules cannot work in other entities. Move them to `CRM_Contribution_Tokens` specifically.
(Standard links)
|
*/ | ||
protected function getExposedFields(): array { | ||
return [ | ||
protected function getApiTokens(): array { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So once we resolve https://lab.civicrm.org/dev/core/-/issues/2745 my expectation is we would entirely remove this function in favour of a metadata-driven function on the parent class - because there is nothing (yet) in this token class that should not be purely metadata driven
At some point in the future we probably will need to add some calculated fields (paid_amount, balance_amount) but that is out of scope
$return = []; | ||
foreach ($this->getExposedFields() as $fieldName) { | ||
$return[$fieldName] = $this->getFieldMetadata()[$fieldName]['title']; | ||
public function getAliasTokens(): array { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at some point we will need a getDeprecatedTokens
function - but we don't want to add already-deprecated tokens - we should agree what the syntax is we are adding and standardise on it
ie I think we have 'advertised tokens' and 'deprecatedTokens' but we shouldn't need 'aliasTokens' - that's apiv3 all over again.
* @return string | ||
*/ | ||
protected function getEntityAlias(): string { | ||
return 'contrib_'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - if changing this is definitely ok for scheduled reminders
* | ||
* - (MUST) Implement getEntityName() and getApiEntityName() | ||
* - (MUST) Implement getApiTokens() | ||
* - (MAY) Implement getAliasTokens() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should offer 'getDeprecatedTokensnot
getAliasTokens` - we are going to need a way to deprecate tokens but we only want to have one 'right' token for each thing.
} | ||
if ($this->isMoneyField($field)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is generic - lots of classes will need this - so moving it to contribution is the wrong way - we might need a 'getCurrency' function for when we hit a class that doesn't have it's own currency field
@totten per chat - I don't think there is anything in the contribution tokens class that isn't generic / shouldn't be metadata driven. My expectation was that once 2747 was resolved the final class would look like
as this token class has nothing in it that wouldn't be wanted by other token classes with the same fields - ie every entity has money fields, every entity has campaigns and fields with psuedoconstants & they should all be able to be driven off the metadata. In terms of transitioning - I do think that we will need a |
fee_amount = {contribution.fee_amount} | ||
legacy campaign = {contribution.campaign} | ||
campaign_id = {contribution.campaign_id} | ||
campaign name = {contribution.campaign_id:name} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - so it looks like you added the pseudoconstant form - I think we should either make this work in apiv4 or use the dot notation per https://github.com/civicrm/civicrm-core/pull/21076/files#diff-196c289971f24f7589d89f613b6199310f614fbe5e2aac9368b1563bf64d2f96R291
@@ -139,6 +139,33 @@ public function testRowTokens() { | |||
} | |||
} | |||
|
|||
public function testRenderLocalizedSmarty() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
} | ||
if ($pseudoKey === 'label') { | ||
$fieldValue = (string) CRM_Core_PseudoConstant::getLabel($this->getBAOName(), $realField, $fieldValue); | ||
public function getPseudoValues(int $id): array { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this is only called from a different class now - so it should be moved to that class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note there is no reason to deprecate - it's only called from recent tested core code
@@ -267,38 +267,44 @@ public static function sendMailings($mappingID, $now) { | |||
); | |||
|
|||
$multilingual = CRM_Core_I18n::isMultilingual(); | |||
$tokenProcessor = self::createTokenProcessor($actionSchedule, $mapping); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these changes all work - I'm all for them :-)
*/ | ||
public static function setCommunicationLanguage($communication_language, $preferred_language) { | ||
public static function pickLocale($communication_language, $preferred_language) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool
* To write a subclass: | ||
* | ||
* - (MUST) Implement getEntityName() and getApiEntityName() | ||
* - (MUST) Implement getApiTokens() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be 'May' implement getApiTokens. - We don't need to implement this if we just use the metadata
* | ||
* To write a subclass: | ||
* | ||
* - (MUST) Implement getEntityName() and getApiEntityName() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only one of these should be must imho
@@ -25,31 +32,29 @@ | |||
* AbstractTokenSubscriber in future. It is being used to clarify | |||
* functionality but should NOT be used from outside of core tested code. | |||
*/ | |||
class CRM_Core_EntityTokens extends AbstractTokenSubscriber { | |||
abstract class CRM_Core_EntityTokens extends AbstractTokenSubscriber { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure- there's still an open question about merging this with AbstractTokenSubscriber or not extending it - but I'm agnostic
} | ||
|
||
/** | ||
* Get pseudoTokens - it tokens that reflect the name or label of a pseudoconstant. | ||
* | ||
* @internal - this function will likely be made protected soon. | ||
* | ||
* @internal - this function is a bridge for legacy CRM_Utils_Token callers. It should be removed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to deprecate this - we can move or remove it - none of the functions on this class are set in stone - they are only used in recent tested core places
Having said that - the point of this was to take the metadata for the entity and add the pseudotokens where the metadata required it - once we remove the hard coded list of tokens in favour of metadata we will need 'something' to add in these tokens
$return[$fieldName . ':name'] = ts('Machine name') . ': ' . $this->fieldMetadata[$fieldName]['input_attrs']['label']; | ||
$labels = $this->getAllTokens(); | ||
// Simpler, but doesn't currently pass: $labels = $this->tokenNames; | ||
$r = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is$r
what $return
looks like in your bikeshed?
public function getAliasTokens(): array { | ||
$aliases = []; | ||
if (CRM_Campaign_BAO_Campaign::isCampaignEnable()) { | ||
// Unit-tests are written to use these funny tokens - but they're not valid in APIv4. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the unit tests from one of the 2 PRs used them..... the other one used campaign_id.name
- we just need to agree which syntax it is
@totten Needs rebase |
Yeah - probably not worth rebasing this until we've resolved the campaign handling / https://lab.civicrm.org/dev/core/-/issues/2745 |
I'm closing this out for now as it's less of a 'needs rebase' and more of a 'the couple of bits that are still relevant need to be picked out & put in a PR with this test #21134 ' I'm leaving that step (setting up evaluate token) to you @totten since what I did was just 'something I copied from elsewhere that was just enough to get the tests to pass' |
Overview
Alternative to #21076, #21046, #21058
A brief description of the pull request. Keep technical jargon to a minimum. Hyperlink relevant discussions.
Before
What is the old user-interface or technical-contract (as appropriate)?
For optimal clarity, include a concrete example such as a screenshot, GIF (LICEcap, SilentCast), or code-snippet.
After
What changed? What is new old user-interface or technical-contract?
For optimal clarity, include a concrete example such as a screenshot, GIF (LICEcap, SilentCast), or code-snippet.
Technical Details
If the PR involves technical details/changes/considerations which would not be manifest to a casual developer skimming the above sections, please describe the details here.
Comments
Anything else you would like the reviewer to note