feat: DynamicContentMap::loopFields() – loop_*-Optionen nach Art (X2); echte Loop-Optionen in DiviFixtures

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 20:43:10 +02:00
co-authored by Claude Sonnet 5.5
parent 617ad2888d
commit 3d12043b4f
3 changed files with 186 additions and 15 deletions
+33 -5
View File
@@ -102,6 +102,14 @@ final class DiviFixtures
$option = static fn (string $id, string $group, array $fields): array => [
'id' => $id, 'label' => $id, 'type' => 'text', 'custom' => false, 'group' => $group, 'fields' => $fields,
];
$select = static fn (): array => ['label' => 'x', 'type' => 'select', 'default' => ''];
$loop = ['before' => $text(), 'after' => $text(), 'loop_position' => $text()];
$date = ['date_format' => $select(), 'custom_date_format' => $text()];
$meta = $loop + $date + [
'select_loop_meta_key' => $select(),
'loop_meta_key' => $text(),
'enable_html' => ['label' => 'x', 'type' => 'yes_no_button', 'default' => 'off'],
];
return [
'post_title' => $option('post_title', 'Default', ['before' => $text(), 'after' => $text()]),
@@ -131,12 +139,32 @@ final class DiviFixtures
'meta_key' => $text(),
'enable_html' => ['type' => 'yes_no_button'],
]),
'loop_post_title' => $option('loop_post_title', 'Loop', [
'before' => $text(), 'after' => $text(), 'loop_position' => $text(),
]),
'loop_post_meta_key_manual_custom_field' => $option('loop_post_meta_key_manual_custom_field', 'Loop', [
'loop_meta_key' => $text(),
// Loop options as Divi 5.15.1's get_options() returns them (research 4e §2): id = key = stored name.
'loop_post_title' => $option('loop_post_title', 'Loop', $loop),
'loop_post_date' => $option('loop_post_date', 'Loop', $loop + $date),
'loop_post_featured_image' => $option('loop_post_featured_image', 'Loop', [
'loop_position' => $text(), 'thumbnail_size' => $select(),
]),
'loop_post_meta_key_manual_custom_field' => $option(
'loop_post_meta_key_manual_custom_field',
'Loop Post Custom Fields',
$meta
),
'loop_product_title' => $option('loop_product_title', 'Loop Product', $loop),
'loop_term_name' => $option('loop_term_name', 'Loop Terms', $loop),
'loop_term_meta_key_manual_custom_field' => $option(
'loop_term_meta_key_manual_custom_field',
'Loop Term Custom Fields',
$meta
),
'loop_user_name' => $option('loop_user_name', 'Loop Users', $loop),
'loop_user_meta_key_manual_custom_field' => $option(
'loop_user_meta_key_manual_custom_field',
'Loop User Custom Fields',
$meta
),
'loop_menu_text' => $option('loop_menu_text', 'Loop Menus', $loop),
'loop_acf_team|||name' => $option('loop_acf_team|||name', 'Loop ACF team', $loop + ['acf_type' => $text()]),
// Odd entries a third-party filter could add.
'broken_fields' => $option('broken_fields', 'Default', ['a' => 'not-an-array', 'b' => ['type' => 3]]),
'no_fields' => ['id' => 'no_fields', 'type' => 'text'],
@@ -6,6 +6,7 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
use InvalidArgumentException;
use LIT\MCP\Integrations\Divi\Compose\DynamicContentRules;
use LIT\MCP\Integrations\Divi\Compose\LoopScope;
use LIT\MCP\Integrations\Divi\Compose\Placeholders;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
@@ -210,6 +211,88 @@ final class DynamicContentMapTest extends TestCase
self::assertSame($before, json_encode($after));
}
/** Ruling X2: loop_* options stay, grouped by kind – by group, the three meta options by name. */
public function testLoopFieldsGroupTheLoopOptionsByKind(): void
{
$map = new DynamicContentMap(static fn (int $id): array => DiviFixtures::dynamicContentRegistry());
self::assertSame(
[
'loop_post_title', 'loop_post_date', 'loop_post_featured_image',
'loop_post_meta_key_manual_custom_field', 'loop_product_title',
],
array_keys($map->loopFields(LoopScope::POST))
);
self::assertSame(
['loop_term_name', 'loop_term_meta_key_manual_custom_field'],
array_keys($map->loopFields(LoopScope::TERM))
);
self::assertSame(
['loop_user_name', 'loop_user_meta_key_manual_custom_field'],
array_keys($map->loopFields(LoopScope::USER))
);
self::assertSame(['loop_menu_text'], array_keys($map->loopFields(LoopScope::MENU)));
// ACF repeater rows have no kind (X2/X7e); an unknown kind has no options.
self::assertSame([], $map->loopFields('repeater_team'));
self::assertSame([], $map->loopFields(''));
}
public function testLoopFieldsHaveTheShapeOfTheAllowMap(): void
{
$map = new DynamicContentMap(static fn (int $id): array => DiviFixtures::dynamicContentRegistry());
$post = $map->loopFields(LoopScope::POST);
self::assertSame(
[
'before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text',
'date_format' => 'select', 'custom_date_format' => 'text',
],
$post['loop_post_date']
);
self::assertSame(['loop_position' => 'text', 'thumbnail_size' => 'select'], $post['loop_post_featured_image']);
self::assertSame(
['select', 'text', 'yes_no_button'],
[
$post['loop_post_meta_key_manual_custom_field']['select_loop_meta_key'],
$post['loop_post_meta_key_manual_custom_field']['loop_meta_key'],
$post['loop_post_meta_key_manual_custom_field']['enable_html'],
]
);
}
/** The custom field groups are translated ("Loop %s Custom Fields", esc_html__) – their name decides. */
public function testTheMetaOptionsFollowTheirNameNotTheirGroup(): void
{
$registry = DiviFixtures::dynamicContentRegistry();
$registry['loop_user_meta_key_manual_custom_field']['group'] = 'Schleife: Benutzer-Felder';
$registry['loop_term_meta_key_manual_custom_field']['group'] = 'Loop';
$registry['loop_post_meta_key_manual_custom_field']['group'] = 'Loop Users';
$map = new DynamicContentMap(static fn (int $id): array => $registry);
self::assertArrayHasKey('loop_user_meta_key_manual_custom_field', $map->loopFields(LoopScope::USER));
self::assertArrayHasKey('loop_term_meta_key_manual_custom_field', $map->loopFields(LoopScope::TERM));
self::assertArrayHasKey('loop_post_meta_key_manual_custom_field', $map->loopFields(LoopScope::POST));
self::assertArrayNotHasKey('loop_post_meta_key_manual_custom_field', $map->loopFields(LoopScope::USER));
}
public function testLoopFieldsReadTheRegistryOfThePostAndAreEmptyWithoutIt(): void
{
$seen = [];
$map = new DynamicContentMap(static function (int $postId) use (&$seen): array {
$seen[] = $postId;
return DiviFixtures::dynamicContentRegistry();
});
$map->loopFields(LoopScope::POST, 42);
$map->loopFields(LoopScope::TERM);
self::assertSame([42, 0], $seen);
self::assertSame([], (new DynamicContentMap(static fn (): array => []))->loopFields(LoopScope::POST));
self::assertSame([], (new DynamicContentMap())->loopFields(LoopScope::POST), 'without Divi');
}
private static function assertRejected(callable $call, string $fragment): void
{
try {