233 lines
8.3 KiB
PHP
233 lines
8.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\HannahBundle\Resources\script;
|
|
|
|
use Doctrine\DBAL\Exception\DeadlockException;
|
|
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
use External\HannahBundle\Util\FTP\FTPClient;
|
|
use External\HannahBundle\Util\ProductUtil;
|
|
use KupShop\AdminBundle\Util\Script\Script;
|
|
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
|
use KupShop\KupShopBundle\Util\System\PathFinder;
|
|
use Psr\Log\LoggerInterface;
|
|
use Query\Operator;
|
|
|
|
class ImportPhotosScript extends Script
|
|
{
|
|
protected static $name = 'Importovat fotky k produktům z FTP';
|
|
protected static $defaultParameters = [
|
|
'productIds' => [],
|
|
// 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;
|