Feat: Updates to Product Indexer, Shopify Importer, Adding cascade delete on reviews
This commit is contained in:
@@ -25,6 +25,7 @@ use Modules\Core\MigrateImport\Shopify\Resolvers\ProductOptionResolver;
|
||||
use Modules\Core\MigrateImport\Shopify\Resolvers\ProductTypeResolver;
|
||||
use Modules\Core\MigrateImport\Shopify\Resolvers\TagResolver;
|
||||
use Modules\Core\MigrateImport\Shopify\Resolvers\TaxClassResolver;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
|
||||
class ShopifyExportImporter implements Importer
|
||||
{
|
||||
@@ -113,7 +114,7 @@ class ShopifyExportImporter implements Importer
|
||||
$options = $this->attachOptions($product, $row);
|
||||
|
||||
foreach ($group->variantRows as $index => $variantRow) {
|
||||
$this->importVariant($product, $group->handle, $index, $variantRow, $taxClass, $currency, $options);
|
||||
$this->importVariant($product, $group->handle, $index, $variantRow, $taxClass, $currency, $options, $imagesPath);
|
||||
}
|
||||
|
||||
foreach ($group->imageRows as $index => $imageRow) {
|
||||
@@ -151,6 +152,7 @@ class ShopifyExportImporter implements Importer
|
||||
TaxClass $taxClass,
|
||||
Currency $currency,
|
||||
array $options,
|
||||
string $imagesPath,
|
||||
): void {
|
||||
$externalId = "{$handle}#{$index}";
|
||||
$existing = ImportMapping::resolve(self::SOURCE, 'variant', $externalId);
|
||||
@@ -182,6 +184,26 @@ class ShopifyExportImporter implements Importer
|
||||
: null;
|
||||
|
||||
$this->priceResolver->resolve($variant, $currency, $price, $comparePrice);
|
||||
|
||||
// Shopify's own "Variant Image" column — the one image a variant picker
|
||||
// actually swaps to when that variant is selected — distinct from the
|
||||
// product's full gallery (imageRows below). Often the same file as one
|
||||
// of the product's own image rows, sometimes not yet imported at all
|
||||
// (e.g. a variant-only image never listed as its own image row) — either
|
||||
// way resolveOrImportImage() handles both via the same Image Src dedup
|
||||
// key, so whichever of importVariant()/importImage() runs first for a
|
||||
// given src does the actual import.
|
||||
$variantImageSrc = trim((string) ($row['Variant Image'] ?? ''));
|
||||
|
||||
if ($variantImageSrc !== '') {
|
||||
$media = $this->resolveOrImportImage($product, $handle, $variantImageSrc, 1, $imagesPath);
|
||||
|
||||
if ($media) {
|
||||
$variant->images()->syncWithoutDetaching([
|
||||
$media->id => ['primary' => true, 'position' => 1],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function importImage(
|
||||
@@ -191,29 +213,54 @@ class ShopifyExportImporter implements Importer
|
||||
array $row,
|
||||
string $imagesPath,
|
||||
): void {
|
||||
$externalId = $row['Image Src'] ?: "{$handle}#image-{$index}";
|
||||
$position = (int) ($row['Image Position'] ?? $index + 1);
|
||||
|
||||
if (ImportMapping::resolve(self::SOURCE, 'image', $externalId)) {
|
||||
return;
|
||||
$this->resolveOrImportImage($product, $handle, $row['Image Src'], $position, $imagesPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the Media already imported for $imageSrc (recorded under
|
||||
* source_type 'image', keyed by Image Src — the same URL Shopify repeats
|
||||
* across a product's own image rows and any variant's "Variant Image"
|
||||
* column), importing it via AssetResolver if this is the first time this
|
||||
* src has been seen. Shared by importImage() (product gallery) and
|
||||
* importVariant() (variant-specific image) so the same physical file is
|
||||
* never uploaded to Spatie MediaLibrary twice just because Shopify's flat
|
||||
* CSV format repeats the URL on multiple rows.
|
||||
*/
|
||||
private function resolveOrImportImage(
|
||||
Product $product,
|
||||
string $handle,
|
||||
string $imageSrc,
|
||||
int $position,
|
||||
string $imagesPath,
|
||||
): ?Media {
|
||||
$externalId = $imageSrc ?: "{$handle}#image-{$position}";
|
||||
|
||||
$existing = ImportMapping::resolve(self::SOURCE, 'image', $externalId);
|
||||
|
||||
if ($existing instanceof Media) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
$localFile = $this->findLocalFile($imagesPath, $row['Image Src']);
|
||||
$localFile = $this->findLocalFile($imagesPath, $imageSrc);
|
||||
|
||||
if ($localFile === null) {
|
||||
Log::warning('Shopify import: image file not found', [
|
||||
'handle' => $handle,
|
||||
'image_src' => $row['Image Src'],
|
||||
'image_src' => $imageSrc,
|
||||
]);
|
||||
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$media = $this->assetResolver->resolve($product, $localFile, $position);
|
||||
|
||||
if ($media) {
|
||||
ImportMapping::record(self::SOURCE, 'image', $externalId, $product);
|
||||
ImportMapping::record(self::SOURCE, 'image', $externalId, $media);
|
||||
}
|
||||
|
||||
return $media;
|
||||
}
|
||||
|
||||
private function findLocalFile(string $imagesPath, string $imageSrc): ?string
|
||||
|
||||
Reference in New Issue
Block a user