<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use FOS\ElasticaBundle\Finder\PaginatedFinderInterface;
use FOS\ElasticaBundle\Index\IndexManager;
use FOS\ElasticaBundle\Transformer\ElasticaToModelTransformerCollection;
use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Elastica\Query;
use Elastica\Suggest;
use Elastica\Query\Match;
use Elastica\Query\BoolQuery;
use FOS\ElasticaBundle\Repository;
use App\Entity\Product;
use App\Entity\Company;
use App\Entity\OmeloPost;
use App\Entity\UserSearch;
use App\Service\UserService;
use App\Service\ProductService;
use App\Entity\Region;
use App\Entity\Conservation;
use App\Entity\Category;
use App\SearchRepository\ProductSearch;
class SearchController extends AbstractController
{
protected $validProductFinder;
protected $validCompanyFinder;
protected $indexManager;
protected $userServ;
/*
* @param PaginatedFinderInterface $productFinder
*/
public function __construct(PaginatedFinderInterface $validProductFinder,
PaginatedFinderInterface $validCompanyFinder,
IndexManager $indexManager,
UserService $userServ,
EntityManagerInterface $em
){
$this->validProductFinder = $validProductFinder;
$this->validCompanyFinder = $validCompanyFinder;
$this->indexManager = $indexManager;
$this->userServ = $userServ;
$this->em = $em;
}
public function findProducts(string $string){
$query = $this->indexManager->getIndex('product')->getType('valid')
->createSearch($string)
->getQuery();
$association = $this->userServ->getAssociationUserIsBrowsing();
if (!$association){
$query->setPostFilter(new Query\Term(['isDisplayedInAssociationOnly' => false]));
} else {
$query->setPostFilter(new Query\Term([
'searchAssociations' => $association->getId()
]));
}
return $query;
}
public function findProductsSuggestions(string $string) {
$products = $this->em->getRepository(Product::class)->searchForProductsSuggestions($string);
return $products;
}
public function findCompany(string $string){
$query = $this->indexManager->getIndex('product')->getType('valid')
->createSearch($string)
->getQuery();
$association = $this->userServ->getAssociationUserIsBrowsing();
if (!$association){
//$query->setPostFilter(new Query\Term(['isDisplayedInAssociationOnly' => true]));
} else {
$query->setPostFilter(new Query\Term([
'searchAssociations' => $association->getId()
]));
}
return $query;
}
/**
* @Route("/api/search/product/valid",
* options = { "expose" = true },
* name="searchValidProduct"
* )
*/
public function ApiSearch(Request $request, UrlGeneratorInterface $router, ProductService $productServ, EntityManagerInterface $em)
{
$term = $request->get('term');
$suggestions = array();
$productSuggestions = $this->findProductsSuggestions($term);
if (empty($productSuggestions)) {
$productSuggestions = $this->getSuggestions($term, 'product');
}
$columns = array();
$productId = array();
//Limit to 10
$cmtp = 0;
foreach ($productSuggestions as $product) {
$route = false;
//It remove the duplicates
if(!in_array($product->getId(), $productId)){
$productId[]= $product->getId();
$cmtp++;
$product = $productServ->getProductById($product->getId());
//Sometime the search engine doesn'T get updated for products
if($product->getCompany()->getShowPublicly()){
$route = $router->generate('viewProduct',
array(
'id' => $product->getId(),
'producerUrl' => $product->getCompany()->getUrlName(),
'productName' => $product->getUrlName()
)
);
}
if($route){
$suggestions[] = array(
'label' => $product->getNameWithDetails().' par '.$product->getCompany()->getName(),
'category' => 'Produits',
'value' => $route
);
}
}
if($cmtp >= 15)
break;
}
$companySuggestions = $this->getSuggestions($term, 'company');
$companyId = array();
$cmtp = 0;
foreach ($companySuggestions as $company) {
if(!in_array($company->getId(), $companyId)){
$cmtp++;
$companyId[]= $company->getId();
$company = $em->getRepository(Company::class)->findOneBy(
[
'id' => $company->getId()
]
);
if($company->getShowPublicly()){
$route = $router->generate('companyStore',
array(
'urlname' => $company->getUrlName(),
'id' => $company->getId()
)
);
$suggestions[] = array(
'label' => $company->getName().' à '.ucfirst(strtolower($company->getMainLocation()->getCity())).', '.$company->getMainLocation()->getProvince(),
'category' => 'Boutiques',
'value' => $route
);
}
}
if($cmtp >= 5)
break;
}
$response = new JsonResponse($suggestions , 200, array('Cache-Control' => 'no-cache'));
return $response;
}
private function getSuggestions($string, $indexName){
$mngr = $this->indexManager;
$search = $mngr->getIndex($indexName)->createSearch();
$search->addType('valid');
if($indexName == 'product')
return $search->search($this->findProducts($string));
elseif ($indexName == 'company')
return $search->search($this->findCompany($string));
return $search->search($string);
}
/**
* @Route({
* "fr": "/recherche/{type}",
* "en": "/search/{type}"
* }, name="noSearch")
*/
public function noSearch(){
$this->addFlash('success', "Nous avons trouvé aucun produit, voici nos suggestions");
return $this->redirectToRoute('allCategory');
}
/**
* @Route({
* "fr": "/recherche/{type}/{string}",
* "en": "/search/{type}/{string}"
* },
* options = { "expose" = true },
* name="search"
* )
* @Template("frontend/category.html.twig")
*/
public function search(UserService $userServ, $type='a', $string, EntityManagerInterface $em, ProductService $productServ, RepositoryManagerInterface $manager, PaginatorInterface $paginator, Request $request)
{
$twigData = array();
$twigData['hideSideMenu'] = true;
$string = urldecode($string);
$twigData['search'] = $string;
$twigData['products']=false;
$twigData['companies']=false;
$order = "desc";
if ($request->get("order")) {
$order = $request->get("order");
}
$twigData['order']= $order;
$sorting = "popularity";
if ($request->get("sorting")) {
$sorting = $request->get("sorting");
}
$twigData['sorting']= $sorting;
$twigData['productsAmount'] = 0;
$twigData['forcedBanner'] = '/assets/frontend/images/categorie-banner.png';
/* Template dependant */
$twigData['category'] = new Category();
$twigData['category']->settitle('Résultats pour '.$string);
$twigData['selected'] = array(
'main' => false,
'sub1' => false,
'sub2' => false,
'sub3' => false
);
$twigData['regions'] = $em->getRepository(Region::class)->findBy(['province'=>'Qc']);
$twigData['conservations'] = $em->getRepository(Conservation::class)->findAll();
$query = $this->findProducts($string);
switch($type){
case 'p':
$paginator = $this->validProductFinder->findPaginated($query);
$twigData['amountSearch'] = $paginator->getNbResults();
$twigData['products'] = $paginator;
if($twigData['amountSearch'] == 1){
$product = current($this->validProductFinder->find($query));
return $this->redirectToRoute('viewProduct', array(
'producerUrl' => $product->getCompany()->getUrlName(),
'productName' => $product->getUrlName(),
'id' => $product->getId()
));
}
break;
case 'c':
$paginator = $this->validCompanyFinder->findPaginated($query);
$twigData['amountSearch'] = $paginator->getNbResults();
$twigData['companies'] = $paginator;
if($twigData['amountSearch'] == 1){
$company = current($this->validCompanyFinder->find($query));
return $this->redirectToRoute('companyStore', array(
'urlname' => $company->getUrlName(),
'id' => $company->getId()
));
}
break;
//All
case 'a':
$twigData['products'] = array();
$mngr = $this->indexManager;
$amount = $this->em->getRepository(Product::class)->searchForProducts($string, true);
$query = $this->em->getRepository(Product::class)->searchForProducts($string, false, $sorting, $order);
$twigData['productsAmount'] = $amount;
$page = 1;
if(!empty($request->get('page')))
$page = $request->get('page');
$twigData['products']= $paginator->paginate(
$query,
$request->query->getInt('page', $page),
20
);
$twigData['suggestionProductsAmount'] = 0;
$twigData['suggestionProducts'] = false;
if ($amount <= 5) {
$squery = $this->findProducts($string);
$adapter = $this->validProductFinder->createPaginatorAdapter($squery);
$Epaginator = $this->validProductFinder->findPaginated($squery);
$twigData['suggestionProductsAmount'] = $Epaginator->getNbResults();
$twigData['suggestionProducts'] = $paginator->paginate(
$adapter,
$request->query->getInt('page', $page),
20
);
$suggestionProducts = [];
foreach($twigData['suggestionProducts'] as $sp) {
$include = true;
foreach($twigData['products'] as $p) {
if ($sp->getId() == $p->getId()) {
$include = false;
break;
}
}
if ($include) {
$suggestionProducts[] = $sp;
}
}
$twigData["suggestionProducts"] = $suggestionProducts;
}
break;
}
$userServ->saveSearch($string, $twigData['productsAmount'], $type);
return $twigData;
}
function removeAccents($string) {
if ( !preg_match('/[\x80-\xff]/', $string) )
return $string;
$chars = array(
// Decompositions for Latin-1 Supplement
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
chr(195).chr(191) => 'y',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's'
);
$string = strtr($string, $chars);
return $string;
}
}