IPN-Vorschlagslogik erweitert und Bauteil-IPN vereindeutigt

Die IPN-Logik wurde um eine Konfiguration zur automatischen Suffix-Anfügung und die Berücksichtigung von doppelten Beschreibungen bei Bedarf ergänzt. Zudem wurde das Datenmodell angepasst, um eine eindeutige Speicherung der IPN zu gewährleisten.
This commit is contained in:
Marcel Diegelmann 2025-09-29 13:54:13 +02:00
parent 9b90a513c9
commit 654c2ed2af
16 changed files with 165 additions and 117 deletions

View file

@ -5,14 +5,12 @@ namespace App\EventSubscriber\UserSystem;
use App\Entity\Parts\Part;
use App\Settings\MiscSettings\IpnSuggestSettings;
use Doctrine\Common\EventSubscriber;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
class PartUniqueIpnSubscriber implements EventSubscriber
{
public function __construct(
private EntityManagerInterface $entityManager,
private IpnSuggestSettings $ipnSuggestSettings
) {
}
@ -20,55 +18,80 @@ class PartUniqueIpnSubscriber implements EventSubscriber
public function getSubscribedEvents(): array
{
return [
Events::prePersist,
Events::preUpdate,
Events::onFlush,
];
}
public function prePersist(LifecycleEventArgs $args): void
public function onFlush(OnFlushEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof Part) {
$this->ensureUniqueIpn($entity);
}
}
public function preUpdate(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof Part) {
$this->ensureUniqueIpn($entity);
}
}
private function ensureUniqueIpn(Part $part): void
{
if ($part->getIpn() === null || $part->getIpn() === '') {
if (!$this->ipnSuggestSettings->autoAppendSuffix) {
return;
}
$existingPart = $this->entityManager
->getRepository(Part::class)
->findOneBy(['ipn' => $part->getIpn()]);
$em = $args->getObjectManager();
$uow = $em->getUnitOfWork();
$meta = $em->getClassMetadata(Part::class);
if ($existingPart && $existingPart->getId() !== $part->getId()) {
if ($this->ipnSuggestSettings->enableUniqueCheck) {
// Collect all IPNs already reserved in the current flush (so new entities do not collide with each other)
$reservedIpns = [];
// Helper to assign a collision-free IPN for a Part entity
$ensureUnique = function (Part $part) use ($em, $uow, $meta, &$reservedIpns) {
$ipn = $part->getIpn();
if ($ipn === null || $ipn === '') {
return;
}
// Anhang eines Inkrements bis ein einzigartiger Wert gefunden wird
// Check against IPNs already reserved in the current flush (except itself)
$originalIpn = $ipn;
$candidate = $originalIpn;
$increment = 1;
$originalIpn = $part->getIpn();
while ($this->entityManager
->getRepository(Part::class)
->findOneBy(['ipn' => $originalIpn . "_$increment"])) {
$conflicts = function (string $candidate) use ($em, $part, $reservedIpns) {
// Collision within the current flush session?
if (isset($reservedIpns[$candidate]) && $reservedIpns[$candidate] !== $part) {
return true;
}
// Collision with an existing DB row?
$existing = $em->getRepository(Part::class)->findOneBy(['ipn' => $candidate]);
return $existing !== null && $existing->getId() !== $part->getId();
};
while ($conflicts($candidate)) {
$candidate = $originalIpn . '_' . $increment;
$increment++;
}
$part->setIpn($originalIpn . "_$increment");
if ($candidate !== $ipn) {
$before = $part->getIpn();
$part->setIpn($candidate);
// Recompute the change set so Doctrine writes the change
$uow->recomputeSingleEntityChangeSet($meta, $part);
$reservedIpns[$candidate] = $part;
// If the old IPN was reserved already, clean it up
if ($before !== null && isset($reservedIpns[$before]) && $reservedIpns[$before] === $part) {
unset($reservedIpns[$before]);
}
} else {
// Candidate unchanged, but reserve it so subsequent entities see it
$reservedIpns[$candidate] = $part;
}
};
// 1) Iterate over new entities
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Part) {
$ensureUnique($entity);
}
}
// 2) Iterate over updates (if IPN changed, ensure uniqueness again)
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof Part) {
$ensureUnique($entity);
}
}
}
}