src/Services/ImageService.php line 75

Open in your IDE?
  1. <?php
  2. namespace Slivki\Services;
  3. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Imagine\Exception\InvalidArgumentException;
  6. use Imagine\Gd\Font;
  7. use Imagine\Gd\Imagine;
  8. use Imagine\Image\Box;
  9. use Imagine\Image\Palette\RGB;
  10. use Imagine\Image\Point;
  11. use Slivki\Entity\Media;
  12. use Slivki\Entity\Media\AbstractMediaBase;
  13. use Slivki\Entity\Media\OfferAppTeaserMedia;
  14. use Slivki\Entity\Media\OfferMobileTeaserMedia;
  15. use Slivki\Entity\Media\OfferTeaserMedia;
  16. use Slivki\Entity\MediaSize;
  17. use Slivki\Entity\MediaType;
  18. use Slivki\Entity\Offer;
  19. use Slivki\Entity\SaleProduct;
  20. use Slivki\Util\SoftCache;
  21. use Symfony\Component\Filesystem\Filesystem;
  22. use Symfony\Component\HttpKernel\KernelInterface;
  23. class ImageService
  24. {
  25.     public const OFFER_TEASER_WIDTH 500;
  26.     public const OFFER_TEASER_HEIGHT 324;
  27.     public const OFFER_MOBILE_TEASER_WIDTH 764;
  28.     public const OFFER_MOBILE_TEASER_HEIGHT 382;
  29.     public const OFFER_APP_TEASER_WIDTH 500;
  30.     public const OFFER_APP_TEASER_HEIGHT 258;
  31.     const CACHE_NAME "media-1-0-";
  32.     const MEDIA_ROOT "/znijki-media/";
  33.     public const PUBLIC_DIR "/public";
  34.     const INITIAL_PATH "initial";
  35.     const BACKGROUNDS_PATH "backgrounds/";
  36.     const BACKGROUNDS_CERTIFICATES_PATH "backgrounds/certificates/";
  37.     const FALLBACK_IMAGE "/common-img/d.gif";
  38.     const DEFAULT_AVATAR "/images/default_avatar.png?v=2";
  39.     const IMAGE_URL_CACHE_EXPIRE 1800;
  40.     const CACHE_KEY "-url-1-0-";
  41.     private $mediaRepository;
  42.     private $entityManager;
  43.     private $webRoot;
  44.     private $baseUrl;
  45.     private Filesystem $filesystem;
  46.     public function __construct(
  47.         EntityManagerInterface $entityManager,
  48.         KernelInterface $kernel,
  49.         Filesystem $filesystem,
  50.         string $baseUrl
  51.     ) {
  52.         $this->mediaRepository $entityManager->getRepository(Media::class);
  53.         $this->entityManager $entityManager;
  54.         $this->webRoot $kernel->getProjectDir() . "/public";
  55.         $this->baseUrl $baseUrl;
  56.         $this->filesystem $filesystem;
  57.     }
  58.     public function getImageURLCached (AbstractMediaBase $media null$width$height) {
  59.         if (!$media) {
  60.             return self::FALLBACK_IMAGE;
  61.         }
  62.         $softCache = new SoftCache(self::CACHE_NAME);
  63.         $cacheKey $media->getID() . self::CACHE_KEY $width "x" $height;
  64.         $imageURLCached $softCache->get($cacheKey);
  65.         if ($imageURLCached) {
  66.             return $imageURLCached;
  67.         }
  68.         $imageURL $this->getImageURL($media$width$height);
  69.         if (!$imageURL) {
  70.             return self::FALLBACK_IMAGE;
  71.         }
  72.         $softCache->set($cacheKey$imageURLself::IMAGE_URL_CACHE_EXPIRE);
  73.         return $imageURL;
  74.     }
  75.     public function mediaCacheReload(AbstractMediaBase $media): void
  76.     {
  77.         $softCache = new SoftCache(self::CACHE_NAME);
  78.         $softCache->delete(sprintf('%s%s%s'$media->getID(), self::CACHE_KEY'0x0'));
  79.         foreach ($media->getSizes() as $mediaSize) {
  80.             $this->resizeImage($media$mediaSize->getWidth(), $mediaSize->getHeight());
  81.         }
  82.     }
  83.     public function getImageURLCachedWithDomain(?AbstractMediaBase $mediaint $widthint $height): string
  84.     {
  85.         return $this->baseUrl $this->getImageURLCached($media$width$height);
  86.     }
  87.     public function getFallbackImageURLWithDomain(): string
  88.     {
  89.         return $this->baseUrl self::FALLBACK_IMAGE;
  90.     }
  91.     public function getFullAvatarUrlByPath(string $path): string
  92.     {
  93.         return \sprintf('%s%s%s%s'$this->baseUrlself::MEDIA_ROOTself::INITIAL_PATH$path);
  94.     }
  95.     public function getImageURL(AbstractMediaBase $media$width$height) {
  96.         $media $this->entityManager->merge($media);
  97.         if ($width == && $height == 0) {
  98.             return self::MEDIA_ROOT self::INITIAL_PATH $media->getPath() . $media->getName();
  99.         }
  100.         $mediaSizes $media->getSizes();
  101.         $mediaSize null;
  102.         foreach ($mediaSizes->toArray() as $size) {
  103.             if ($size->getWidth() == $width && $size->getHeight() == $height) {
  104.                 $mediaSize $size;
  105.                 break;
  106.             }
  107.         }
  108.         if (!$mediaSize) {
  109.             $imageCreated false;
  110.             $mediaSize = new MediaSize();
  111.             $mediaSize->setWidth($width);
  112.             $mediaSize->setHeight($height);
  113.             $mediaSize->setMedia($media);
  114.             try {
  115.                 $this->entityManager->persist($mediaSize);
  116.                 $this->entityManager->flush($mediaSize);
  117.             } catch (UniqueConstraintViolationException $exception) {
  118.                 $imageCreated false;
  119.             }
  120.             if (!$imageCreated) {
  121.                 if (!$this->resizeImage($media$width$height)) {
  122.                     //return false;
  123.                     //$softCache = new SoftCache(self::CACHE_NAME);
  124.                     //$cacheKey = $media->getID() . "-url-" . $width . "x" . $height;
  125.                     //$softCache->set($cacheKey, self::FALLBACK_IMAGE, self::IMAGE_URL_CACHE_EXPIRE);
  126.                     //return self::FALLBACK_IMAGE;
  127.                 }
  128.             }
  129.         }
  130.         return $this->getMediaURL($media$width$height);
  131.     }
  132.     public function clearMediaUrlCache(AbstractMediaBase $media): void
  133.     {
  134.         $softCache = new SoftCache(self::CACHE_NAME);
  135.         $softCache->delete(sprintf('%d%s%dx%d'$media->getID(), self::CACHE_KEY00));
  136.         foreach ($media->getSizes() as $mediaSize) {
  137.             $softCache->delete(
  138.                 sprintf(
  139.                     '%d%s%dx%d',
  140.                     $media->getID(),
  141.                     self::CACHE_KEY,
  142.                     $mediaSize->getWidth(),
  143.                     $mediaSize->getHeight(),
  144.                 )
  145.             );
  146.         }
  147.     }
  148.     public function resizeImage(AbstractMediaBase $media$width$height) {
  149.         $mediaPathNew $this->getMediaPath($media$width$height);
  150.         $this->makeMediaPath($mediaPathNew);
  151.         $originalMediaPath $this->webRoot self::MEDIA_ROOT self::INITIAL_PATH $media->getPath() . $media->getName();
  152.         if ($width == && $height == 0) {
  153.             $fileSystem = new Filesystem();
  154.             if (!$fileSystem->exists($originalMediaPath)) {
  155.                 return false;
  156.             }
  157.             $fileSystem->copy($originalMediaPath$this->getFullMediaPath($media$width$height));
  158.             return true;
  159.         }
  160.         try {
  161.             $imagine = new Imagine();
  162.             $image $imagine->open($originalMediaPath);
  163.             $errorReporting error_reporting(E_ERROR);
  164.             $exifData exif_read_data($originalMediaPath);
  165.             $rotateValue 0;
  166.             if ($exifData !== false && isset($exifData['Orientation'])) {
  167.                 switch ($exifData['Orientation']) {
  168.                     case 8:
  169.                         $rotateValue = -90;
  170.                         break;
  171.                     case 3:
  172.                         $rotateValue 180;
  173.                         break;
  174.                     case 6:
  175.                         $rotateValue 90;
  176.                         break;
  177.                 }
  178.                 if ($rotateValue != 0) {
  179.                     $image->rotate($rotateValue);
  180.                 }
  181.             }
  182.             error_reporting($errorReporting);
  183.             if ($width == 0) {
  184.                 $sourceImageSize $image->getSize();
  185.                 $newImageSize $sourceImageSize->heighten($height);
  186.             } else if ($height == 0) {
  187.                 $sourceImageSize $image->getSize();
  188.                 $newImageSize $sourceImageSize->widen($width);
  189.             } else {
  190.                 $newImageSize = new Box($width$height);
  191.             }
  192.             $image->resize($newImageSize);
  193.             $fileName $this->getFullMediaPath($media$width$height);
  194.             $image->save($fileName);
  195.             if ($media instanceof OfferTeaserMedia || $media instanceof OfferMobileTeaserMedia || $media instanceof OfferAppTeaserMedia) {
  196.                 $tinifyResult $this->tinify($fileName);
  197.                 if (isset($tinifyResult['output']['url'])) {
  198.                     copy($tinifyResult['output']['url'], $fileName);
  199.                 }
  200.             }
  201.         } catch (InvalidArgumentException $exception) {
  202.             return false;
  203.         }
  204.         $softCache = new SoftCache(self::CACHE_NAME);
  205.         $cacheKey $media->getID() . self::CACHE_KEY $width "x" $height;
  206.         $softCache->delete($cacheKey);
  207.         return true;
  208.     }
  209.     private function getMediaPath(AbstractMediaBase $media$width$height) {
  210.         return "w" $width "_" $height $media->getPath();
  211.     }
  212.     public function getFullMediaPath(AbstractMediaBase $media$width$height) {
  213.         return $this->webRoot self::MEDIA_ROOT $this->getMediaPath($media$width$height) . $media->getName();
  214.     }
  215.     public function saveMailingTeaserImage(Offer $offer) {
  216.         $width 500;
  217.         $height 324;
  218.         $captionHeight 60;
  219.         $teaserMedia $offer->getTeaserMedia();
  220.         if (!$teaserMedia) {
  221.             return false;
  222.         }
  223.         $initialMediaPath $this->webRoot self::MEDIA_ROOT self::INITIAL_PATH $teaserMedia->getPath();
  224.         $teaserMediaName $teaserMedia->getName();
  225.         if (!file_exists($initialMediaPath $teaserMediaName)) {
  226.             return false;
  227.         }
  228.         $imagine = new Imagine();
  229.         $image $imagine->open($initialMediaPath $teaserMediaName);
  230.         $newImageSize = new Box($width$height);
  231.         $image->resize($newImageSize);
  232.         if ($offer->getMailingTeaserMedia()) {
  233.             $newFileName $offer->getMailingTeaserMedia()->getName();
  234.         } else {
  235.             $newFileName 'm_' $teaserMediaName;
  236.             $fs = new Filesystem();
  237.             while ($fs->exists($initialMediaPath$newFileName)) {
  238.                 $newFileName time() . '_' $newFileName;
  239.             }
  240.         }
  241.         $caption $offer->getCaptionName();
  242.         if ($caption) {
  243.             $palette = new RGB();
  244.             $captionFont = new Font('/home/virtwww/slivki/public/fonts/roboto/Roboto-Bold.ttf' 28,  $palette->color('#ffffff'));
  245.             $caption mb_strtoupper($caption);
  246.             $captionBox $captionFont->box($caption);
  247.             while ($captionBox->getWidth() > $width 20) {
  248.                 $caption str_replace('...'''$caption);
  249.                 $caption mb_substr($caption0, -1) . '...';
  250.                 $captionBox $captionFont->box($caption);
  251.             }
  252.             $captionCoordinates = [
  253.                 new Point(0$height $captionHeight),
  254.                 new Point($width$height $captionHeight),
  255.                 new Point($width,  $height),
  256.                 new Point(0$height),
  257.             ];
  258.             $image->draw()->polygon($captionCoordinates$palette->color($offer->getCaptionColor()), true);
  259.             $image->draw()->text($caption$captionFont, new Point((int)($width/$captionBox->getWidth()/2), $height 46));
  260.         }
  261.         $image->save($initialMediaPath $newFileName, ['jpeg_quality' => 100]);
  262.         return $newFileName;
  263.     }
  264.     public function tinify($fileName) {
  265.         $apiKey 'J7BW6n1n3gN3VmQ5rC8mqcnKdf4lsrJq';
  266.         $url 'https://api.tinify.com/shrink';
  267.         $curl curl_init();
  268.         curl_setopt($curlCURLOPT_RETURNTRANSFERtrue);
  269.         curl_setopt($curlCURLOPT_USERPWD'api:' $apiKey);
  270.         curl_setopt($curlCURLOPT_POSTFIELDSfile_get_contents($fileName));
  271.         curl_setopt($curlCURLOPT_URL$url);
  272.         $result curl_exec($curl);
  273.         curl_close($curl);
  274.         if (!$result) {
  275.             return false;
  276.         }
  277.         $result json_decode($resulttrue);
  278.         return $result;
  279.     }
  280.     public function saveSaleProductMailingTeaserImage(SaleProduct $saleProduct) {
  281.         $width 524;
  282.         $height 340;
  283.         $padding 20;
  284.         $suplpierLogoHeight 60;
  285.         $teaserMedia $saleProduct->getTeaserMedia();
  286.         if (!$teaserMedia) {
  287.             return false;
  288.         }
  289.         $initialMediaPath $this->webRoot self::MEDIA_ROOT self::INITIAL_PATH $teaserMedia->getPath();
  290.         $teaserMediaName $teaserMedia->getName();
  291.         if (!file_exists($initialMediaPath $teaserMediaName)) {
  292.             return false;
  293.         }
  294.         $imagine = new Imagine();
  295.         $image $imagine->open($initialMediaPath $teaserMediaName);
  296.         $newImageSize = new Box($width$height);
  297.         $image->resize($newImageSize);
  298.         if ($saleProduct->getMailingTeaserMedia()) {
  299.             $newFileName $saleProduct->getMailingTeaserMedia()->getName();
  300.         } else {
  301.             $newFileName 'm_' $teaserMediaName;
  302.             $fs = new Filesystem();
  303.             while ($fs->exists($initialMediaPath$newFileName)) {
  304.                 $newFileName time() . '_' $newFileName;
  305.             }
  306.         }
  307.         if ($saleProduct->getRegularPrice() > 0) {
  308.             $palette = new RGB();
  309.             $discountLabelFont = new Font('/home/virtwww/slivki/public/fonts/roboto/Roboto-Bold.ttf' 28,  $palette->color('#333333'));
  310.             $discountLabelBgColor $palette->color('#6747e5');
  311.             $discountLabelText '-' round($saleProduct->getDiscountPercent(), 0) . '%';
  312.             $discountLabelBox $discountLabelFont->box($discountLabelText);
  313.             $discountLabelCoordinates = [
  314.                 new Point($width $discountLabelBox->getWidth() - $padding0),
  315.                 new Point($width0),
  316.                 new Point($width,  $discountLabelBox->getHeight() + $padding),
  317.                 new Point($width $discountLabelBox->getWidth() - $padding$discountLabelBox->getHeight() + $padding),
  318.             ];
  319.             $image->draw()->polygon($discountLabelCoordinates$discountLabelBgColortrue);
  320.             $discountLabelPosition = new Point($width $discountLabelBox->getWidth() - $padding$padding);
  321.             $image->draw()->text($discountLabelText$discountLabelFont$discountLabelPosition);
  322.         }
  323.         $supplierLogo $this->entityManager->getRepository(Media::class)
  324.             ->getMedia($saleProduct->getSaleVersion()->getSale()->getID(), MediaType::TYPE_SALE_FLIER_LOGO_ID);
  325.         if ($supplierLogo) {
  326.             $supplierLogo $supplierLogo[0];
  327.             $supplierLogoImageFile $this->webRoot self::MEDIA_ROOT self::INITIAL_PATH $supplierLogo->getPath() . $supplierLogo->getName();
  328.             if (file_exists($supplierLogoImageFile)) {
  329.                 $supplierLogoImage $imagine->open($supplierLogoImageFile);
  330.                 $imageSize $supplierLogoImage->getSize();
  331.                 $newSize $imageSize->heighten($suplpierLogoHeight);
  332.                 $supplierLogoImage->resize($newSize);
  333.                 $image->paste($supplierLogoImage, new Point(1010));
  334.             }
  335.         }
  336.         $image->save($initialMediaPath $newFileName, ['jpeg_quality' => 100]);
  337.         return $newFileName;
  338.     }
  339.     private function getMediaURL(AbstractMediaBase $media$width$height) {
  340.         return self::MEDIA_ROOT $this->getMediaPath($media$width$height) . $media->getName();
  341.     }
  342.     private function makeMediaPath($mediaPath) {
  343.         $fileSystem = new Filesystem();
  344.         if ($fileSystem->exists($this->webRoot self::MEDIA_ROOT $mediaPath)) {
  345.             return;
  346.         }
  347.         $folders explode("/"$mediaPath);
  348.         $mediaPath $this->webRoot self::MEDIA_ROOT;
  349.         foreach ($folders as $folder) {
  350.             if ($folder == "") {
  351.                 continue;
  352.             }
  353.             $mediaPath .= $folder;
  354.             if (!$fileSystem->exists($mediaPath)) {
  355.                 $fileSystem->mkdir($mediaPath);
  356.             }
  357.             $mediaPath .= "/";
  358.         }
  359.     }
  360.     public function deleteMediaImages(AbstractMediaBase $media): void
  361.     {
  362.         $initialMediaPath sprintf(
  363.             '%s%s%s%s%s',
  364.             $this->webRoot,
  365.             self::MEDIA_ROOT,
  366.             self::INITIAL_PATH,
  367.             $media->getPath(),
  368.             $media->getName()
  369.         );
  370.         foreach ($media->getSizes() as $mediaSize) {
  371.             $path $this->getFullMediaPath($media,  $mediaSize->getWidth(), $mediaSize->getHeight());
  372.             $this->filesystem->remove($path);
  373.         }
  374.         $this->filesystem->remove($initialMediaPath);
  375.     }
  376.     public function getBaseUrlWithDomain(): string
  377.     {
  378.         return $this->baseUrl
  379.     }
  380. }