src/Services/Search/BaseSearch.php line 80

Open in your IDE?
  1. <?php
  2. namespace App\Services\Search;
  3. use Symfony\Component\HttpKernel\KernelInterface;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use Knp\Component\Pager\PaginatorInterface;
  6. use Symfony\Component\HttpFoundation\RequestStack;
  7. use App\Services\Search\SearchInterface;
  8. use \DateTime;
  9. use Symfony\Component\Cache\Adapter\FilesystemAdapter;
  10. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  11. use XLabs\SocialBundle\Engines\Friendship;
  12. use XLabs\SocialBundle\Engines\Views;
  13. use App\Services\Timeline;
  14. abstract class BaseSearch implements SearchInterface
  15. {
  16.     protected $kernel;
  17.     protected $em;
  18.     protected $paginator;
  19.     protected $request;
  20.     protected $default_locale;
  21.     protected $options;
  22.     protected $friendshipEngine;
  23.     protected $timelineEngine;
  24.     protected $viewsEngine;
  25.     public function __construct(KernelInterface $kernelEntityManagerInterface $emPaginatorInterface $knp_paginatorRequestStack $request_stackParameterBagInterface $params)
  26.     {
  27.         $this->em $em;
  28.         $this->paginator $knp_paginator;
  29.         $this->request $request_stack->getMainRequest();
  30.         $this->default_locale $params->get('kernel.default_locale');
  31.         $this->options = [
  32.             'show_suggestions' => false// 'did you mean ...' results; must be a number of results to show; only used when it is a sphinx search without results
  33.             'no_cache' => false,
  34.             'paginator_params' => array(), // paginator overridden params, for the pagination template
  35.             'paginator_route' => false// to force a url when mixing ajax/non-ajax pagination route gets corrupted
  36.             'return_sphinx_results' => false// if true, will return sphinx results (sphinx data, no queries)
  37.             'return_query' => false // if true, query object is returned
  38.         ];
  39.         if($kernel->getEnvironment() != 'dev')
  40.         {
  41.             $this->em->getConnection()->getConfiguration()->setSQLLogger(null);
  42.         }
  43.     }
  44.     public function getOptions($default_options$options)
  45.     {
  46.         $aOptions array_merge(array_merge($this->options$default_options), $options);
  47.         // When on ajax, params come as POST params, but booleans turned into strings
  48.         array_walk_recursive($aOptions, function(&$aOption){
  49.             if(is_string($aOption))
  50.             {
  51.                 switch($aOption)
  52.                 {
  53.                     case 'true':
  54.                         $aOption true;
  55.                         break;
  56.                     case 'false':
  57.                         $aOption false;
  58.                         break;
  59.                     case 'null':
  60.                         $aOption null;
  61.                         break;
  62.                 }
  63.             }
  64.         });
  65.         // Fix; some sortings add HIDDEN selects that make getSingleScalarResult() crash
  66.         if(isset($aOptions['return_count']) && $aOptions['return_count'] && isset($aOptions['sorting']) && $aOptions['sorting'])
  67.         {
  68.             $aOptions['sorting'] = false;
  69.         }
  70.         return $aOptions;
  71.     }
  72.     public function paginate($aOptions)
  73.     {
  74.         $default_options = [
  75.             'aResults' => [],
  76.             'repository_class' => false,
  77.             'max_results' => false,
  78.             'page' => 1,
  79.             'show_pagination' => true,
  80.             'paginator_params' => [],
  81.             'paginator_route' => false,
  82.             'searchType' => 'doctrine'
  83.         ];
  84.         $aOptions array_merge($default_options$aOptions);
  85.         $result_ids = [];
  86.         $total_results 0;
  87.         switch($aOptions['searchType'])
  88.         {
  89.             case 'doctrine':
  90.                 $aResults array_map(function($s){
  91.                     return $s['id'];
  92.                 }, $aOptions['aResults']);
  93.                 $result_ids $aResults;
  94.                 $total_results count($result_ids);
  95.                 break;
  96.             case 'redis':
  97.                 $aResults $aOptions['aResults']; // this is a query object with a custom hint (items are sliced by redis)
  98.                 $result_ids array_map(function($s){
  99.                     return $s['id'];
  100.                 }, $aResults->getArrayResult());
  101.                 $total_results count($result_ids);
  102.                 break;
  103.             case 'sphinx':
  104.                 $aResults $aOptions['aResults'];
  105.                 $result_ids = isset($aResults['matches']) ? array_keys($aResults['matches']) : [];
  106.                 $total_results = isset($aResults) && is_array($aResults) && isset($aResults['total_found']) ? $aResults['total_found'] : count($result_ids);
  107.                 break;
  108.         }
  109.         $pagination false;
  110.         $aOptions['max_results'] = $aOptions['max_results'] ?: PHP_INT_MAX;
  111.         //if($aOptions['show_pagination'] && $aOptions['max_results'])
  112.         if($aOptions['max_results'])
  113.         {
  114.             if($aOptions['page'] == 'last')
  115.             {
  116.                 $pagination $this->paginator->paginate(
  117.                     $aResults,
  118.                     1,
  119.                     $aOptions['max_results']
  120.                 );
  121.                 $aOptions['page'] = ceil($pagination->getTotalItemCount() / $aOptions['max_results']);
  122.             }
  123.             $aOptions['paginator_params']['searchType'] = $aOptions['searchType'];
  124.             $pagination $this->paginator->paginate(
  125.                 $aResults,
  126.                 $aOptions['page'],
  127.                 $aOptions['max_results'],
  128.                 $aOptions['paginator_params']
  129.             );
  130.             if($aOptions['paginator_route'])
  131.             {
  132.                 $pagination->setUsedRoute($aOptions['paginator_route']);
  133.             }
  134.             if($aOptions['searchType'] == 'doctrine')
  135.             {
  136.                 $result_ids $pagination->getItems();
  137.             }
  138.         }
  139.         $locale $this->request $this->request->getLocale() : $this->default_locale;
  140.         $repository $this->em->getRepository($aOptions['repository_class']);
  141.         if(method_exists($repository'setLocale'))
  142.         {
  143.             $repository $repository->setLocale($locale);
  144.         }
  145.         $results $repository->getCollectionById([
  146.             'result_ids' => $result_ids,
  147.             'sorting' => 'field'
  148.         ]);
  149.         /*$cache = new FilesystemAdapter();
  150.         $key = md5(json_encode($params));
  151.         $cache->deleteItem($key);
  152.         $_posts = $cache->getItem($key);
  153.         if($_posts->isHit())
  154.         {
  155.             $posts = $_posts->get();
  156.         } else {
  157.             $posts = $this->em->getRepository('MMMediaBundle:Post')->getPostsById($params);
  158.             $cache->save($_posts->set($posts));
  159.         }*/
  160.         return [
  161.             'result_ids' => $result_ids,
  162.             'results' => $results,
  163.             'pagination' => $pagination,
  164.             //'total_results' => isset($aResults) && is_array($aResults) && isset($aResults['total_found']) ? $aResults['total_found'] : count($result_ids)
  165.             'total_results' => $total_results
  166.         ];
  167.     }
  168.     public function getSphinxSorting($arrValues)
  169.     {
  170.         $set = [];
  171.         $arrValues array_reverse($arrValues);
  172.         for ($i=0;$i count($arrValues);$i++)
  173.         {
  174.             $set[$arrValues[$i]] = $i 1// turns the array into array(document_id1 => position, document_id2 => position, ...)
  175.         }
  176.         return $set;
  177.     }
  178.     /*
  179.      * Results for "did you mean ..." when no sphinx results found
  180.      * Requires 'id' and 'value' to be included in the $query
  181.      * MySQL driven
  182.      */
  183.     protected function getSuggestedResults($aOptions$query)
  184.     {
  185.         $cache = new FilesystemAdapter();
  186.         $cache_key 'suggested_results.'.md5($query);
  187.         $cache_ttl 86400// 24h
  188.         if(!$cache->has($cache_key))
  189.         {
  190.             $conn $this->em->getConnection();
  191.             $conn->getConfiguration()->setSQLLogger(null); // disable logging
  192.             $stmt $conn->prepare($query);
  193.             $stmt->execute();
  194.             $cache->set($cache_keyjson_encode($stmt->fetchAll()), $cache_ttl);
  195.         }
  196.         $results json_decode($cache->get($cache_key), true);
  197.         $shortest 100;
  198.         //$closest = false;
  199.         $closeIds = [];
  200.         $closeWords = [];
  201.         // loop through words to find the closest
  202.         foreach($results as $result)
  203.         {
  204.             $id $result['id'];
  205.             $word $result['value'];
  206.             // calculate the distance between the input word and the current word
  207.             //$lev = levenshtein($aOptions['search_terms'], $word);
  208.             $lev levenshtein(preg_replace('/\s+/''-'$aOptions['search_terms']), preg_replace('/\s+/''-'$word));
  209.             //if the distance is shorter than the last shortest one, replace it.
  210.             if($lev <= $shortest)
  211.             {
  212.                 // set the closest match, and shortest distance
  213.                 $closest $word;
  214.                 $shortest $lev;
  215.                 array_unshift($closeWords$closest);
  216.                 array_unshift($closeIds$id);
  217.             }
  218.         }
  219.         //return array_slice($closeWords, 0, $aOptions['show_suggestions']);
  220.         return array_slice($closeIds0$aOptions['show_suggestions']);
  221.         //return $closest;
  222.     }
  223.     public function setFriendshipEngine(Friendship $friendshipEngine)
  224.     {
  225.         $this->friendshipEngine $friendshipEngine;
  226.     }
  227.     public function setTimelineEngine(Timeline $timelineEngine)
  228.     {
  229.         $this->timelineEngine $timelineEngine;
  230.     }
  231.     public function setViewsEngine(Views $viewsEngine)
  232.     {
  233.         $this->viewsEngine $viewsEngine;
  234.     }
  235. }