fix(4e-review M1): Update unterscheidet Composer und Zweitdurchlauf nach der ganzen Schleifen-Kette (LoopScope::key)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
b91c356a28
commit
8be2f5e558
@@ -94,6 +94,18 @@ final class LoopScope
|
|||||||
return $this->queryType === null ? [] : [$this->kind(), ...$this->outerKinds];
|
return $this->queryType === null ? [] : [$this->kind(), ...$this->outerKinds];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identity of the context: the loop and the kinds of its whole chain (audit M1). Two scopes with the same key
|
||||||
|
* resolve every loop_* reference the same way; no loop is "-|".
|
||||||
|
*/
|
||||||
|
public function key(): string
|
||||||
|
{
|
||||||
|
return ($this->queryType ?? '-') . '|' . implode(',', array_map(
|
||||||
|
static fn (?string $kind): string => $kind ?? '?',
|
||||||
|
$this->kinds()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/** Whether there is a loop whose loop_* options the tools allow. */
|
/** Whether there is a loop whose loop_* options the tools allow. */
|
||||||
public function active(): bool
|
public function active(): bool
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ final class DiviUpdateAbility extends DiviWriteAbility
|
|||||||
*/
|
*/
|
||||||
private static function composeInLoops(NodeIndex $index, DiviComposition $composition, array $pending): array
|
private static function composeInLoops(NodeIndex $index, DiviComposition $composition, array $pending): array
|
||||||
{
|
{
|
||||||
/** @var array<string, Composer> $composers query type ('' = no loop) => Composer of that scope */
|
/** @var array<string, Composer> $composers LoopScope::key() => Composer of that scope */
|
||||||
$composers = [];
|
$composers = [];
|
||||||
$scopes = [];
|
$scopes = [];
|
||||||
$results = [];
|
$results = [];
|
||||||
@@ -247,7 +247,7 @@ final class DiviUpdateAbility extends DiviWriteAbility
|
|||||||
}
|
}
|
||||||
foreach ($pending as $position => [$block, $change, $field]) {
|
foreach ($pending as $position => [$block, $change, $field]) {
|
||||||
$scope = self::loopAt($index, $block, $newAttrs);
|
$scope = self::loopAt($index, $block, $newAttrs);
|
||||||
if ($scope->queryType() !== $scopes[$position]->queryType()) {
|
if ($scope->key() !== $scopes[$position]->key()) {
|
||||||
$results[$position] = self::composerFor($composition, $scope, $composers)
|
$results[$position] = self::composerFor($composition, $scope, $composers)
|
||||||
->update($block, $change, $field);
|
->update($block, $change, $field);
|
||||||
}
|
}
|
||||||
@@ -256,9 +256,9 @@ final class DiviUpdateAbility extends DiviWriteAbility
|
|||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param array<string, Composer> $composers One Composer per loop scope (S31 as reworded by X3). */
|
/** @param array<string, Composer> $composers One Composer per loop context, chain included (S31 as reworded by X3). */
|
||||||
private static function composerFor(DiviComposition $composition, LoopScope $scope, array &$composers): Composer
|
private static function composerFor(DiviComposition $composition, LoopScope $scope, array &$composers): Composer
|
||||||
{
|
{
|
||||||
return $composers[$scope->queryType() ?? ''] ??= $composition->inLoop($scope)->composer();
|
return $composers[$scope->key()] ??= $composition->inLoop($scope)->composer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ final class DiviLoopWriteTest extends DiviWriteAbilityTestCase
|
|||||||
'date_format' => $field('select'), 'custom_date_format' => $field('text'),
|
'date_format' => $field('select'), 'custom_date_format' => $field('text'),
|
||||||
]),
|
]),
|
||||||
'loop_term_name' => $option('loop_term_name', 'Loop Terms', $common),
|
'loop_term_name' => $option('loop_term_name', 'Loop Terms', $common),
|
||||||
|
'loop_post_meta_key_manual_custom_field' => $option('loop_post_meta_key_manual_custom_field', 'Loop', $common + [
|
||||||
|
'select_loop_meta_key' => $field('select'), 'loop_meta_key' => $field('text'),
|
||||||
|
]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,4 +533,55 @@ final class DiviLoopWriteTest extends DiviWriteAbilityTestCase
|
|||||||
self::assertTrue($result['ok'], (string) json_encode($result));
|
self::assertTrue($result['ok'], (string) json_encode($result));
|
||||||
self::assertStringContainsString('loop_post_meta_key_manual_custom_field', implode(' | ', $result['warnings']));
|
self::assertStringContainsString('loop_post_meta_key_manual_custom_field', implode(' | ', $result['warnings']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Two sections: a post loop on its own, and a post loop inside a term loop (s1). */
|
||||||
|
private function useTwoSectionPage(): void
|
||||||
|
{
|
||||||
|
$inner = static fn (): string => self::block('text', self::loop(self::POST_LOOP));
|
||||||
|
$this->usePage('<!-- wp:divi/placeholder -->'
|
||||||
|
. self::block('section', [], self::block('row', [], self::block('column', [], $inner())))
|
||||||
|
. self::block('section', self::loop(self::TERM_LOOP), self::block('row', [], self::block('column', [], $inner())))
|
||||||
|
. '<!-- /wp:divi/placeholder -->');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function metaChange(string $id): array
|
||||||
|
{
|
||||||
|
$ref = VariableRef::serialize(
|
||||||
|
'content',
|
||||||
|
'loop_post_meta_key_manual_custom_field',
|
||||||
|
['before' => '', 'after' => '', 'loop_meta_key' => 'telefon']
|
||||||
|
);
|
||||||
|
|
||||||
|
return ['id' => $id, 'attrs' => ['content.innerContent' => ['desktop' => $ref]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Re-review (M1): the composers of one update call are told apart by the whole chain, not the inner kind. */
|
||||||
|
public function testMetaKeyContentIsCheckedAgainstTheWholeChainInOneCall(): void
|
||||||
|
{
|
||||||
|
$this->useTwoSectionPage();
|
||||||
|
$inTerm = $this->changeAll([self::metaChange('s1.r0.c0.m0')]);
|
||||||
|
self::assertStringContainsString('Typ: Beiträge', self::problems($inTerm), 'refused when sent alone');
|
||||||
|
|
||||||
|
$both = $this->changeAll([self::metaChange('s0.r0.c0.m0'), self::metaChange('s1.r0.c0.m0')]);
|
||||||
|
self::assertStringContainsString('changes[1]', implode(' ', array_keys(self::errorFields($both))));
|
||||||
|
self::assertStringContainsString('passt nicht zur Schleife', self::problems($both));
|
||||||
|
self::assertSame([], $this->saved);
|
||||||
|
|
||||||
|
$plain = $this->changeAll([self::metaChange('s0.r0.c0.m0')]);
|
||||||
|
self::assertTrue($plain['ok'], (string) json_encode($plain));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Re-review (M1): an outer loop switched to another kind in the same call refuses meta key content below. */
|
||||||
|
public function testSwitchingTheOuterLoopRefusesMetaKeyContentOfTheInnerLoop(): void
|
||||||
|
{
|
||||||
|
$this->useTwoSectionPage();
|
||||||
|
|
||||||
|
$refused = $this->changeAll([
|
||||||
|
['id' => 's0', 'attrs' => ['module.advanced.loop' => ['desktop' => self::TERM_LOOP]]],
|
||||||
|
self::metaChange('s0.r0.c0.m0'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertStringContainsString('changes[1]', implode(' ', array_keys(self::errorFields($refused))));
|
||||||
|
self::assertSame([], $this->saved);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user