[], // Pokud true, tak se doimportuji chybejici obrazky 'update' => false, ]; protected function run(array $arguments) { $productUtil = ServiceContainer::getService(ProductUtil::class); $qb = sqlQueryBuilder() ->select('p.id, p.code, JSON_VALUE(p.data, "$.photosHash") as photosHash, COUNT(ppr.id_photo) as photosCount') ->from('products', 'p') ->leftJoin('p', 'photos_products_relation', 'ppr', 'p.id = ppr.id_product') // ignore outfits ->andWhere(Operator::not(Operator::findInSet(['OF'], 'p.campaign'))) ->groupBy('p.id'); // ignore outfits if labels are used if ($outfitLabelId = $productUtil->getLabelByCode('OF')) { $qb->leftJoin('p', 'product_labels_relation', 'plr', 'plr.id_product = p.id AND plr.id_label = :outfitLabelId') ->andWhere('plr.id_label IS NULL') ->setParameter('outfitLabelId', $outfitLabelId); } if (!($arguments['update'] ?? false)) { $qb->andWhere('ppr.id_photo IS NULL'); } if ($arguments['productIds'] ?? false) { $qb->andWhere(Operator::inIntArray((array) $arguments['productIds'], 'p.id')); } foreach ($qb->execute() as $item) { if (empty($this->getPhotoDownloader()->getPhotosCache())) { // pravdepodobne se nepodarilo naloadovat obrazky z FTP, tak at neodeberu vsem produktum fotky continue; } $originalCode = $item['code']; $code = $originalCode; // pokud ma kod 11 znaku, tak zkusim ke kodu doplnit 00, aby mel 13 znaku if (strlen($originalCode) == 11) { $code .= '00'; } // zkusim najit fotky podle 13 mistneho kodu if (!($photos = $this->getPhotoDownloader()->getProductPhotos($code))) { // pokud nenajdu fotky, tak zkusim najit fotky podle originalniho kodu if (!($photos = $this->getPhotoDownloader()->getProductPhotos($originalCode))) { // pokud nenajdu fotky ani podle originalniho kodu, tak zkusim najit fotky podle originalniho kodu + 01 na konci $photos = $this->getPhotoDownloader()->getProductPhotos($originalCode.'01'); } } $photosHash = md5(implode('-', $photos)); $invalidHash = !empty($photos) && $item['photosCount'] <= 0 && !empty($item['photosHash']); // Stejny fotky, nemam co aktualizovat if ($photosHash === $item['photosHash'] && !$invalidHash) { continue; } $this->withRetryStrategy(fn () => $this->updatePhotos((int) $item['id'], $photos)); // Ulozit photosHash sqlQueryBuilder() ->update('products') ->set('data', 'JSON_SET(COALESCE(data, "{}"), "$.photosHash", :value)') ->setParameter('value', $photosHash) ->where(Operator::equals(['id' => $item['id']])) ->execute(); } } private function updatePhotos(int $productId, array $photos): void { $photoRelations = []; foreach ($photos as $index => $photo) { $dest = $this->getPathFinder()->dataPath('photos/old'.$photo); $pathInfo = pathinfo($dest); if (!file_exists($pathInfo['dirname'])) { mkdir($pathInfo['dirname'], 0777, true); } $photoSource = trim(str_replace('data/photos/', '', $pathInfo['dirname']), '/').'/'; $photoName = $pathInfo['basename']; if (file_exists($dest) || $this->getPhotoDownloader()->copyFile($photo, $dest)) { $photoId = sqlQueryBuilder() ->select('id') ->from('photos') ->where(Operator::equals( [ 'filename' => $photoName, 'source' => $photoSource, ] )) ->execute()->fetchOne(); if (!$photoId) { $photoId = sqlGetConnection()->transactional(function () use ($photoName, $photoSource) { sqlQueryBuilder() ->insert('photos') ->directValues( [ 'filename' => $photoName, 'source' => $photoSource, 'image_2' => $photoName, 'date' => (new \DateTime())->format('Y-m-d H:i:s'), ] )->execute(); return (int) sqlInsertId(); }); } // Zarazeni provedu az pozdeji $photoRelations[$photoId] = $index; } } // pokud u produktu podle FTP nemam zadne fotky, tak nic nedelam, abych produktu nesmazal fotky if (empty($photoRelations)) { $this->getLogger()->notice('[OC] Empty photos update', [ 'productId' => $productId, 'photos' => $photos, 'photoRelations' => $photoRelations, ]); return; } // Aktualizovat photos_products_relation sqlGetConnection()->transactional(function () use ($photoRelations, $productId) { sqlQueryBuilder() ->delete('photos_products_relation') ->where(Operator::equals(['id_product' => $productId])) ->execute(); foreach ($photoRelations as $photoId => $position) { try { sqlQueryBuilder() ->insert('photos_products_relation') ->directValues( [ 'id_photo' => $photoId, 'id_product' => $productId, 'id_variation' => null, 'show_in_lead' => $position === 0 ? 'Y' : 'N', 'position' => $position, 'date_added' => (new \DateTime())->format('Y-m-d H:i:s'), ] )->execute(); } catch (UniqueConstraintViolationException $e) { } } }); } private function withRetryStrategy(callable $fn, int $maxTries = 5, int $currentTry = 0): void { try { $fn(); } catch (LockWaitTimeoutException|DeadlockException $e) { if ($currentTry >= $maxTries) { return; } sleep(1); $this->withRetryStrategy($fn, $maxTries, ++$currentTry); } } private function getPathFinder(): PathFinder { static $pathFinder; if (!$pathFinder) { $pathFinder = ServiceContainer::getService(PathFinder::class); } return $pathFinder; } private function getLogger(): LoggerInterface { static $logger; if (!$logger) { $logger = ServiceContainer::getService('logger'); } return $logger; } private function getPhotoDownloader(): FTPClient { static $photoDownloader; if (!$photoDownloader) { $photoDownloader = ServiceContainer::getService(FTPClient::class); } return $photoDownloader; } } return ImportPhotosScript::class;