diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index 85159c51d..62acacdd5 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -145,7 +145,10 @@ public function getCategories(): array */ public function getCategoryParts(?Category $category): array { - $cacheKey = 'kicad_category_parts_'.($category?->getID() ?? 0) . '_' . $this->category_depth; + // Settings fingerprint keeps cached responses from surviving settings changes + // (tag-based invalidation only fires on part/category/footprint edits) + $cacheKey = 'kicad_category_parts_'.($category?->getID() ?? 0) . '_' . $this->category_depth + . '_' . $this->edaSettingsFingerprint(); return $this->kicadCache->get($cacheKey, function (ItemInterface $item) use ($category) { $item->tag([ @@ -355,6 +358,19 @@ public function getKiCADPart(Part $part): array return $result; } + /** + * Fingerprint of every setting that changes the content of a serialized part. + */ + private function edaSettingsFingerprint(): string + { + return hash("xxh3", json_encode([ + $this->datasheetAsPdf, + $this->kiCadEDASettings->defaultOrderdetailsVisibility, + $this->kiCadEDASettings->defaultParameterVisibility, + $this->kiCadEDASettings->defaultParameterSymbolVisibility, + ], JSON_THROW_ON_ERROR)); + } + /** * Determine if the given part should be visible for the EDA. * @param Category $category diff --git a/tests/Services/EDA/KiCadHelperTest.php b/tests/Services/EDA/KiCadHelperTest.php index b043d5980..1d094d6e8 100644 --- a/tests/Services/EDA/KiCadHelperTest.php +++ b/tests/Services/EDA/KiCadHelperTest.php @@ -33,6 +33,7 @@ use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Orderdetail; use App\Services\EDA\KiCadHelper; +use App\Settings\MiscSettings\KiCadEDASettings; use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\Attributes\Group; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -654,4 +655,38 @@ public function testCategoryPartsListingContainsFields(): void self::assertArrayHasKey('symbolIdStr', $part); } } + + /** + * The category listing is cached and only invalidated by entity changes. + * Changing a setting that affects the exported fields must not serve a stale listing. + */ + public function testCategoryPartsCacheIsInvalidatedBySettingsChange(): void + { + /** @var KiCadEDASettings $settings */ + $settings = self::getContainer()->get(KiCadEDASettings::class); + $category = $this->em->find(Category::class, 1); + + $part = new Part(); + $part->setName('Part for cache test'); + $part->setCategory($category); + $param = new PartParameter(); + $param->setName('CacheTestParam'); + $param->setValueText('42'); + $param->setEdaVisibility(null); + $part->addParameter($param); + $this->em->persist($part); + $this->em->flush(); + + $findFields = fn(array $listing): array => array_values(array_filter($listing, fn($p) => (int) $p['id'] === $part->getId()))[0]['fields']; + + $settings->defaultParameterVisibility = false; + $before = $findFields($this->helper->getCategoryParts($category)); + self::assertArrayNotHasKey('CacheTestParam', $before); + + // No entity changed, only the setting: the cached listing must not be reused + $settings->defaultParameterVisibility = true; + $after = $findFields($this->helper->getCategoryParts($category)); + self::assertArrayHasKey('CacheTestParam', $after); + self::assertSame('42', $after['CacheTestParam']['value']); + } }