Part-DB-server/src/Validator/Constraints/AssemblySystem/AssemblyInvalidBomEntryValidator.php
2026-02-16 09:19:33 +01:00

142 lines
5.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Validator\Constraints\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use Symfony\Component\Form\Form;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilder;
use ReflectionClass;
/**
* Validator to check that no child assemblies are referenced in BOM entries.
*/
class AssemblyInvalidBomEntryValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof AssemblyInvalidBomEntry) {
throw new UnexpectedTypeException($constraint, AssemblyInvalidBomEntry::class);
}
if (!$value instanceof Assembly) {
return;
}
$availableViolations = $this->context->getViolations();
if (count($availableViolations) > 0) {
//already violations given, currently no more needed to check
return;
}
$bomEntries = [];
if ($this->context->getRoot() instanceof Form && $this->context->getRoot()->has('bom_entries')) {
$bomEntries = $this->context->getRoot()->get('bom_entries')->getData();
$bomEntries = is_array($bomEntries) ? $bomEntries : iterator_to_array($bomEntries);
} elseif ($this->context->getRoot() instanceof Assembly) {
$bomEntries = $value->getBomEntries()->toArray();
}
$relevantEntries = [];
foreach ($bomEntries as $bomEntry) {
if ($bomEntry->getReferencedAssembly() !== null) {
$relevantEntries[$bomEntry->getId()] = $bomEntry;
}
}
foreach ($relevantEntries as $bomEntry) {
$referencedAssembly = $bomEntry->getReferencedAssembly();
if ($bomEntry->getAssembly()->getParent()?->getId() === $referencedAssembly->getParent()?->getId()) {
//Save on the same assembly level
continue;
} elseif ($this->isInvalidBomEntry($referencedAssembly, $bomEntry->getAssembly())) {
$this->addViolation($value, $constraint);
}
}
}
/**
* Determines whether a Bill of Materials (BOM) entry is invalid based on the relationship
* between the current assembly and the parent assembly.
*
* @param Assembly|null $currentAssembly The current assembly being analyzed. Null indicates no assembly is referenced.
* @param Assembly $parentAssembly The parent assembly to check against the current assembly.
*
* @return bool Returns
*/
private function isInvalidBomEntry(?Assembly $currentAssembly, Assembly $parentAssembly): bool
{
//No assembly referenced -> no problems
if ($currentAssembly === null) {
return false;
}
//Check: is the current assembly a descendant of the parent assembly?
if ($currentAssembly->isChildOf($parentAssembly)) {
return true;
}
//Recursive check: Analyze the current assembly list
foreach ($currentAssembly->getBomEntries() as $bomEntry) {
$referencedAssembly = $bomEntry->getReferencedAssembly();
if ($this->isInvalidBomEntry($referencedAssembly, $parentAssembly)) {
return true;
}
}
return false;
}
/**
* Adds a violation to the current context if it hasnt already been added.
*
* This method checks whether a violation with the same property path as the current violation
* already exists in the context. If such a violation is found, the current violation is not added again.
* The process involves reflection to access private or protected properties of violation objects.
*
* @param mixed $value The value that triggered the violation.
* @param AssemblyInvalidBomEntry $constraint The constraint containing the validation details.
*
*/
private function addViolation($value, AssemblyInvalidBomEntry $constraint): void
{
/** @var ConstraintViolationBuilder $buildViolation */
$buildViolation = $this->context->buildViolation($constraint->message)
->setParameter('%name%', $value->getName());
$alreadyAdded = false;
try {
$reflectionClass = new ReflectionClass($buildViolation);
$property = $reflectionClass->getProperty('propertyPath');
$propertyPath = $property->getValue($buildViolation);
$availableViolations = $this->context->getViolations();
foreach ($availableViolations as $tmpViolation) {
$tmpReflectionClass = new ReflectionClass($tmpViolation);
$tmpProperty = $tmpReflectionClass->getProperty('propertyPath');
$tmpPropertyPath = $tmpProperty->getValue($tmpViolation);
if ($tmpPropertyPath === $propertyPath) {
$alreadyAdded = true;
}
}
} catch (\ReflectionException) {
}
if (!$alreadyAdded) {
$buildViolation->addViolation();
}
}
}