addArgument('file', InputArgument::REQUIRED, 'Path to the workbook (XLSX)'); $this->addArgument('product', InputArgument::REQUIRED, 'Slug or uid of the target product'); $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Report only - nothing written'); } protected function execute(InputInterface $input, OutputInterface $output): int { Bootstrap::initializeBackendAuthentication(); $dryRun = (bool)$input->getOption('dry-run'); $file = (string)$input->getArgument('file'); if (!is_file($file)) { $output->writeln('File not found: ' . $file . ''); return Command::FAILURE; } $product = $this->loadProduct((string)$input->getArgument('product')); if ($product === null) { $output->writeln('No product record for "' . $input->getArgument('product') . '".'); return Command::FAILURE; } $uid = (int)$product['uid']; $output->writeln(sprintf('Target: uid %d "%s" (/%s)%s', $uid, $product['title'], $product['slug'], $dryRun ? ' - DRY RUN' : '')); $parsed = $this->reader->parse($file); $pageType = (string)($parsed['pageType'] ?? 'unknown'); if ($pageType !== 'product') { $output->writeln('Not a product workbook (pageType "' . $pageType . '") - nothing imported.'); return Command::FAILURE; } // Workbook URL vs record slug - warn only, same as the module. $url = trim((string)($parsed['meta']['url'] ?? ''), '/'); $slug = trim((string)$product['slug'], '/'); if ($url !== '' && $slug !== '' && !str_ends_with($url, $slug)) { $output->writeln(sprintf('URL mismatch: /%s (workbook) vs /%s (record) - check the target!', $url, $slug)); } $mapping = array_filter($this->mappingRepository->load('product_xlsx')['mapping'] ?? []); if ($mapping === []) { $mapping = ProductTextImportController::DEFAULT_MAPPING; } $data = []; foreach ($mapping as $field => $selector) { $value = $this->reader->valueFor($parsed, $selector); if ($value === null) { $output->writeln(sprintf('%-20s %-34s -> not in this file', $field, $selector)); continue; } $old = (string)($product[$field] ?? ''); if (trim($old) === trim($value)) { $output->writeln(sprintf('%-20s %-34s -> unchanged', $field, $selector)); continue; } $data[$field] = $value; $output->writeln(sprintf('%-20s %-34s -> WRITE (%d chars%s)', $field, $selector, mb_strlen($value), $old === '' ? ', was empty' : '')); } if ($data === []) { $output->writeln('All mapped fields are up to date - nothing to write.'); return Command::SUCCESS; } if ($dryRun) { $output->writeln(sprintf('DRY RUN - %d field(s) would be written.', count($data))); return Command::SUCCESS; } $dataHandler = GeneralUtility::makeInstance(DataHandler::class); $dataHandler->start([self::TABLE => [(string)$uid => $data]], []); $dataHandler->process_datamap(); if ($dataHandler->errorLog !== []) { foreach ($dataHandler->errorLog as $error) { $output->writeln('' . $error . ''); } return Command::FAILURE; } $this->storeWorkbook($uid, $parsed, basename($file)); $output->writeln(sprintf('%d field(s) written (%s). Workbook stored for the module. Flush the cache: vendor/bin/typo3 cache:flush', count($data), implode(', ', array_keys($data)))); return Command::SUCCESS; } /** @return array|null */ private function loadProduct(string $slugOrUid): ?array { $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE); $qb->select('*')->from(self::TABLE)->where($qb->expr()->eq('deleted', 0)); if (ctype_digit($slugOrUid)) { $qb->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter((int)$slugOrUid, ParameterType::INTEGER))); } else { $qb->andWhere($qb->expr()->eq('slug', $qb->createNamedParameter($slugOrUid, ParameterType::STRING))); } $row = $qb->executeQuery()->fetchAssociative(); return $row ?: null; } /** * Same storage the module upload uses, so "Edit Product" reopens on this * workbook (related-card review without re-upload). * * @param array $parsed */ private function storeWorkbook(int $productUid, array $parsed, string $filename): void { $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tx_vitec_product_workbook'); $values = [ 'filename' => mb_substr($filename, 0, 255), 'payload' => (string)json_encode($parsed, JSON_UNESCAPED_UNICODE), 'be_user' => 0, 'tstamp' => time(), ]; if ($connection->count('product_uid', 'tx_vitec_product_workbook', ['product_uid' => $productUid]) > 0) { $connection->update('tx_vitec_product_workbook', $values, ['product_uid' => $productUid]); } else { $connection->insert('tx_vitec_product_workbook', $values + ['product_uid' => $productUid]); } } }