<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserModificationFormType;
use App\Repository\CategoryRepository;
use App\Repository\ProductRepository;
use App\Repository\UserRepository;
use App\Services\Cart;
use App\Services\SearchFilterPagination;
use App\Services\Pagination;
use App\Services\SearchProductsPagination;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use Google\Service\CloudResourceManager\SearchProjectsResponse;
use Google\Service\CustomSearchAPI\Search;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
class MainController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function index(ProductRepository $productRepo, CategoryRepository $catRepo, Request $request): Response
{
$products = $productRepo->findAll();
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
$response = $this->render('main/index.html.twig', [
'products' => $products,
'categories' => $categories,
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
]);
$response->headers->set('Cache-Control',
'no-store, no-cache, must-revalidate, max-age=0');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
return $response;
}
#[Route('/produit/{id}', name: 'product')]
public function produit($id, Request $request, ProductRepository $productRepo, CategoryRepository $catRepo): Response
{
$product = $productRepo->find($id);
$categories = $catRepo->findAll();
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
$stock = $product->getStock();
$comeFromPage = $request->headers->get('referer');
//Get instagram links
$filePath = $this->getParameter('kernel.project_dir').'/public/instagram_links.json';
$jsonFile = file_get_contents($filePath);
$instagramLinks = json_decode($jsonFile, true);
return $this->render('main/product.html.twig',
[
'product' => $product,
'categories' => $categories,
'comeFromPage' => $comeFromPage,
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
'stock' => $stock,
'instagramLinks' => $instagramLinks,
'test' => $cart->test()
]);
}
//DOUBLON ACCOUNT ! VOIR REGISTRATION CONTROLLER ! Vérifier l'utilité de cette route !
#[Route('/account', name: 'account')]
public function account(CategoryRepository $catRepo, Request $request): Response
{
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
return $this->render('main/account.html.twig', [
'categories' => $categories,
'totalQuantity' => $totalQuantity
]);
}
#[Route('/user-account', name: 'user_account')]
public function userAccount(ProductRepository $productRepo, CategoryRepository $catRepo, Request $request): Response
{
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
return $this->render('account/user_account.html.twig', [
'categories' => $categories,
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice
]);
}
#[Route('/edit-account/{id}', name: 'edit_account')]
public function editAccount($id, UserRepository $userRepo, ProductRepository $productRepo, Request $request, EntityManagerInterface $entityManager, CategoryRepository $catRepo): Response
{
$user = $userRepo->find($id);
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
$form = $this->createForm(UserModificationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$entityManager->flush();
//If $id == à celui de l'admin
//return $this->redirectToRoute('admin_index');
return $this->redirectToRoute('user_account');
}
return $this->render('account/edit_account.html.twig', [
'categories' => $categories,
'form' => $form->createView(),
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice
]);
}
#[Route('/change-user-password', name:('change_user_password'))]
public function changeUserPassword(Request $request, UserPasswordHasherInterface $passwordHasher, EntityManagerInterface $em): JsonResponse
{
//Ecrire les intructions du mot de passe coté client (min 8, max 64)
$data = json_decode($request->getContent(), true);
try
{
if (!$data || !isset($data['newPass'])) {
throw new Exception('Données manquantes : le mot de passe est requis.');
}
$newPassword = $data['newPass'];
$user = $this->getUser();
$passwordLength = strlen($newPassword);
if ($passwordLength < 8 || $passwordLength > 64) {
throw new Exception('Le mot de passe doit contenir entre 8 et 64 caractères.');
}
if (preg_match('/[<>&\'"\/]/', $newPassword)) {
throw new Exception('Le mot de passe contient des caractères non autorisés.');
}
$encodedPassword = $passwordHasher->hashPassword($user, $newPassword);
$user->setPassword($encodedPassword);
$em->persist($user);
$em->flush();
$this->addFlash('success', "Nouveau mot de passe enregistré !");
return new JsonResponse([
"status" => "ok",
"message" => "Pass bien reçus",
"password" => $newPassword
], 200);
}
catch(Exception $e)
{
return new JsonResponse([
'status' => 'error',
'message' => $e->getMessage()
], 400);
}
return new JsonResponse([
"status custom" => "ok",
"message" => "Pass bien reçus",
"password" => $newPassword
], 200);
}
#[Route('/all-products-category/{page}', name: 'all_products_category')]
public function allProductsCategory(ProductRepository $productRepo, CategoryRepository $catRepo, Request $request, $page = 1): Response
{
$products = $productRepo->findAll();
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
//PAGINATION
//Change this value to set ne numbers of products per pages
$productsPerPage = 3;
$pageToDisplay = $page - 1;
$currentPage = 1 + $page;
$totalProducts = count($products);
$totalPages = $totalProducts / 3;
for ($i = 0; $i <= $totalPages - 1; $i++)
{
$pageContent[] = array_slice($products, $i * $productsPerPage, $productsPerPage);
}
return $this->render('main/all_products_category.html.twig', [
'products' => $pageContent[$pageToDisplay],
'categories' => $categories,
'currentPage' => $currentPage,
'maxPage' => count($pageContent),
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'totalQuantity' => $totalQuantity
]);
}
#[Route('/products-category/{id}', name: 'products_category')]
public function productsCategory($id, CategoryRepository $catRepo, ProductRepository $productRepo, Request $request): Response
{
$selectedCategory = $catRepo->find($id);
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
return $this->render('main/products_category.html.twig', [
'category' => $selectedCategory,
'categories' => $categories,
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'totalQuantity' => $totalQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice
]);
}
#[Route('filtre-produit', name:'products_filter')]
public function productsFilter(Request $request, ProductRepository $productRepo, CategoryRepository $catRepo): Response
{
$categories = $catRepo->findAll();
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$products = [];
$pageAsked = 0;
$productsByCategoryRequested = $request->query->all();
if ($request->query->has('category'))
{
if (count($productsByCategoryRequested) == 0)
{
//dd('Array empty');
$products = $productRepo->findAll();
}
else
{
//dd('There is a request');
if (!is_array($productsByCategoryRequested['category']))
{
//dd('Single request');
$products = $productRepo->findBy(['category' => $productsByCategoryRequested['category']]);
}
if (is_array($productsByCategoryRequested['category']))
{
//dd('Multiple request');
$productsByCategories = [];
$products = [];
foreach ($productsByCategoryRequested['category'] as $id)
{
$productsByCategories[] = $productRepo->findBy(['category' => $id]);
}
foreach ($productsByCategories as $categoriesOfProducts)
{
$products = array_merge($categoriesOfProducts, $products);
}
}
}
}
else
{
$products = $productRepo->findAll();
}
if($request->query->has('price-range'))
{
//Obtenir le price range
$priceRange = $request->query->all('price-range')[0];
list($range1, $range2) = explode('-', $priceRange);
$range1 = (int)$range1;
$range2 = (int)$range2;
$sortedByPriceProducts = [];
//dd($range2);
foreach($products as $product)
{
$productPrice = $product->getPrice();
if ($productPrice >= $range1 && $productPrice < $range2)
{
$sortedByPriceProducts[] = $product;
}
}
$products = $sortedByPriceProducts;
}
if($request->query->has('page'))
{
$pageAsked = $request->query->get('page');
}
//Change this value to automaticaly change the numbers of pages according to products to display wished
$numberOfProductsToDisplayPerPages = 10;
$pagination = new Pagination($products, $numberOfProductsToDisplayPerPages);
$paginatedProducts = $pagination->makePagination($pageAsked);
//dd($pagination->getStatus());
return $this->render('main/products_filter.html.twig', [
'products' => $products,
'numberOfProductsToDisplayPerPages' => $numberOfProductsToDisplayPerPages,
'paginatedProducts' => $paginatedProducts,
'categories' => $categories,
'currentPage' => $pagination->getCurrentPage(),
'maxPage' => $pagination->getMaxPage(),
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
]);
}
#[Route('/contact', name:'contact')]
public function contacts(CategoryRepository $catRepo): Response
{
$categories = $catRepo->findAll();
return $this->render('main/contact.html.twig', [
'categories' => $categories
]);
}
#[Route('/recherche-produits/{pageAsked}', name: 'search_products', methods: ['GET', 'POST'])]
public function searchProducts(Request $request, ProductRepository $productRepo, CategoryRepository $catRepo, int $pageAsked = 0)
{
$categories = $catRepo->findAll();
$filters = $request->query->all();
if (!$filters)
{
//Pas de reqête GET donc je dois récupérer la requête POST
// LE PROBLEME EST ICI !!!
$terms = $request->request->get('terms_to_search');
//Je pense que c'est résolut avec la conditionnel suivante:
if ($request->headers->get('Content-Type') === 'application/json')
{
$data = json_decode($request->getContent(), true);
$terms = $data['terms_to_search'] ?? '';
}
}
else
{
$data = json_decode($request->getContent(), true);
$terms = $data['terms_to_search'] ?? '';
//$terms = "trevor";
}
//TO COPY TO EVERY ROUTE THAT DISPLAY CART
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
$results = $productRepo->fulltextSearch($terms);
//FILTERS
if($filters)
{
if (isset($filters["category"]) && $filters["category"] !== '')
{
$selectedFilters = [...$filters["category"]];
//PHASE I vérification des categories
$filteredProducts = [];
//Boucle sur les produits du $result initial de la recherche fullText
foreach ($results as $result)
{
if (in_array($result->getCategory()->getName(), $selectedFilters))
{
array_push($filteredProducts, $result);
}
}
$results = $filteredProducts;
}
}
if (isset($filters["price-range"]) && $filters["price-range"] !== '')
{
$filteredProdcutsWithPriceRange = [];
foreach($results as $result)
{
$price = $result->getPrice();
$priceRange = $filters["price-range"][0];
list($min, $max) = explode('-', $priceRange);
$min = (float) $min;
$max = (float) $max;
if ($price >= $min && $price <= $max)
{
array_push($filteredProdcutsWithPriceRange, $result);
}
$results = $filteredProdcutsWithPriceRange;
}
}
if (!$results)
{
$result = [];
}
//PAGINATION
$SearchProductsPagination = new SearchProductsPagination($results, 2, $pageAsked, 4);
return $this->render('main/search_results.html.twig', [
//Fulltext search
'terms' => $terms,
'results' => $SearchProductsPagination->paginatedProducts(),
//Pagination
'pageAsked' => $pageAsked,
'numberOfPages' => $SearchProductsPagination->getNumberOfPages(),
'selectorOffset' => $SearchProductsPagination->getNumberOfSelectorPageToDisplay(),
'categories' => $categories,
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
]);
}
#[Route('/add-to-cart/{id}/{quantity}/{customerCustomMessage?}/{customerCustomPolice?}/{customerCustomImage?}', name: 'add_to_cart')]
public function addToCart(int $id, int $quantity = 1, ?string $customerCustomMessage = null, ?string $customerCustomPolice = null, ?string $customerCustomImage = null, Request $request, ProductRepository $productRepo): JsonResponse
{
$cart = new Cart($request->getSession());
$productToAdd = $productRepo->find($id);
if (!$productToAdd) {
return $this->json(['success' => false, 'error' => 'Produit introuvable.'], 404);
}
$productStock = $productToAdd->getStock();
// Validation quantité
if ($quantity < 1) {
return $this->json(['success' => false, 'error' => 'La quantité doit être au moins 1.'], 400);
}
if ($quantity > $productStock) {
return $this->json(['success' => false, 'error' => 'Quantité demandée supérieure au stock disponible.'], 400);
}
// Customizable data protections
$messageMaxLength = 200;
$policeMinNumber = 1;
$policeMaxNumber = 10;
$imageMinNumber = 1;
$imageMaxNumber = 5;
if ($customerCustomMessage !== null || $customerCustomPolice !== null || $customerCustomImage !== null)
{
// Validation message (si présent)
if ($customerCustomMessage !== null) {
if ($customerCustomMessage === 'undefined' || trim($customerCustomMessage) === '') {
return $this->json(['success' => false, 'error' => 'Message personnalisé invalide.'], 400);
}
$messagePattern = '/^[\p{L}0-9 _\-?!\']{1,' . $messageMaxLength . '}$/u';
if (!preg_match($messagePattern, $customerCustomMessage)) {
return $this->json(['success' => false, 'error' => 'Message personnalisé invalide.'], 400);
}
}
// Validation police (si présente)
if ($customerCustomPolice !== null) {
if (!ctype_digit($customerCustomPolice)) {
return $this->json(['success' => false, 'error' => 'Style du texte doit être un entier.'], 400);
}
$policeValue = (int) $customerCustomPolice;
if ($policeValue < $policeMinNumber || $policeValue > $policeMaxNumber) {
return $this->json(['success' => false, 'error' => "Style du texte doit être entre $policeMinNumber et $policeMaxNumber."], 400);
}
}
// Validation image (si présente)
if ($customerCustomImage !== null) {
if (!ctype_digit($customerCustomImage)) {
return $this->json(['success' => false, 'error' => 'Motif doit être un entier.'], 400);
}
$imageValue = (int) $customerCustomImage;
if ($imageValue < $imageMinNumber || $imageValue > $imageMaxNumber) {
return $this->json(['success' => false, 'error' => "Motif doit être entre $imageMinNumber et $imageMaxNumber."], 400);
}
}
}
// Ajout au panier
$cart->addToCart($id, $quantity, $customerCustomMessage, $customerCustomPolice, $customerCustomImage);
return $this->json([
'success' => true,
'cart' => $cart->getCart()
]);
}
#[Route('/get-product-stock/{id}', name:'get_product_stock')]
public function getProductStock($id, ProductRepository $productRepo): JsonResponse
{
$product = $productRepo->find($id);
$productStock = $product->getStock();
return $this->json($productStock);
}
/**
* Cart edition. Use requested id to compare with present product id
* inside cart session if exist.
*/
#[Route('/edit-cart/{id}/{quantity}', name: 'edit_cart')]
public function editCart($id, $quantity = 1, Request $request): JsonResponse
{
$quantity = intval($quantity);
$cart = new Cart($request->getSession());
$maxQuantity = $cart->getMaxQuantityAllowed();
if ($cart->cartSessionExists())
{
$cartContent = $cart->getCart();
foreach($cartContent as $contentId => $content)
{
if ($id == $contentId)
{
if ($quantity === 0)
{
unset($cartContent[$contentId]);
$cart->setCartContent($cartContent);
}
elseif ($quantity != $content['quantity'] && $quantity >= 1 && $quantity <= $maxQuantity)
{
$cartContent[$id]['quantity'] = $quantity;
$cart->setCartContent($cartContent);
}
}
}
}
return $this->json($cart->getCart());
}
#[Route('/check-cart-content', name: 'check_cart_content')]
public function checkCartContent(Request $request, ProductRepository $productRepo): JsonResponse
{
$session = $request->getSession();
$cart = new Cart($session);
$cartContent = $cart->getCartProducts($productRepo);
dd($cartContent);
return $this->json($cartContent);
}
#[Route('/check-cart-quantity', name: 'check_cart_quantity')]
public function checkCart(Request $request): Response
{
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
return new Response($totalQuantity, Response::HTTP_OK, [
'Content-Type' => 'text/plain',
]);
}
#[Route('/clear-cart', name: 'clear_cart')]
public function clearCart(Request $request): Response
{
$session = $request->getSession();
$session->remove('cart');
return new Response('Session cleared !', Response::HTTP_OK, [
'Content-Type' => 'text/plain'
]);
}
#[Route('/test-mail', name: 'test_mail')]
public function testMail(MailerInterface $mailer)
{
$email = (new Email())
->from('no-reply@example.com')
->to('user@example.com')
->subject('Test Mail')
->text('This is a test email sent to MailHog.')
->html('<p>This is a test email sent to MailHog.</p>');
$mailer->send($email);
return new Response('Mail sent to MailHog!');
}
#[Route('/politique-confidentialite', name: 'politique_confidentialite')]
public function informationPage(Request $request, ProductRepository $productRepo, CategoryRepository $catRepo)
{
$categories = $catRepo->findAll();
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
return $this->render('main/politique-confidentialite.html.twig', [
'categories' => $categories,
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
'title' => 'politique de confidentialité'
]);
}
#[Route('/cgv', name: 'cgv')]
public function cgv(Request $request, ProductRepository $productRepo, CategoryRepository $catRepo)
{
$categories = $catRepo->findAll();
$session = $request->getSession();
$cart = new Cart($session);
$totalQuantity = $cart->getCartTotalQuantity();
$maxQuantity = $cart->getMaxQuantityAllowed();
$cartProducts = $cart->getCartProducts($productRepo);
$totalPrice = $cart->getTotalPrice($productRepo);
return $this->render('main/cgv.html.twig', [
'categories' => $categories,
'totalQuantity' => $totalQuantity,
'maxQuantity' => $maxQuantity,
'cartProducts' => $cartProducts,
'totalPrice' => $totalPrice,
'title' => 'Conditions générales de vente'
]);
}
#[Route('/test', name:'test')]
public function test(Request $request, ProductRepository $productRepo, CategoryRepository $catRepo): Response
{
//dd($user);
$session = $request->getSession();
$cart = new Cart($session);
$cartProducts = $cart->getCartProducts($productRepo);
$cartProductsLength = count($cartProducts);
//$productsStocks = [];
for ($i = 0; $i < $cartProductsLength ; $i++)
{
$productId = $cartProducts[$i]["product"]->getId();
$productName = $cartProducts[$i]["product"]->getName();
$productQuantityAsked = $cartProducts[$i]["quantity"];
$productStockData = $this->forward('App\Controller\MainController::getProductStock', [
'id' => $productId,
]);
$productStock = intval( json_decode($productStockData->getContent(), true));
if ($productQuantityAsked > $productStock)
{
throw new Exception('Le produit '.$productName.'n\'est plus disponible');
}
}
dd($productStockData);
}
}