validateEntity($entityId); $this->validateTaxonomyTerm($taxonomyTermId); $this->validateRelevance($relevance); // Check if mapping already exists if ($this->entityTaxonomyRepository->exists($entityId, $taxonomyTermId)) { throw new \InvalidArgumentException( "Mapping already exists for entity {$entityId} and term {$taxonomyTermId}" ); } $mapping = new EntityTaxonomyMapping(); $mapping->setEntityId($entityId); $mapping->setTaxonomyTermId($taxonomyTermId); $mapping->setRelevance(Confidence::fromFloat($relevance)); $mapping->setValidated(false); $id = $this->entityTaxonomyRepository->save($mapping); $this->auditService->logCreate( table: 'entity_taxonomy_mapping', id: $id, data: [ 'entity_id' => $entityId, 'taxonomy_term_id' => $taxonomyTermId, 'relevance' => $relevance, 'validated' => false, ], actor: 'system', actorType: 'pipeline' ); return $id; } /** * Validate an existing mapping (mark as human-verified). * * @throws \InvalidArgumentException if mapping not found */ public function validateMapping(int $mappingId): void { $mappings = $this->entityTaxonomyRepository->getUnvalidatedMappings(1000); $found = null; foreach ($mappings as $mapping) { if ($mapping->getId() === $mappingId) { $found = $mapping; break; } } if ($found === null) { // Check if mapping exists at all $result = $this->entityTaxonomyRepository->markAsValidated($mappingId); if (!$result) { throw new \InvalidArgumentException("Mapping {$mappingId} not found"); } } else { $this->entityTaxonomyRepository->markAsValidated($mappingId); } $this->auditService->logUpdate( table: 'entity_taxonomy_mapping', id: $mappingId, oldData: ['validated' => false], newData: ['validated' => true], actor: 'user', actorType: 'user' ); } /** * Batch assign taxonomy terms to entities. * * @param array $assignments * @return array{success: int, errors: array} */ public function batchAssign(array $assignments): array { $successCount = 0; $errors = []; foreach ($assignments as $index => $assignment) { try { $this->execute( entityId: $assignment['entity_id'], taxonomyTermId: $assignment['taxonomy_term_id'], relevance: $assignment['relevance'] ); $successCount++; } catch (\Exception $e) { $errors[] = "Assignment {$index}: " . $e->getMessage(); } } return [ 'success' => $successCount, 'errors' => $errors, ]; } /** * Get validation statistics. * * @return array{total: int, validated: int, pending: int} */ public function getValidationStats(): array { return $this->entityTaxonomyRepository->getValidationStats(); } private function validateEntity(int $entityId): void { $entity = $this->entityRepository->find($entityId); if ($entity === null) { throw new \InvalidArgumentException("Entity {$entityId} not found"); } } private function validateTaxonomyTerm(int $termId): void { $term = $this->taxonomyRepository->find($termId); if ($term === null) { throw new \InvalidArgumentException("Taxonomy term {$termId} not found"); } } private function validateRelevance(float $relevance): void { if ($relevance < 0.0 || $relevance > 1.0) { throw new \InvalidArgumentException( "Relevance must be between 0.0 and 1.0, got {$relevance}" ); } } }