Fix: Updating MIgrateImportCommand to accept language for import
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace Modules\Core\Command;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Lunar\Models\Language;
|
||||
use Modules\Core\MigrateImport\ImportSpec;
|
||||
use Modules\Core\MigrateImport\RunMigrateImportJob;
|
||||
|
||||
@@ -62,11 +63,29 @@ class MigrateImportCommand extends Command
|
||||
$credentials = null;
|
||||
}
|
||||
|
||||
// Shopify's own product export is a flat CSV — one Title/Body
|
||||
// (HTML)/etc. column per row, no per-locale columns at all — so
|
||||
// its text is necessarily written in exactly one language, and
|
||||
// there is no reliable way to detect which one from the file
|
||||
// itself. Modules\Core\MigrateImport\ImportLocale::code() used to
|
||||
// (as its former name, DefaultLocale, admits) assume it always
|
||||
// matched this store's own Lunar\Models\
|
||||
// Language::getDefault(), which is often wrong (a store's default
|
||||
// admin/storefront language and the language a given export
|
||||
// happens to be written in are two independent facts) — every
|
||||
// imported product's name/description then saved silently under
|
||||
// the wrong language, invisible unless that language happened to
|
||||
// also be selected when viewing/editing the product afterward.
|
||||
$locale = $source === 'shopify' && $type === 'export'
|
||||
? $this->askImportLocale()
|
||||
: null;
|
||||
|
||||
$spec = new ImportSpec(
|
||||
source: $source,
|
||||
type: $type,
|
||||
filePath: $filePath,
|
||||
credentials: $credentials,
|
||||
locale: $locale,
|
||||
);
|
||||
|
||||
RunMigrateImportJob::dispatch($spec);
|
||||
@@ -74,6 +93,26 @@ class MigrateImportCommand extends Command
|
||||
$this->info('Import queued.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Choices come from Language::all() — the same list an admin manages
|
||||
* from the Filament panel (Settings > Languages) — not a hardcoded
|
||||
* set, so a language this store doesn't have yet simply isn't
|
||||
* offered here; the hint below says where to add it instead of this
|
||||
* command silently accepting an arbitrary code Lunar has no row for.
|
||||
*/
|
||||
private function askImportLocale(): string
|
||||
{
|
||||
$languages = Language::orderBy('default', 'desc')->get(['code', 'name']);
|
||||
|
||||
return $this->choice(
|
||||
"Which language is the export file's own text (product titles, descriptions, etc.) written in?\n".
|
||||
' (Not necessarily this store\'s default language — the two are independent. '.
|
||||
"If the language you need isn't listed, add it first from the admin panel under Languages.)",
|
||||
$languages->mapWithKeys(fn (Language $language) => [$language->code => "{$language->name} ({$language->code})"])->all(),
|
||||
$languages->first()?->code,
|
||||
);
|
||||
}
|
||||
|
||||
// Answers are relative to storage/app/private/imports (e.g. "shopify" or
|
||||
// "shopify/products_export.csv"); absolute paths are used as-is. A
|
||||
// directory answer picks the first CSV file found inside it.
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\MigrateImport;
|
||||
|
||||
use Lunar\Models\Language;
|
||||
|
||||
class DefaultLocale
|
||||
{
|
||||
public static function code(): string
|
||||
{
|
||||
return Language::getDefault()->code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\MigrateImport;
|
||||
|
||||
use Lunar\Models\Language;
|
||||
|
||||
/**
|
||||
* The language every resolver in an import run writes product-facing text
|
||||
* under (name, description, option names, ...) — NOT necessarily this
|
||||
* store's own Lunar\Models\Language::getDefault(). An export file has no
|
||||
* reliable way to declare its own language, and a store's default admin/
|
||||
* storefront language is an independent fact from whatever language a
|
||||
* given export happens to be written in — conflating the two (this
|
||||
* class's own former name, DefaultLocale, said as much) used to silently
|
||||
* save every imported product's text under the wrong language, invisible
|
||||
* unless that language was also the one selected while viewing/editing
|
||||
* the product afterward.
|
||||
*
|
||||
* Set once per import run from the operator's own answer (see
|
||||
* Command\MigrateImportCommand::askImportLocale(), threaded through
|
||||
* ImportSpec::$locale) at the top of Importer::import() — every resolver
|
||||
* downstream (ProductAttributeResolver, ProductOptionResolver,
|
||||
* CollectionResolver, ImportAttributeResolver) calls code() exactly as
|
||||
* before, unaware anything changed. Falls back to Language::getDefault()
|
||||
* only when nothing was ever set (e.g. an import path with no locale
|
||||
* concept of its own — JudgeMe's reviews-only export never calls set()).
|
||||
*/
|
||||
class ImportLocale
|
||||
{
|
||||
private static ?string $code = null;
|
||||
|
||||
public static function set(string $code): void
|
||||
{
|
||||
self::$code = $code;
|
||||
}
|
||||
|
||||
public static function code(): string
|
||||
{
|
||||
return self::$code ?? Language::getDefault()->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* A static property outlives a single request only inside a
|
||||
* long-running worker process, where a queued job for one import
|
||||
* must not leak its locale into the next — called at the end of
|
||||
* every Importer::import() run, success or failure.
|
||||
*/
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$code = null;
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,22 @@ namespace Modules\Core\MigrateImport;
|
||||
|
||||
class ImportSpec
|
||||
{
|
||||
/**
|
||||
* @param ?string $locale The language the export file's own text
|
||||
* (product titles, descriptions, option names, ...) is actually
|
||||
* written in — asked of the operator at import time (see
|
||||
* Command\MigrateImportCommand), since an export has no reliable
|
||||
* way to declare its own language and it does not necessarily match
|
||||
* this store's Lunar\Models\Language::getDefault(). Null for a
|
||||
* source/type this doesn't apply to (e.g. an API-based import with
|
||||
* no free-text file to attribute a single language to).
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $source,
|
||||
public readonly string $type,
|
||||
public readonly ?string $filePath = null,
|
||||
public readonly ?array $credentials = null,
|
||||
public readonly ?string $locale = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use Lunar\FieldTypes\TranslatedText;
|
||||
use Lunar\Models\Collection;
|
||||
use Lunar\Models\CollectionGroup;
|
||||
use Lunar\Models\Product;
|
||||
use Modules\Core\MigrateImport\DefaultLocale;
|
||||
use Modules\Core\MigrateImport\ImportLocale;
|
||||
|
||||
class CollectionResolver
|
||||
{
|
||||
@@ -45,7 +45,7 @@ class CollectionResolver
|
||||
'collection_group_id' => $group->id,
|
||||
'attribute_data' => [
|
||||
'name' => new TranslatedText(collect([
|
||||
DefaultLocale::code() => new Text($name),
|
||||
ImportLocale::code() => new Text($name),
|
||||
])),
|
||||
],
|
||||
]);
|
||||
|
||||
@@ -8,7 +8,7 @@ use Lunar\Models\Attribute;
|
||||
use Lunar\Models\AttributeGroup;
|
||||
use Lunar\Models\Product;
|
||||
use Lunar\Models\ProductType;
|
||||
use Modules\Core\MigrateImport\DefaultLocale;
|
||||
use Modules\Core\MigrateImport\ImportLocale;
|
||||
|
||||
class ImportAttributeResolver
|
||||
{
|
||||
@@ -40,7 +40,7 @@ class ImportAttributeResolver
|
||||
[
|
||||
'attribute_group_id' => $group->id,
|
||||
'position' => $nextPosition++,
|
||||
'name' => [DefaultLocale::code() => $definition['label']],
|
||||
'name' => [ImportLocale::code() => $definition['label']],
|
||||
'section' => 'main',
|
||||
'type' => $definition['type'],
|
||||
'required' => false,
|
||||
@@ -49,7 +49,7 @@ class ImportAttributeResolver
|
||||
? ['richtext' => false]
|
||||
: [],
|
||||
'system' => false,
|
||||
'description' => [DefaultLocale::code() => ''],
|
||||
'description' => [ImportLocale::code() => ''],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ class ImportAttributeResolver
|
||||
return AttributeGroup::firstOrCreate(
|
||||
['attributable_type' => Product::morphName(), 'handle' => 'import'],
|
||||
[
|
||||
'name' => [DefaultLocale::code() => 'Additional Details'],
|
||||
'name' => [ImportLocale::code() => 'Additional Details'],
|
||||
'position' => 100,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ use Lunar\FieldTypes\Number;
|
||||
use Lunar\FieldTypes\Text;
|
||||
use Lunar\FieldTypes\TranslatedText;
|
||||
use Lunar\Models\ProductType;
|
||||
use Modules\Core\MigrateImport\DefaultLocale;
|
||||
use Modules\Core\MigrateImport\ImportLocale;
|
||||
|
||||
class ProductAttributeResolver
|
||||
{
|
||||
@@ -43,7 +43,7 @@ class ProductAttributeResolver
|
||||
}
|
||||
|
||||
return new TranslatedText(collect([
|
||||
DefaultLocale::code() => new Text($value),
|
||||
ImportLocale::code() => new Text($value),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Modules\Core\MigrateImport\Shopify\Resolvers;
|
||||
use Illuminate\Support\Str;
|
||||
use Lunar\Models\ProductOption;
|
||||
use Lunar\Models\ProductOptionValue;
|
||||
use Modules\Core\MigrateImport\DefaultLocale;
|
||||
use Modules\Core\MigrateImport\ImportLocale;
|
||||
|
||||
class ProductOptionResolver
|
||||
{
|
||||
@@ -24,8 +24,8 @@ class ProductOptionResolver
|
||||
return ProductOption::query()->firstOrCreate(
|
||||
['handle' => $handle],
|
||||
[
|
||||
'name' => [DefaultLocale::code() => $name],
|
||||
'label' => [DefaultLocale::code() => $name],
|
||||
'name' => [ImportLocale::code() => $name],
|
||||
'label' => [ImportLocale::code() => $name],
|
||||
'shared' => true,
|
||||
],
|
||||
);
|
||||
@@ -46,7 +46,7 @@ class ProductOptionResolver
|
||||
|
||||
return $existing ?? ProductOptionValue::create([
|
||||
'product_option_id' => $option->id,
|
||||
'name' => [DefaultLocale::code() => $value],
|
||||
'name' => [ImportLocale::code() => $value],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Lunar\Models\Language;
|
||||
use Lunar\Models\Product;
|
||||
use Lunar\Models\ProductVariant;
|
||||
use Lunar\Models\Url;
|
||||
use Modules\Core\MigrateImport\ImportLocale;
|
||||
use Modules\Core\MigrateImport\ImportSpec;
|
||||
use Modules\Core\MigrateImport\Importer;
|
||||
use Modules\Core\MigrateImport\Models\ImportMapping;
|
||||
@@ -48,16 +49,26 @@ class ShopifyExportImporter implements Importer
|
||||
|
||||
public function import(ImportSpec $spec): void
|
||||
{
|
||||
$groups = $this->csvReader->read($spec->filePath);
|
||||
$imagesPath = dirname($spec->filePath).'/files';
|
||||
$collectionGroup = CollectionGroup::firstOrCreate(
|
||||
['handle' => 'shopify'],
|
||||
['name' => 'Shopify'],
|
||||
);
|
||||
$currency = Currency::getDefault();
|
||||
if ($spec->locale !== null) {
|
||||
ImportLocale::set($spec->locale);
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$this->importProduct($group, $imagesPath, $collectionGroup, $currency);
|
||||
try {
|
||||
$groups = $this->csvReader->read($spec->filePath);
|
||||
$imagesPath = dirname($spec->filePath).'/files';
|
||||
$collectionGroup = CollectionGroup::firstOrCreate(
|
||||
['handle' => 'shopify'],
|
||||
['name' => 'Shopify'],
|
||||
);
|
||||
$currency = Currency::getDefault();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$this->importProduct($group, $imagesPath, $collectionGroup, $currency);
|
||||
}
|
||||
} finally {
|
||||
// A queue worker process outlives a single import run — this
|
||||
// must not leak into whatever's imported next.
|
||||
ImportLocale::reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +111,12 @@ class ShopifyExportImporter implements Importer
|
||||
[
|
||||
'element_type' => $product->getMorphClass(),
|
||||
'element_id' => $product->id,
|
||||
'language_id' => Language::getDefault()->id,
|
||||
// ImportLocale, not Language::getDefault() — same
|
||||
// reasoning as ProductAttributeResolver et al.: this
|
||||
// product's handle/slug came from the export, written in
|
||||
// whatever language the operator said the file is in,
|
||||
// not necessarily this store's own default language.
|
||||
'language_id' => Language::where('code', ImportLocale::code())->value('id'),
|
||||
],
|
||||
[
|
||||
'slug' => $group->handle,
|
||||
|
||||
Reference in New Issue
Block a user