Files
Jürgen MummertandClaude Fable 5.1 3aad6aa559 Store the display fields of events as virtual fields
- Move 29 display-only event fields (subtitles, audience, tags, prices,
  price types, organizer) from columns into the jsonData storage; fields
  used for filtering, joins and calculations stay real columns
- Add EventFieldsToVirtualMigration, which copies the values and drops
  the columns on contao:migrate
- Read and write the fields in the import, the preview and the booking
  through EventRowCodec; other bundles' keys in the storage are kept
- Validate prices in a save callback instead of the percent check that
  rejected every price above 100; accept a decimal comma
- Do not report events as changed after a backend save: ignore the
  spelling of HTML entities in teaser and topics and the order of the
  speakers

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-27 20:42:47 +02:00

122 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Mummert\ContaoWkSyncBundle\Tests\Sync;
use Mummert\ContaoWkSyncBundle\Sync\EventFields;
use Mummert\ContaoWkSyncBundle\Sync\EventRowCodec;
use PHPUnit\Framework\TestCase;
class EventRowCodecTest extends TestCase
{
public function testExpandReadsVirtualFieldsFromTheStorage(): void
{
$row = (new EventRowCodec())->expand([
'id' => 7,
'title' => 'Basis-Kurs',
'wkId' => '0001234',
'jsonData' => '{"wkSubTitle":"Modul 1","wkPrice1":"216.00","wkOrganizerCity":"Moritzburg","playerSize":"16:9"}',
]);
self::assertSame('Modul 1', $row['wkSubTitle']);
self::assertSame('216.00', $row['wkPrice1']);
self::assertSame('Moritzburg', $row['wkOrganizerCity']);
self::assertSame('0001234', $row['wkId']);
self::assertSame('16:9', $row['playerSize'], 'Felder anderer Erweiterungen bleiben lesbar');
self::assertArrayNotHasKey('jsonData', $row);
}
public function testExpandFillsDefaultsForMissingFields(): void
{
$codec = new EventRowCodec();
foreach ([null, '', '{}', 'kein JSON', '"text"'] as $storage) {
$row = $codec->expand(['id' => 1, 'jsonData' => $storage]);
self::assertSame('', $row['wkSubTitle']);
self::assertSame('', $row['wkOrganizerEmail']);
self::assertSame('0.00', $row['wkPrice3']);
}
self::assertSame([], array_diff(EventFields::virtual(), array_keys($codec->expand(['id' => 1]))));
}
public function testExpandKeepsAlreadyExpandedValues(): void
{
// Daten aus einem Contao-Modell sind bereits aufbereitet und haben kein JSON-Feld mehr
$row = (new EventRowCodec())->expand(['id' => 1, 'wkSubTitle' => 'Modul 2', 'wkPrice1' => '12.50']);
self::assertSame('Modul 2', $row['wkSubTitle']);
self::assertSame('12.50', $row['wkPrice1']);
}
public function testCombineMovesVirtualFieldsIntoTheStorage(): void
{
$set = (new EventRowCodec())->combine([
'title' => 'Basis-Kurs',
'wkId' => '0001234',
'wkPlacesLeft' => 12,
'wkSubTitle' => 'Modul 1 „Einführung“',
'wkPrice1' => 216.0,
'wkPrice2' => 1250.5,
'wkPriceType1' => 'Kursgebühr',
]);
self::assertSame(['title', 'wkId', 'wkPlacesLeft', 'jsonData'], array_keys($set));
self::assertSame(
['wkSubTitle' => 'Modul 1 „Einführung“', 'wkPrice1' => '216.00', 'wkPrice2' => '1250.50', 'wkPriceType1' => 'Kursgebühr'],
json_decode($set['jsonData'], true, flags: JSON_THROW_ON_ERROR),
);
self::assertStringContainsString('„Einführung“', $set['jsonData'], 'Umlaute und Anführungszeichen stehen lesbar im JSON');
}
public function testCombineKeepsStoredFieldsThatAreNotPassed(): void
{
$set = (new EventRowCodec())->combine(
['wkSubTitle' => 'Neu', 'tstamp' => 5],
'{"wkSubTitle":"Alt","wkAudience":"Lehrkräfte","playerSize":"16:9"}',
);
self::assertSame(
['wkSubTitle' => 'Neu', 'wkAudience' => 'Lehrkräfte', 'playerSize' => '16:9'],
json_decode($set['jsonData'], true, flags: JSON_THROW_ON_ERROR),
);
self::assertSame(5, $set['tstamp']);
}
public function testCombineWithoutVirtualFieldsLeavesTheStorageEmpty(): void
{
self::assertNull((new EventRowCodec())->combine(['title' => 'Ohne'])['jsonData']);
}
public function testRoundTrip(): void
{
$codec = new EventRowCodec();
$data = ['id' => 3, 'wkId' => '0009', 'wkTags' => 'a, b', 'wkPrice6' => '9.90', 'wkOrganizerEmail' => 'info@example.org'];
$row = $codec->expand($codec->combine($data));
foreach ($data as $field => $value) {
self::assertSame($value, $row[$field]);
}
}
public function testColumnsSkipVirtualFields(): void
{
self::assertSame(
['id', 'wkId', 'wkState', 'jsonData'],
(new EventRowCodec())->columns(['id', 'wkId', 'wkSubTitle', 'wkState', 'wkPrice1', 'id']),
);
}
public function testRealColumnsAreNotVirtual(): void
{
foreach (['wkId', 'wkState', 'wkInternalEventNumber', 'wkEventType', 'wkMaxPlaces', 'wkPlacesLeft', 'wkImported', 'wkCategories', 'wkCategoryPrimary', 'wkTopics', 'wkInstructor', 'wkSpeakers', 'wkNotification', 'wkConfirmationPage'] as $field) {
self::assertFalse(EventFields::isVirtual($field), $field);
}
self::assertCount(29, EventFields::virtual());
}
}