src/Service/ZpriceHelper.php line 113

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Entity\Product;
  4. use App\Entity\Project;
  5. use App\Entity\User;
  6. use App\Entity\UserProject;
  7. use App\Repository\BrandRepository;
  8. use App\Repository\ProductRepository;
  9. use App\Repository\ProjectRepository;
  10. use App\Repository\RoleRepository;
  11. use Aws\S3\S3Client;
  12. use Doctrine\DBAL\Connection;
  13. use Doctrine\DBAL\Exception;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Doctrine\ORM\QueryBuilder;
  16. use Doctrine\Persistence\ManagerRegistry;
  17. use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
  18. use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
  19. use Symfony\Component\HttpFoundation\RequestStack;
  20. use Symfony\Component\HttpKernel\KernelInterface;
  21. use Symfony\Component\Security\Core\Security;
  22. class ZpriceHelper
  23. {
  24.     private Security $security;
  25.     private RequestStack $requestStack;
  26.     private ProjectRepository $projectRepository;
  27.     private RoleRepository $roleRepository;
  28.     private EntityManagerInterface $manager;
  29.     private BrandRepository $brandRepository;
  30.     private ProductRepository $productRepository;
  31.     private KernelInterface $kernel;
  32.     private TokenService $tokenService;
  33.     private Connection $connection;
  34.     public function __construct(
  35.         Security $security,
  36.         RequestStack $requestStack,
  37.         ProjectRepository $projectRepository,
  38.         RoleRepository $roleRepository,
  39.         EntityManagerInterface $manager,
  40.         BrandRepository $brandRepository,
  41.         ProductRepository $productRepository,
  42.         KernelInterface $kernel,
  43.         TokenService $tokenService,
  44.         Connection $connection,
  45.     ) {
  46.         $this->security $security;
  47.         $this->requestStack $requestStack;
  48.         $this->projectRepository $projectRepository;
  49.         $this->roleRepository $roleRepository;
  50.         $this->manager $manager;
  51.         $this->brandRepository $brandRepository;
  52.         $this->productRepository $productRepository;
  53.         $this->kernel $kernel;
  54.         $this->tokenService $tokenService;
  55.         $this->connection $connection;
  56.     }
  57.     /**
  58.      * Vérifie les roles de l'utilisateur sur le projet dans le token.
  59.      *
  60.      * @throws JWTDecodeFailureException
  61.      */
  62.     public function roleChecker(ManagerRegistry $doctrine$role): void
  63.     {
  64.         // vérif du projet, ignorée pour les supers admins
  65.         if (!$this->security->isGranted('ROLE_SUPER_ADMIN')) {
  66.             $user $doctrine->getRepository(User::class)->findBy(['email' => $this->security->getUser()->getEmail()]);
  67.             $project $this->getCurrentProject();
  68.             $userproject $doctrine
  69.                 ->getRepository(UserProject::class)
  70.                 ->findBy(['user' => $user'project' => $project]);
  71.             if (!$userproject) {
  72.                 throw new AccessDeniedException('No right on this project');
  73.             }
  74.         }
  75.         if (!$this->security->isGranted($role)) {
  76.             throw new AccessDeniedException('Not enough right');
  77.         }
  78.     }
  79.     /**
  80.      * Retourne le projet courant de l'utilisateur.
  81.      *
  82.      * @throws JWTDecodeFailureException
  83.      */
  84.     public function getCurrentProject(): ?Project
  85.     {
  86.         $session $this->requestStack->getSession();
  87.         if (null != $this->requestStack->getCurrentRequest()->get('projectId')) {
  88.             $projectId $this->requestStack->getCurrentRequest()->get('projectId');
  89.         } elseif (null != $this->requestStack->getCurrentRequest()->getContent()) {
  90.             $data json_decode($this->requestStack->getCurrentRequest()->getContent());
  91.             if (isset($data->projectId)) {
  92.                 $projectId $data->projectId;
  93.                 $session->set('projectId'$projectId);
  94.             }
  95.         }
  96.         if (empty($projectId)) {
  97.             $projectId $this->tokenService->getProjectFromToken();
  98.         }
  99.         if (empty($projectId)) {
  100.             $projectId $session->get('projectId');
  101.         }
  102.         $session->set('projectId'$projectId);
  103.         if (empty($projectId)) {
  104.             return null;
  105.         }
  106.         $project $this->projectRepository->find($projectId);
  107.         if (!$project) {
  108.             return null;
  109.         }
  110.         return $project;
  111.     }
  112.     public function getImageExtensionFromMime($mimeType): ?string
  113.     {
  114.         $mimeMap = [
  115.             'image/jpeg' => 'jpg',
  116.             'image/png' => 'png',
  117.             'image/gif' => 'gif',
  118.             'image/webp' => 'webp',
  119.             'image/bmp' => 'bmp',
  120.             'image/tiff' => 'tiff',
  121.         ];
  122.         if (array_key_exists($mimeType$mimeMap)) {
  123.             return $mimeMap[$mimeType];
  124.         }
  125.         return null;
  126.     }
  127.     public function getAvailableRoles(): iterable
  128.     {
  129.         $roles $this->roleRepository->findAll();
  130.         $returnTab = [];
  131.         foreach ($roles as $role) {
  132.             $returnTab[$role->getName()] = $role->getConstante();
  133.         }
  134.         return $returnTab;
  135.     }
  136.     public function getAvailableProjectsQB(): callable
  137.     {
  138.         return function (QueryBuilder $qb) {
  139.             $alias $qb->getRootAliases()[0];
  140.             if (!$this->security->isGranted('ROLE_SUPER_ADMIN')) {
  141.                 $qb->where("$alias.id = :id")
  142.                     ->setParameter('id'$this->getCurrentProject()->getId());
  143.             }
  144.         };
  145.     }
  146.     /**
  147.      * @throws Exception
  148.      */
  149.     public function importFile($file$projectId): array
  150.     {
  151.         set_time_limit(0);
  152.         $batchSize 500;
  153.         $brands = [];
  154.         if (($handle fopen($file'r')) !== false) {
  155.             $i 0;
  156.             $photoCount 0;
  157.             $currentProject $this->projectRepository->find($projectId);
  158.             $re '/^(0+)/m';
  159.             while (($data fgetcsv($handle)) !== false) {
  160.                 $ean trim(preg_replace($re''$data[0]));
  161.                 $changes false;
  162.                 if (!empty($ean) && 'ean' != $ean) {
  163.                     $sql 'SELECT id,name,photo,photomini 
  164.                             FROM product 
  165.                             WHERE ean="'.$ean.'" 
  166.                                 AND project_id="'.$projectId.'"
  167.                     ';
  168.                     $stmt $this->connection->prepare($sql);
  169.                     $result $stmt->executeQuery();
  170.                     if (== $result->rowCount()) {
  171.                         $product = new Product();
  172.                         $product->setEan($ean);
  173.                         $product->setProject($currentProject);
  174.                         $product->setName($data[1]);
  175.                         $product->setPhoto(trim($data[3]));
  176.                         $product->setInCatalog(true);
  177.                         if (!empty($data[4])) {
  178.                             @$product->setPhotomini(base64_encode(file_get_contents($data[4])));
  179.                         }
  180.                         $data[2] = trim($data[2]);
  181.                         if (!empty($data[2])) {
  182.                             $product
  183.                                 ->setBrand(
  184.                                     !empty($brands[$data[2]]) ?
  185.                                         $brands[$data[2]] :
  186.                                         $this->brandRepository->findOneByOrCreate(['name' => $data[2]])
  187.                                 );
  188.                             if (empty($brands[$data[2]])) {
  189.                                 $brands[$data[2]] = $product->getBrand();
  190.                             }
  191.                         }
  192.                         $changes true;
  193.                     } else {
  194.                         $productRaw $result->fetchAllAssociative()[0];
  195.                         $productName trim($data[1]);
  196.                         if ($productRaw['name'] != $productName) {
  197.                             $changes true;
  198.                         }
  199.                         $photoFilename trim($data[3]);
  200.                         if ('' != $photoFilename) {
  201.                             if ($photoFilename != $productRaw['photo'] || empty($productRaw['photomini'])) {
  202.                                 $changes true;
  203.                                 $photomini null;
  204.                                 if ('' != $data[4]) {
  205.                                     @$photomini base64_encode(file_get_contents($data[4]));
  206.                                 }
  207.                             }
  208.                         }
  209.                         if ($changes) {
  210.                             $product $this->productRepository->find($productRaw['id']);
  211.                             $product->setName($productName);
  212.                             $product->setPhoto($photoFilename);
  213.                             if (isset($photomini)) {
  214.                                 $product->setPhotomini($photomini);
  215.                             }
  216.                         }
  217.                     }
  218.                     if ($changes) {
  219.                         $product->setLastUpdate(new \DateTime('now'));
  220.                         $this->manager->persist($product);
  221.                         ++$i;
  222.                     }
  223.                 }
  224.                 if (=== $i $batchSize) {
  225.                     $this->manager->flush();
  226.                     $brands = [];
  227.                 }
  228.             }
  229.             fclose($handle);
  230.             $currentProject->setLastUpdated(new \DateTime('now'));
  231.             $this->manager->flush();
  232.             $this->manager->clear();
  233.             return ['productCount' => $i 1'photoCount' => $photoCount];
  234.         } else {
  235.             return ['error' => "Can't find data"];
  236.         }
  237.     }
  238.     public function isCsvFile(string $filePath): bool
  239.     {
  240.         // Vérifier l'extension
  241.         $extension strtolower(pathinfo($filePathPATHINFO_EXTENSION));
  242.         if ('csv' !== $extension) {
  243.             return false;
  244.         }
  245.         // Vérifier le type MIME
  246.         $mimeType mime_content_type($filePath);
  247.         $allowedMimeTypes = [
  248.             'text/csv',
  249.             'text/plain',
  250.             'application/csv',
  251.             'application/vnd.ms-excel',
  252.         ];
  253.         if (!in_array($mimeType$allowedMimeTypestrue)) {
  254.             return false;
  255.         }
  256.         // Ouvrir le fichier et vérifier le format
  257.         if (($handle fopen($filePath'r')) !== false) {
  258.             $row fgetcsv($handle); // Lire la première ligne du fichier
  259.             fclose($handle);
  260.             if (false === $row) {
  261.                 return false;
  262.             }
  263.             // Vérifier que la première ligne contient plusieurs colonnes
  264.             if (count($row) < 2) { // On suppose qu'un CSV a au moins deux colonnes
  265.                 return false;
  266.             }
  267.             return true;
  268.         }
  269.         return false;
  270.     }
  271.     public function getDistance(
  272.         float $latitudeFrom,
  273.         float $longitudeFrom,
  274.         float $latitudeTo,
  275.         float $longitudeTo,
  276.         int $earthRadius 6371,
  277.     ): float|int {
  278.         // Convert degrees to radians
  279.         $latFrom deg2rad($latitudeFrom);
  280.         $lonFrom deg2rad($longitudeFrom);
  281.         $latTo deg2rad($latitudeTo);
  282.         $lonTo deg2rad($longitudeTo);
  283.         // Haversine formula
  284.         $latDelta $latTo $latFrom;
  285.         $lonDelta $lonTo $lonFrom;
  286.         $angle asin(sqrt(pow(sin($latDelta 2), 2) +
  287.                 cos($latFrom) * cos($latTo) * pow(sin($lonDelta 2), 2)));
  288.         return $angle $earthRadius;
  289.     }
  290.     /**
  291.      * @param mixed $photo base64 image file
  292.      *
  293.      * @return array [url, directory, filename, extension]
  294.      *
  295.      * @throws \Exception
  296.      */
  297.     public function storePhoto(mixed $photo): array
  298.     {
  299.         $img $photo->data;
  300.         $img preg_replace('#^data:image/\w+;base64,#i'''$img);
  301.         $img base64_decode($img);
  302.         $directory $this->kernel->getEnvironment().DIRECTORY_SEPARATOR.$_ENV['S3_DIRECTORY'];
  303.         $finfo = new \finfo(FILEINFO_MIME_TYPE);
  304.         $mimeType $finfo->buffer($img);
  305.         $bucket $_ENV['S3_BUCKET'];
  306.         $extension $this->getImageExtensionFromMime($mimeType);
  307.         $filename uniqid().'.'.$extension;
  308.         if (!$extension) {
  309.             throw new \Exception('Unknown image mime');
  310.         }
  311.         $s3client = new S3Client([
  312.             'version' => 'latest',
  313.             'region' => 'eu-west-3',
  314.             'credentials' => [
  315.                 'key' => $_ENV['S3_KEY'],
  316.                 'secret' => $_ENV['S3_SECRET'],
  317.             ],
  318.         ]);
  319.         $response $s3client->putObject([
  320.             'ACL' => 'public-read',
  321.             'Bucket' => $bucket,
  322.             'Key' => $directory.'/'.$filename,
  323.             'Body' => $img,
  324.             'ContentType' => $mimeType,
  325.         ]);
  326.         return [
  327.             'url' => $response->get('ObjectURL'),
  328.             'extension' => $extension,
  329.             'directory' => $directory,
  330.             'filename' => $filename,
  331.         ];
  332.     }
  333. }