diff --git a/src/Services/EDA/KiCadHelper.php b/src/Services/EDA/KiCadHelper.php index a4d0a901f..4ce2f651d 100644 --- a/src/Services/EDA/KiCadHelper.php +++ b/src/Services/EDA/KiCadHelper.php @@ -148,7 +148,7 @@ public function getCategoryParts(?Category $category, bool $minimal = false): ar { $cacheKey = 'kicad_category_parts_'.($category?->getID() ?? 0) . '_' . $this->category_depth . ($minimal ? '_min' : ''); return $this->kicadCache->get($cacheKey, - function (ItemInterface $item) use ($category) { + function (ItemInterface $item) use ($category, $minimal) { $item->tag([ $this->tagGenerator->getElementTypeCacheTag(Category::class), $this->tagGenerator->getElementTypeCacheTag(Part::class), @@ -182,11 +182,21 @@ function (ItemInterface $item) use ($category) { continue; } - $result[] = [ - 'id' => (string)$part->getId(), - 'name' => $part->getName(), - 'description' => $part->getDescription(), - ]; + //A minimal listing carries just enough to populate the chooser's + //name column. Otherwise inline the same record the per-part + //endpoint returns: KiCad skips its follow-up request for every + //part whose listing entry already carries a "fields" object + //(see HTTP_LIB_CONNECTION::SelectAll), which turns a cold library + //open from one request per part into one request per category. + if ($minimal) { + $result[] = [ + 'id' => (string)$part->getId(), + 'name' => $part->getName(), + 'description' => $part->getDescription(), + ]; + } else { + $result[] = $this->getKiCADPart($part); + } } return $result; diff --git a/tests/Services/EDA/KiCadHelperTest.php b/tests/Services/EDA/KiCadHelperTest.php index 99106d5e4..c6e0f0a19 100644 --- a/tests/Services/EDA/KiCadHelperTest.php +++ b/tests/Services/EDA/KiCadHelperTest.php @@ -632,4 +632,57 @@ public function testParameterWithEmptyNameIsSkipped(): void // Empty-named parameter should not appear self::assertArrayNotHasKey('', $result['fields']); } + + /** + * Category 1 (from fixtures) has a KiCad symbol set, so its parts are visible to the EDA. + * The listing must carry the fields, so KiCad does not have to request each part separately. + */ + public function testCategoryPartsListingContainsFields(): void + { + $category = $this->em->find(Category::class, 1); + + $result = $this->helper->getCategoryParts($category); + + self::assertNotEmpty($result); + foreach ($result as $part) { + self::assertArrayHasKey('fields', $part); + self::assertIsArray($part['fields']); + //Every part gets at least these, either from itself or from its category + self::assertArrayHasKey('reference', $part['fields']); + self::assertArrayHasKey('value', $part['fields']); + self::assertArrayHasKey('footprint', $part['fields']); + self::assertArrayHasKey('symbolIdStr', $part); + } + } + + /** + * The minimal listing stays minimal: id, name and description only. + */ + public function testMinimalCategoryPartsListingContainsNoFields(): void + { + $category = $this->em->find(Category::class, 1); + + $result = $this->helper->getCategoryParts($category, true); + + self::assertNotEmpty($result); + foreach ($result as $part) { + self::assertSame(['id', 'name', 'description'], array_keys($part)); + } + } + + /** + * The minimal and the full listing must describe the same parts, in the same order. + */ + public function testMinimalAndFullCategoryPartsListingsAgreeOnParts(): void + { + $category = $this->em->find(Category::class, 1); + + $minimal = $this->helper->getCategoryParts($category, true); + $full = $this->helper->getCategoryParts($category); + + self::assertSame( + array_column($minimal, 'id'), + array_column($full, 'id') + ); + } }