Skip to content

Commit

Permalink
Merge branch '2.4-develop' into Hammer_Platform_Health_Scope_25082023
Browse files Browse the repository at this point in the history
  • Loading branch information
sidolov authored Aug 31, 2023
2 parents 94f6302 + 7f35b87 commit 5ff7a60
Show file tree
Hide file tree
Showing 36 changed files with 1,862 additions and 138 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Cache\Product\MediaGallery;

use Magento\Catalog\Model\Product;
use Magento\Framework\EntityManager\HydratorPool;
use Magento\Framework\EntityManager\TypeResolver;
use Magento\GraphQlResolverCache\Model\Resolver\Result\DehydratorInterface;

/**
* MediaGallery resolver data dehydrator to create snapshot data necessary to restore model.
*/
class ProductModelDehydrator implements DehydratorInterface
{
/**
* @var TypeResolver
*/
private TypeResolver $typeResolver;

/**
* @var HydratorPool
*/
private HydratorPool $hydratorPool;

/**
* @param HydratorPool $hydratorPool
* @param TypeResolver $typeResolver
*/
public function __construct(
HydratorPool $hydratorPool,
TypeResolver $typeResolver
) {
$this->typeResolver = $typeResolver;
$this->hydratorPool = $hydratorPool;
}

/**
* @inheritdoc
*/
public function dehydrate(array &$resolvedValue): void
{
if (count($resolvedValue) > 0) {
$firstKey = array_key_first($resolvedValue);
$this->dehydrateMediaGalleryEntity($resolvedValue[$firstKey]);
foreach ($resolvedValue as $key => &$value) {
if ($key !== $firstKey) {
unset($value['model']);
}
}
}
}

/**
* Dehydrate the resolved value of a media gallery entity.
*
* @param array $mediaGalleryEntityResolvedValue
* @return void
* @throws \Exception
*/
private function dehydrateMediaGalleryEntity(array &$mediaGalleryEntityResolvedValue): void
{
if (array_key_exists('model', $mediaGalleryEntityResolvedValue)
&& $mediaGalleryEntityResolvedValue['model'] instanceof Product) {
/** @var Product $model */
$model = $mediaGalleryEntityResolvedValue['model'];
$entityType = $this->typeResolver->resolve($model);
$mediaGalleryEntityResolvedValue['model_info']['model_data'] = $this->hydratorPool->getHydrator($entityType)
->extract($model);
$mediaGalleryEntityResolvedValue['model_info']['model_entity_type'] = $entityType;
$mediaGalleryEntityResolvedValue['model_info']['model_id'] = $model->getId();
unset($mediaGalleryEntityResolvedValue['model']);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Cache\Product\MediaGallery;

use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductFactory;
use Magento\Framework\EntityManager\HydratorPool;
use Magento\GraphQlResolverCache\Model\Resolver\Result\HydratorInterface;
use Magento\GraphQlResolverCache\Model\Resolver\Result\PrehydratorInterface;

/**
* Product resolver data hydrator to rehydrate propagated model.
*/
class ProductModelHydrator implements HydratorInterface, PrehydratorInterface
{
/**
* @var ProductFactory
*/
private ProductFactory $productFactory;

/**
* @var Product[]
*/
private array $products = [];

/**
* @var HydratorPool
*/
private HydratorPool $hydratorPool;

/**
* @param ProductFactory $productFactory
* @param HydratorPool $hydratorPool
*/
public function __construct(
ProductFactory $productFactory,
HydratorPool $hydratorPool
) {
$this->hydratorPool = $hydratorPool;
$this->productFactory = $productFactory;
}

/**
* @inheritdoc
*/
public function hydrate(array &$resolverData): void
{
if (array_key_exists('model_info', $resolverData)) {
if (isset($this->products[$resolverData['model_info']['model_id']])) {
$resolverData['model'] = $this->products[$resolverData['model_info']['model_id']];
} else {
$hydrator = $this->hydratorPool->getHydrator($resolverData['model_info']['model_entity_type']);
$model = $this->productFactory->create();
$hydrator->hydrate($model, $resolverData['model_info']['model_data']);
$this->products[$resolverData['model_info']['model_id']] = $model;
$resolverData['model'] = $this->products[$resolverData['model_info']['model_id']];
}
unset($resolverData['model_info']);
}
}

/**
* @inheritDoc
*/
public function prehydrate(array &$resolverData): void
{
$firstKey = array_key_first($resolverData);
foreach ($resolverData as &$value) {
$value['model_info'] = &$resolverData[$firstKey]['model_info'];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Cache\Product\MediaGallery;

use Magento\Catalog\Model\Product;
use Magento\GraphQlResolverCache\Model\Resolver\Result\Cache\IdentityInterface;

/**
* Identity for resolved media gallery for resolver cache type
*/
class ResolverCacheIdentity implements IdentityInterface
{
/**
* @var string
*/
public const CACHE_TAG = 'gql_media_gallery';

/**
* @inheritDoc
*/
public function getIdentities($resolvedData, ?array $parentResolvedData = null): array
{
if (empty($resolvedData)) {
return [];
}
/** @var Product $mediaGalleryEntryProduct */
$mediaGalleryEntryProduct = array_pop($resolvedData)['model'];
return [
sprintf('%s_%s', self::CACHE_TAG, $mediaGalleryEntryProduct->getId())
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Cache\Product\MediaGallery;

use Magento\Catalog\Model\Product;
use Magento\CatalogGraphQl\Model\Resolver\Product\MediaGallery\ChangeDetector;
use Magento\Framework\App\Cache\Tag\StrategyInterface;

class TagsStrategy implements StrategyInterface
{
/**
* @var ChangeDetector
*/
private $mediaGalleryChangeDetector;

/**
* @param ChangeDetector $mediaGalleryChangeDetector
*/
public function __construct(ChangeDetector $mediaGalleryChangeDetector)
{
$this->mediaGalleryChangeDetector = $mediaGalleryChangeDetector;
}

/**
* @inheritDoc
*/
public function getTags($object)
{
if ($object instanceof Product &&
!$object->isObjectNew() &&
$this->mediaGalleryChangeDetector->isChanged($object)
) {
return [
sprintf('%s_%s', ResolverCacheIdentity::CACHE_TAG, $object->getId())
];
}

return [];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\CacheKey\FactorProvider;

use Magento\Framework\Model\AbstractModel;
use Magento\GraphQl\Model\Query\ContextInterface;
use Magento\GraphQlResolverCache\Model\Resolver\Result\CacheKey\ParentValueFactorProviderInterface;

/**
* Provides product id from the model object in the parent resolved value
* as a factor to use in the cache key for resolver cache
*/
class ParentProductEntityId implements ParentValueFactorProviderInterface
{
/**
* Factor name.
*/
private const NAME = "PARENT_ENTITY_PRODUCT_ID";

/**
* @inheritdoc
*/
public function getFactorName(): string
{
return static::NAME;
}

/**
* @inheritDoc
*/
public function getFactorValue(ContextInterface $context, array $parentValue): string
{
if (array_key_exists('model_info', $parentValue)
&& array_key_exists('model_id', $parentValue['model_info'])) {
return (string)$parentValue['model_info']['model_id'];
} elseif (array_key_exists('model', $parentValue) && $parentValue['model'] instanceof AbstractModel) {
return (string)$parentValue['model']->getId();
}
throw new \InvalidArgumentException(__CLASS__ . " factor provider requires parent value " .
"to contain product model id or product model.");
}

/**
* @inheritDoc
*/
public function isRequiredOrigData(): bool
{
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\CatalogGraphQl\Model\Resolver\Product\MediaGallery;

use Magento\Catalog\Model\Product;
use Magento\Framework\Serialize\SerializerInterface;

class ChangeDetector
{
/**
* @var SerializerInterface
*/
private $serializer;

/**
* @param SerializerInterface $serializer
*/
public function __construct(
SerializerInterface $serializer
) {
$this->serializer = $serializer;
}

/**
* Check if the media gallery of the given product is changed
*
* @param Product $product
* @return bool
*/
public function isChanged(Product $product): bool
{
if ($product->isDeleted()) {
return true;
}

if (!$product->hasDataChanges()) {
return false;
}

$mediaGalleryImages = $product->getMediaGallery('images') ?? [];

$origMediaGalleryImages = $product->getOrigData('media_gallery')['images'] ?? [];

$origMediaGalleryImageKeys = array_keys($origMediaGalleryImages);
$mediaGalleryImageKeys = array_keys($mediaGalleryImages);

if ($origMediaGalleryImageKeys !== $mediaGalleryImageKeys) {
return true;
}

// remove keys from original array that are not in new array; some keys are omitted from the new array on save
foreach ($mediaGalleryImages as $imageKey => $mediaGalleryImage) {
$origMediaGalleryImages[$imageKey] = array_intersect_key(
$origMediaGalleryImages[$imageKey],
$mediaGalleryImage
);

// client UI converts null values to empty string due to behavior of HTML encoding;
// match this behavior before performing comparison
foreach ($origMediaGalleryImages[$imageKey] as $key => &$value) {
if ($value === null) {
$value = '';
}

if ($mediaGalleryImages[$imageKey][$key] === null) {
$mediaGalleryImages[$imageKey][$key] = '';
}
}
}

$mediaGalleryImagesSerializedString = $this->serializer->serialize($mediaGalleryImages);
$origMediaGalleryImagesSerializedString = $this->serializer->serialize($origMediaGalleryImages);

return $origMediaGalleryImagesSerializedString != $mediaGalleryImagesSerializedString;
}
}
Loading

0 comments on commit 5ff7a60

Please sign in to comment.