. */ declare(strict_types=1); namespace App\Services\EntityMergers\Mergers; use App\Entity\Parts\InfoProviderReference; use App\Entity\Parts\ManufacturingStatus; use App\Entity\Parts\Part; use App\Entity\Parts\PartAssociation; use App\Entity\Parts\PartLot; use App\Entity\PriceInformations\Orderdetail; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; /** * @implements EntityMergerInterface */ #[Autoconfigure(public: true)] class PartMerger implements EntityMergerInterface { use EntityMergerHelperTrait; public function supports(object $target, object $other, array $context = []): bool { return $target instanceof Part && $other instanceof Part; } public function merge(object $target, object $other, array $context = []): Part { if (!$target instanceof Part || !$other instanceof Part) { throw new \InvalidArgumentException('The target and the other entity must be instances of Part'); } //Merge basic fields $this->mergeTextWithSeparator($target, $other, 'name'); $this->mergeTextWithSeparator($target, $other, 'description'); $this->mergeComment($target, $other); $this->useOtherValueIfNotEmtpy($target, $other, 'manufacturer_product_url'); $this->useOtherValueIfNotEmtpy($target, $other, 'manufacturer_product_number'); $this->useOtherValueIfNotEmtpy($target, $other, 'mass'); $this->useOtherValueIfNotEmtpy($target, $other, 'ipn'); //Merge relations to other entities $this->useOtherValueIfNotNull($target, $other, 'manufacturer'); $this->useOtherValueIfNotNull($target, $other, 'footprint'); $this->useOtherValueIfNotNull($target, $other, 'category'); $this->useOtherValueIfNotNull($target, $other, 'partUnit'); //We assume that the higher value is the correct one for minimum instock $this->useLargerValue($target, $other, 'minamount'); //We assume that a part needs review and is a favorite if one of the parts is $this->useTrueValue($target, $other, 'needs_review'); $this->useTrueValue($target, $other, 'favorite'); //Merge the tags using the tag merger $this->mergeTags($target, $other, 'tags'); //Merge manufacturing status $this->useCallback(function (?ManufacturingStatus $t, ?ManufacturingStatus $o): ManufacturingStatus { //Use the other value, if the target value is not set if ($t === ManufacturingStatus::NOT_SET || $t === null) { return $o ?? ManufacturingStatus::NOT_SET; } return $t; }, $target, $other, 'manufacturing_status'); //Merge provider reference $this->useCallback(function (InfoProviderReference $t, InfoProviderReference $o): InfoProviderReference { if (!$t->isProviderCreated() && $o->isProviderCreated()) { return $o; } return $t; }, $target, $other, 'providerReference'); //Merge the collections $this->mergeCollectionFields($target, $other, $context); return $target; } private function mergeCollectionFields(Part $target, Part $other, array $context): void { /******************************************************************************** * Merge collections ********************************************************************************/ //Lots from different parts are never considered equal, so we just merge them together $this->mergeCollections($target, $other, 'partLots'); $this->mergeAttachments($target, $other); $this->mergeParameters($target, $other); $this->mergeCollections($target, $other, 'associated_parts_as_owner', function (PartAssociation $t, PartAssociation $o) { //We compare the translation keys, as it contains info about the type and other type info return $t->getOther() === $o->getOther() && $t->getTypeTranslationKey() === $o->getTypeTranslationKey(); }); $this->mergeCollections($target, $other, 'orderdetails', function (Orderdetail $t, Orderdetail $o) { //First check that the orderdetails infos are equal $tmp = $t->getSupplier() === $o->getSupplier() && $t->getSupplierPartNr() === $o->getSupplierPartNr() && $t->getSupplierProductUrl(false) === $o->getSupplierProductUrl(false); if (!$tmp) { return false; } //Check if the pricedetails are equal $t_pricedetails = $t->getPricedetails(); $o_pricedetails = $o->getPricedetails(); //Ensure that both pricedetails have the same length if (count($t_pricedetails) !== count($o_pricedetails)) { return false; } //Check if all pricedetails are equal for ($n=0, $nMax = count($t_pricedetails); $n< $nMax; $n++) { $t_price = $t_pricedetails->get($n); $o_price = $o_pricedetails->get($n); if (!$t_price->getPrice()->isEqualTo($o_price->getPrice()) || $t_price->getCurrency() !== $o_price->getCurrency() || $t_price->getPriceRelatedQuantity() !== $o_price->getPriceRelatedQuantity() || $t_price->getMinDiscountQuantity() !== $o_price->getMinDiscountQuantity() ) { return false; } } //If all pricedetails are equal, the orderdetails are equal return true; }); //The pricedetails are not correctly assigned to the new orderdetails, so fix that foreach ($target->getOrderdetails() as $orderdetail) { foreach ($orderdetail->getPricedetails() as $pricedetail) { $pricedetail->setOrderdetail($orderdetail); } } } }