feat: VariableDiff beschreibt Variablenänderungen (angelegt, geändert, archiviert, aktiviert) samt Zusammenfassung

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 14:01:02 +02:00
co-authored by Claude Sonnet 5.5
parent a7a385d3b3
commit 68248da648
2 changed files with 188 additions and 0 deletions
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Design;
defined('ABSPATH') || exit;
/**
* What a variable change does (spec §4 step 4, "diff"): per variable added, changed (with the changed fields label,
* value, order, status), archived or activated (status switch; "fields" then lists label/value changes only – archiving
* clears the order on its own). lastUpdated is no change of its own. Variables are never removed (ruling V1), so a
* variable missing in $after produces no row. Sorted by type, then ID. Pure PHP.
*/
final class VariableDiff
{
private const COMPARED = ['label', 'value', 'order', 'status'];
/**
* @return list<array{type: string, id: string, name: string, action: string, fields?: list<string>}>
* action: added|changed|archived|activated
*/
public static function between(VariableBook $before, VariableBook $after): array
{
$new = $after->data();
ksort($new);
$rows = [];
foreach ($new as $type => $entries) {
ksort($entries);
foreach ($entries as $id => $is) {
$id = (string) $id;
$was = $before->entry($type, $id);
$row = ['type' => $type, 'id' => $id, 'name' => $is['label'] !== '' ? $is['label'] : $id];
if ($was === null) {
$rows[] = $row + ['action' => 'added'];
continue;
}
$action = self::action((string) $was['status'], (string) $is['status']);
// A status switch is the action itself; archiving clears the order, which is no change of its own.
$compared = $action === 'changed' ? self::COMPARED : array_slice(self::COMPARED, 0, 2);
$fields = array_values(array_filter(
$compared,
static fn (string $key): bool => $was[$key] !== $is[$key]
));
if ($fields !== [] || $action !== 'changed') {
$rows[] = $row + ['action' => $action] + ($fields === [] ? [] : ['fields' => $fields]);
}
}
}
return $rows;
}
/**
* One German sentence for the dry run and the change log (spec §4 step 6).
*
* @param list<array<string, mixed>> $diff
*/
public static function summary(array $diff): string
{
$count = array_count_values(array_map(static fn (array $row): string => (string) $row['action'], $diff));
$words = [
'added' => 'angelegt', 'changed' => 'geändert', 'archived' => 'archiviert', 'activated' => 'aktiviert',
];
$parts = [];
foreach ($words as $action => $word) {
if (isset($count[$action])) {
$parts[] = $count[$action] . ' ' . $word;
}
}
return 'Variablen: ' . ($parts === [] ? 'keine Änderung' : implode(', ', $parts)) . '.';
}
private static function action(string $was, string $is): string
{
return match (true) {
$was === $is => 'changed',
$is === 'archived' => 'archived',
$was === 'archived' && $is === 'active' => 'activated',
default => 'changed',
};
}
}