<?php
namespace App\Service;
use App\Entity\Product;
use App\Entity\Project;
use App\Entity\User;
use App\Entity\UserProject;
use App\Repository\BrandRepository;
use App\Repository\ProductRepository;
use App\Repository\ProjectRepository;
use App\Repository\RoleRepository;
use Aws\S3\S3Client;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Security\Core\Security;
class ZpriceHelper
{
private Security $security;
private RequestStack $requestStack;
private ProjectRepository $projectRepository;
private RoleRepository $roleRepository;
private EntityManagerInterface $manager;
private BrandRepository $brandRepository;
private ProductRepository $productRepository;
private KernelInterface $kernel;
private TokenService $tokenService;
private Connection $connection;
public function __construct(
Security $security,
RequestStack $requestStack,
ProjectRepository $projectRepository,
RoleRepository $roleRepository,
EntityManagerInterface $manager,
BrandRepository $brandRepository,
ProductRepository $productRepository,
KernelInterface $kernel,
TokenService $tokenService,
Connection $connection,
) {
$this->security = $security;
$this->requestStack = $requestStack;
$this->projectRepository = $projectRepository;
$this->roleRepository = $roleRepository;
$this->manager = $manager;
$this->brandRepository = $brandRepository;
$this->productRepository = $productRepository;
$this->kernel = $kernel;
$this->tokenService = $tokenService;
$this->connection = $connection;
}
/**
* Vérifie les roles de l'utilisateur sur le projet dans le token.
*
* @throws JWTDecodeFailureException
*/
public function roleChecker(ManagerRegistry $doctrine, $role): void
{
// vérif du projet, ignorée pour les supers admins
if (!$this->security->isGranted('ROLE_SUPER_ADMIN')) {
$user = $doctrine->getRepository(User::class)->findBy(['email' => $this->security->getUser()->getEmail()]);
$project = $this->getCurrentProject();
$userproject = $doctrine
->getRepository(UserProject::class)
->findBy(['user' => $user, 'project' => $project]);
if (!$userproject) {
throw new AccessDeniedException('No right on this project');
}
}
if (!$this->security->isGranted($role)) {
throw new AccessDeniedException('Not enough right');
}
}
/**
* Retourne le projet courant de l'utilisateur.
*
* @throws JWTDecodeFailureException
*/
public function getCurrentProject(): ?Project
{
$session = $this->requestStack->getSession();
if (null != $this->requestStack->getCurrentRequest()->get('projectId')) {
$projectId = $this->requestStack->getCurrentRequest()->get('projectId');
} elseif (null != $this->requestStack->getCurrentRequest()->getContent()) {
$data = json_decode($this->requestStack->getCurrentRequest()->getContent());
if (isset($data->projectId)) {
$projectId = $data->projectId;
$session->set('projectId', $projectId);
}
}
if (empty($projectId)) {
$projectId = $this->tokenService->getProjectFromToken();
}
if (empty($projectId)) {
$projectId = $session->get('projectId');
}
$session->set('projectId', $projectId);
if (empty($projectId)) {
return null;
}
$project = $this->projectRepository->find($projectId);
if (!$project) {
return null;
}
return $project;
}
public function getImageExtensionFromMime($mimeType): ?string
{
$mimeMap = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
'image/webp' => 'webp',
'image/bmp' => 'bmp',
'image/tiff' => 'tiff',
];
if (array_key_exists($mimeType, $mimeMap)) {
return $mimeMap[$mimeType];
}
return null;
}
public function getAvailableRoles(): iterable
{
$roles = $this->roleRepository->findAll();
$returnTab = [];
foreach ($roles as $role) {
$returnTab[$role->getName()] = $role->getConstante();
}
return $returnTab;
}
public function getAvailableProjectsQB(): callable
{
return function (QueryBuilder $qb) {
$alias = $qb->getRootAliases()[0];
if (!$this->security->isGranted('ROLE_SUPER_ADMIN')) {
$qb->where("$alias.id = :id")
->setParameter('id', $this->getCurrentProject()->getId());
}
};
}
/**
* @throws Exception
*/
public function importFile($file, $projectId): array
{
set_time_limit(0);
$batchSize = 500;
$brands = [];
if (($handle = fopen($file, 'r')) !== false) {
$i = 0;
$photoCount = 0;
$currentProject = $this->projectRepository->find($projectId);
$re = '/^(0+)/m';
while (($data = fgetcsv($handle)) !== false) {
$ean = trim(preg_replace($re, '', $data[0]));
$changes = false;
if (!empty($ean) && 'ean' != $ean) {
$sql = 'SELECT id,name,photo,photomini
FROM product
WHERE ean="'.$ean.'"
AND project_id="'.$projectId.'"
';
$stmt = $this->connection->prepare($sql);
$result = $stmt->executeQuery();
if (0 == $result->rowCount()) {
$product = new Product();
$product->setEan($ean);
$product->setProject($currentProject);
$product->setName($data[1]);
$product->setPhoto(trim($data[3]));
$product->setInCatalog(true);
if (!empty($data[4])) {
@$product->setPhotomini(base64_encode(file_get_contents($data[4])));
}
$data[2] = trim($data[2]);
if (!empty($data[2])) {
$product
->setBrand(
!empty($brands[$data[2]]) ?
$brands[$data[2]] :
$this->brandRepository->findOneByOrCreate(['name' => $data[2]])
);
if (empty($brands[$data[2]])) {
$brands[$data[2]] = $product->getBrand();
}
}
$changes = true;
} else {
$productRaw = $result->fetchAllAssociative()[0];
$productName = trim($data[1]);
if ($productRaw['name'] != $productName) {
$changes = true;
}
$photoFilename = trim($data[3]);
if ('' != $photoFilename) {
if ($photoFilename != $productRaw['photo'] || empty($productRaw['photomini'])) {
$changes = true;
$photomini = null;
if ('' != $data[4]) {
@$photomini = base64_encode(file_get_contents($data[4]));
}
}
}
if ($changes) {
$product = $this->productRepository->find($productRaw['id']);
$product->setName($productName);
$product->setPhoto($photoFilename);
if (isset($photomini)) {
$product->setPhotomini($photomini);
}
}
}
if ($changes) {
$product->setLastUpdate(new \DateTime('now'));
$this->manager->persist($product);
++$i;
}
}
if (0 === $i % $batchSize) {
$this->manager->flush();
$brands = [];
}
}
fclose($handle);
$currentProject->setLastUpdated(new \DateTime('now'));
$this->manager->flush();
$this->manager->clear();
return ['productCount' => $i - 1, 'photoCount' => $photoCount];
} else {
return ['error' => "Can't find data"];
}
}
public function isCsvFile(string $filePath): bool
{
// Vérifier l'extension
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if ('csv' !== $extension) {
return false;
}
// Vérifier le type MIME
$mimeType = mime_content_type($filePath);
$allowedMimeTypes = [
'text/csv',
'text/plain',
'application/csv',
'application/vnd.ms-excel',
];
if (!in_array($mimeType, $allowedMimeTypes, true)) {
return false;
}
// Ouvrir le fichier et vérifier le format
if (($handle = fopen($filePath, 'r')) !== false) {
$row = fgetcsv($handle); // Lire la première ligne du fichier
fclose($handle);
if (false === $row) {
return false;
}
// Vérifier que la première ligne contient plusieurs colonnes
if (count($row) < 2) { // On suppose qu'un CSV a au moins deux colonnes
return false;
}
return true;
}
return false;
}
public function getDistance(
float $latitudeFrom,
float $longitudeFrom,
float $latitudeTo,
float $longitudeTo,
int $earthRadius = 6371,
): float|int {
// Convert degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
// Haversine formula
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return $angle * $earthRadius;
}
/**
* @param mixed $photo base64 image file
*
* @return array [url, directory, filename, extension]
*
* @throws \Exception
*/
public function storePhoto(mixed $photo): array
{
$img = $photo->data;
$img = preg_replace('#^data:image/\w+;base64,#i', '', $img);
$img = base64_decode($img);
$directory = $this->kernel->getEnvironment().DIRECTORY_SEPARATOR.$_ENV['S3_DIRECTORY'];
$finfo = new \finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->buffer($img);
$bucket = $_ENV['S3_BUCKET'];
$extension = $this->getImageExtensionFromMime($mimeType);
$filename = uniqid().'.'.$extension;
if (!$extension) {
throw new \Exception('Unknown image mime');
}
$s3client = new S3Client([
'version' => 'latest',
'region' => 'eu-west-3',
'credentials' => [
'key' => $_ENV['S3_KEY'],
'secret' => $_ENV['S3_SECRET'],
],
]);
$response = $s3client->putObject([
'ACL' => 'public-read',
'Bucket' => $bucket,
'Key' => $directory.'/'.$filename,
'Body' => $img,
'ContentType' => $mimeType,
]);
return [
'url' => $response->get('ObjectURL'),
'extension' => $extension,
'directory' => $directory,
'filename' => $filename,
];
}
}