diff --git a/CHANGELOG.md b/CHANGELOG.md index cc36160e01..3203e72bd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Improved the performance of `Product::getVariants()`. ([#3578](https://github.com/craftcms/commerce/issues/3758)) - Fixed a SQL error that could occur when creating a variant. ([#3763](https://github.com/craftcms/commerce/issues/)) ## 5.2.3 - 2024-11-13 diff --git a/src/elements/Product.php b/src/elements/Product.php index 98eea0d891..20526d32c8 100644 --- a/src/elements/Product.php +++ b/src/elements/Product.php @@ -1045,6 +1045,21 @@ public function getVariants(bool $includeDisabled = false): VariantCollection } $this->_variants = self::createVariantQuery($this)->status(null)->collect(); + $this->_variants->map(function(Variant $v) { + if (!$this->id) { + return $v; + } + + if ($v->primaryOwnerId === $this->id) { + $v->setPrimaryOwner($this); + } + + if ($v->ownerId === $this->id) { + $v->setOwner($this); + } + + return $v; + }); } return $this->_variants->filter(fn(Variant $variant) => $includeDisabled || ($variant->getStatus() === self::STATUS_ENABLED)); diff --git a/src/elements/Variant.php b/src/elements/Variant.php index f1b8816b92..f0ddc22b28 100755 --- a/src/elements/Variant.php +++ b/src/elements/Variant.php @@ -60,8 +60,6 @@ * @property-read string $gqlTypeName * @property-read string $skuAsText * @property string $salePriceAsCurrency - * @method Product|null getOwner() - * @method Product|null getPrimaryOwner() * @author Pixel & Tonic, Inc. * @since 2.0 */ @@ -524,6 +522,59 @@ public function setOwner(?ElementInterface $owner): void $this->traitSetOwner($owner); } + /** + * @inheritdoc + * @TODO remove implementation when `NestedElementTrait::getOwner()` is updated + */ + public function getPrimaryOwner(): ?Product + { + if (!isset($this->_primaryOwner)) { + $primaryOwnerId = $this->getPrimaryOwnerId(); + if (!$primaryOwnerId) { + return null; + } + + $this->_primaryOwner = Craft::$app->getElements()->getElementById($primaryOwnerId, Product::class, $this->siteId, [ + 'trashed' => null, + ]) ?? false; + if (!$this->_primaryOwner) { + throw new InvalidConfigException("Invalid owner ID: $primaryOwnerId"); + } + } + + /** @phpstan-ignore-next-line */ + return $this->_primaryOwner ?: null; + } + + /** + * @inheritdoc + * @TODO remove implementation when `NestedElementTrait::getOwner()` is updated + */ + public function getOwner(): ?Product + { + if (!isset($this->_owner)) { + $ownerId = $this->getOwnerId(); + if (!$ownerId) { + return null; + } + + // If ownerId and primaryOwnerId are the same, return the primary owner + if ($ownerId === $this->getPrimaryOwnerId()) { + return $this->getPrimaryOwner(); + } + + $this->_owner = Craft::$app->getElements()->getElementById($ownerId, Product::class, $this->siteId, [ + 'trashed' => null, + ]) ?? false; + if (!$this->_owner) { + throw new InvalidConfigException("Invalid owner ID: $ownerId"); + } + } + + /** @phpstan-ignore-next-line */ + return $this->_owner ?: null; + } + /** * Returns the product associated with this variant. *