getId(); if ($brandId <= 0) { throw new LocalizedException(__('Please save the brand before importing specifications.')); } $zip = new \ZipArchive(); $absoluteArchivePath = $this->media->getAbsolutePath($archivePath); if ($zip->open($absoluteArchivePath) !== true) { throw new LocalizedException(__('Unable to open the ZIP file.')); } $this->deleteExistingBrandData($brandId); $import = $this->importFactory->create(); $import->setData([ 'brand_id' => $brandId, 'archive_file' => $archivePath, 'file_count' => 0, 'status' => 1, ])->save(); $brandName = (string)$brand->getData('brand_name'); $normalizedBrandName = $this->normalizeName($brandName); $fileCount = 0; for ($index = 0; $index < $zip->numFiles; $index++) { $stat = $zip->statIndex($index); $entryName = (string)($stat['name'] ?? ''); if ($this->shouldSkipEntry($entryName)) { continue; } $parts = array_values(array_filter(explode('/', trim($entryName, '/')), static fn (string $part): bool => $part !== '')); if (count($parts) < 3) { continue; } $fileName = array_pop($parts); $modelName = (string)array_pop($parts); $entryBrandName = (string)array_pop($parts); if ($this->normalizeName($entryBrandName) !== $normalizedBrandName) { continue; } $contents = $zip->getFromIndex($index); if ($contents === false) { continue; } $lowerFileName = strtolower($fileName); $relativePath = $this->media->writeSpecificationFile($brandId, $modelName, $lowerFileName, $contents); $file = $this->fileFactory->create(); $file->setData([ 'import_id' => (int)$import->getId(), 'brand_id' => $brandId, 'brand_name' => $brandName, 'brand_logo' => (string)$brand->getData('brand_logo'), 'model_name' => $modelName, 'file_name' => $lowerFileName, 'file_path' => $relativePath, 'product_name_key' => $this->buildProductNameKey($lowerFileName), 'is_product_specific' => (int)$brand->getData('is_product_specific') === 1 ? 1 : 0, 'file_size' => strlen($contents), 'file_mtime' => !empty($stat['mtime']) ? date('Y-m-d H:i:s', (int)$stat['mtime']) : null, ]); $this->fileResource->save($file); $fileCount++; } $zip->close(); $import->setData('file_count', $fileCount)->save(); if ($fileCount === 0) { $import->delete(); $this->media->deleteBrandParsedFiles($brandId); throw new LocalizedException(__('No specification files were found for brand "%1" in this ZIP file.', $brandName)); } return $import; } public function buildProductNameKey(string $fileName): string { $withoutExtension = pathinfo(strtolower($fileName), PATHINFO_FILENAME); $key = preg_replace('/[^a-z0-9]+/', ' ', $withoutExtension) ?: ''; $key = preg_replace('/\\s+/', ' ', $key) ?: ''; return trim($key); } public function buildProductSearchTokens(string $productName): array { $normalized = $this->buildProductNameKey($productName); $tokens = []; if ($normalized !== '') { $tokens[$normalized] = $normalized; $parts = preg_split('/\\s+/', $normalized) ?: []; for ($length = count($parts); $length >= 1; $length--) { for ($start = 0; $start <= count($parts) - $length; $start++) { $token = implode(' ', array_slice($parts, $start, $length)); if (strlen($token) >= 3) { $tokens[$token] = $token; } } } } return array_values($tokens); } private function deleteExistingBrandData(int $brandId): void { foreach ($this->importCollectionFactory->create()->addFieldToFilter('brand_id', $brandId) as $import) { $import->delete(); } foreach ($this->fileCollectionFactory->create()->addFieldToFilter('brand_id', $brandId) as $file) { $file->delete(); } $this->media->deleteBrandParsedFiles($brandId); } private function shouldSkipEntry(string $entryName): bool { $entryName = trim($entryName); if ($entryName === '' || str_ends_with($entryName, '/')) { return true; } $parts = explode('/', $entryName); $basename = end($parts); return str_contains($entryName, '__MACOSX/') || $basename === '.DS_Store' || str_starts_with((string)$basename, '._'); } private function normalizeName(string $value): string { $value = strtolower(trim($value)); $value = preg_replace('/[^a-z0-9]+/', '', $value) ?: ''; return $value; } }