feat: expose eBay aspect usage tier (RECOMMENDED vs OPTIONAL)
EbayTaxonomyService.getCategoryAspects() now returns 'usage' alongside 'required'. eBay has three effective tiers for category 177/Notebooks: required=true + usage=RECOMMENDED → hard gate (3 aspects) required=false + usage=RECOMMENDED → search ranking signal (17 aspects) required=false + usage=OPTIONAL → truly optional (11 aspects) Integration test covers all three tiers explicitly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
68a9f0094e
commit
7f2ec21c64
2 changed files with 26 additions and 11 deletions
|
|
@ -20,9 +20,13 @@ final class EbayTaxonomyService
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the required item aspect names for a given eBay category.
|
||||
* Returns item aspects for a given eBay category, including eBay's usage tier.
|
||||
*
|
||||
* @return list<array{name: string, required: bool, values: list<string>}>
|
||||
* usage values:
|
||||
* 'RECOMMENDED' — eBay search-ranking signal; include whenever possible
|
||||
* 'OPTIONAL' — truly optional, low impact
|
||||
*
|
||||
* @return list<array{name: string, required: bool, usage: string, values: list<string>}>
|
||||
*/
|
||||
public function getCategoryAspects(string $categoryId): array
|
||||
{
|
||||
|
|
@ -45,14 +49,15 @@ final class EbayTaxonomyService
|
|||
],
|
||||
);
|
||||
|
||||
/** @var array{aspects?: list<array{localizedAspectName: string, aspectConstraint?: array{aspectRequired?: bool}, aspectValues?: list<array{localizedValue: string}>}>} $data */
|
||||
/** @var array{aspects?: list<array{localizedAspectName: string, aspectConstraint?: array{aspectRequired?: bool, aspectUsage?: string}, aspectValues?: list<array{localizedValue: string}>}>} $data */
|
||||
$data = $response->toArray();
|
||||
$aspects = [];
|
||||
|
||||
foreach ($data['aspects'] ?? [] as $aspect) {
|
||||
$aspects[] = [
|
||||
'name' => $aspect['localizedAspectName'],
|
||||
'required' => $aspect['aspectConstraint']['aspectRequired'] ?? false,
|
||||
'required' => (bool) ($aspect['aspectConstraint']['aspectRequired'] ?? false),
|
||||
'usage' => $aspect['aspectConstraint']['aspectUsage'] ?? 'OPTIONAL',
|
||||
'values' => array_column($aspect['aspectValues'] ?? [], 'localizedValue'),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,35 +71,45 @@ final class EbayTaxonomyIntegrationTest extends TestCase
|
|||
|
||||
$this->assertNotEmpty($aspects, 'Category 177 should have aspects');
|
||||
|
||||
// Each aspect must have the expected shape
|
||||
foreach ($aspects as $aspect) {
|
||||
$this->assertArrayHasKey('name', $aspect);
|
||||
$this->assertArrayHasKey('required', $aspect);
|
||||
$this->assertArrayHasKey('usage', $aspect);
|
||||
$this->assertArrayHasKey('values', $aspect);
|
||||
$this->assertIsString($aspect['name']);
|
||||
$this->assertIsBool($aspect['required']);
|
||||
$this->assertContains($aspect['usage'], ['RECOMMENDED', 'OPTIONAL'], 'usage must be a known eBay tier');
|
||||
$this->assertIsArray($aspect['values']);
|
||||
}
|
||||
|
||||
// Notebooks should have at least a Prozessor/CPU aspect
|
||||
$names = array_column($aspects, 'name');
|
||||
$this->assertNotEmpty(array_filter($names, static fn (string $n) => str_contains(strtolower($n), 'prozes') || str_contains(strtolower($n), 'cpu') || str_contains(strtolower($n), 'speicher')), 'Expected at least one hardware aspect (CPU/RAM)');
|
||||
$this->assertNotEmpty(
|
||||
array_filter($names, static fn (string $n) => str_contains(strtolower($n), 'prozes') || str_contains(strtolower($n), 'cpu') || str_contains(strtolower($n), 'speicher')),
|
||||
'Expected at least one hardware aspect (CPU/RAM)',
|
||||
);
|
||||
}
|
||||
|
||||
public function test_required_aspects_are_flagged(): void
|
||||
public function test_three_tier_aspect_classification(): void
|
||||
{
|
||||
// eBay has three effective tiers:
|
||||
// required=true + usage=RECOMMENDED → hard gate, eBay blocks listing without it
|
||||
// required=false + usage=RECOMMENDED → "you should have this", search ranking signal
|
||||
// required=false + usage=OPTIONAL → truly optional, low impact
|
||||
$aspects = $this->taxonomy->getCategoryAspects('177');
|
||||
|
||||
$required = array_filter($aspects, static fn (array $a) => $a['required']);
|
||||
$hardRequired = array_filter($aspects, static fn (array $a) => $a['required']);
|
||||
$recommended = array_filter($aspects, static fn (array $a) => !$a['required'] && 'RECOMMENDED' === $a['usage']);
|
||||
$optional = array_filter($aspects, static fn (array $a) => !$a['required'] && 'OPTIONAL' === $a['usage']);
|
||||
|
||||
$this->assertNotEmpty($required, 'Category 177 should have at least one required aspect');
|
||||
$this->assertNotEmpty($hardRequired, 'Should have hard-required aspects');
|
||||
$this->assertNotEmpty($recommended, 'Should have recommended (ranking-signal) aspects');
|
||||
$this->assertNotEmpty($optional, 'Should have truly optional aspects');
|
||||
}
|
||||
|
||||
public function test_aspects_with_predefined_values_have_options(): void
|
||||
{
|
||||
$aspects = $this->taxonomy->getCategoryAspects('177');
|
||||
|
||||
// At least some aspects should have predefined value lists (e.g. Zustand, Prozessorfamilie)
|
||||
$withOptions = array_filter($aspects, static fn (array $a) => [] !== $a['values']);
|
||||
|
||||
$this->assertNotEmpty($withOptions, 'Some aspects should have predefined selectable values');
|
||||
|
|
|
|||
Loading…
Reference in a new issue