$payload One entry, as it travels to the browser. * @param string|null $url `wc_get_cart_url()`, or null where there is no store. * @return array */ public static function into(array $payload, ?string $url): array { if ($url === null || $url === '') { return $payload; } return TemplateTree::rewrittenIn($payload, static fn (array $node): array => self::resolve($node, $url)); } /** * One node, with the cart's URL filled in where the CTA asked for it. * * The walk is {@see TemplateTree::rewrittenIn()}'s — shared with * {@see PolicyLink}, which is the only other thing that resolves a * site-local destination into a tree at render time. Two resolvers, one * walk, and each holds only the sentence it can argue for. * * @param array $node * @return array */ private static function resolve(array $node, string $url): array { // The converting act, in its click spelling. `action: link` is what // the renderer draws as an anchor and what {@see ConvertingAct} reads // to decide the Template's metric, so asking the same key here means // the three cannot disagree about which node converts. // // An href already there is the merchant's, typed into the settings // panel, and it wins. **Emptiness is judged the way a form field is** // — an absent key and an empty string both mean "nothing was chosen", // which is the reading {@see \WConvert\Rules\RuleVocabulary::hasTrigger()} // already takes one axis over. `panel.ts`'s `withValue()` deletes the // key when a control is cleared, so the two agree; an entry // hand-written or imported with `href: ""` is the case that would // otherwise leave a merchant who cleared the field with a dead CTA and // nothing to read. $href = $node['href'] ?? null; if (($node['type'] ?? null) === 'button' && ($node['action'] ?? null) === 'link' && (!is_string($href) || $href === '') ) { $node['href'] = $url; } return $node; } }