SearchController.php 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. namespace Muzich\CoreBundle\Controller;
  3. use Muzich\CoreBundle\lib\Controller;
  4. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  5. use Muzich\CoreBundle\Searcher\ElementSearcher;
  6. use Muzich\CoreBundle\Form\Search\ElementSearchForm;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Muzich\CoreBundle\Util\StrictCanonicalizer;
  9. class SearchController extends Controller
  10. {
  11. protected function searchElementsMore($elements, $invertcolors, $message)
  12. {
  13. $end = (($count = count($elements)) < $this->container->getParameter('search_ajax_more'));
  14. $html = '';
  15. if ($count)
  16. {
  17. $html = $this->render('MuzichCoreBundle:SearchElement:default.html.twig', array(
  18. 'user' => $this->getUser(),
  19. 'elements' => $elements,
  20. 'invertcolor' => $invertcolors
  21. ))->getContent();
  22. }
  23. return $this->jsonResponse(array(
  24. 'count' => $count,
  25. 'message' => $message,
  26. 'html' => $html,
  27. 'end' => $end
  28. ));
  29. }
  30. /**
  31. * Procédure de recherche, qui met a jour l'objet de recherche (ainsi
  32. * que les paramétres en session).
  33. *
  34. */
  35. public function searchElementsAction($id_limit = null, $invertcolors = false)
  36. {
  37. if ($this->getUser() == 'anon.')
  38. {
  39. if ($this->getRequest()->isXmlHttpRequest())
  40. {
  41. return $this->jsonResponse(array(
  42. 'status' => 'mustbeconnected'
  43. ));
  44. }
  45. else
  46. {
  47. return $this->redirect($this->generateUrl('index'));
  48. }
  49. }
  50. $request = $this->getRequest();
  51. $search_object = $this->getElementSearcher();
  52. $search_form = $this->getSearchForm($search_object);
  53. $form_submited = false;
  54. if ($request->getMethod() == 'POST')
  55. {
  56. $form_submited = true;
  57. $search_form->bindRequest($request);
  58. // Si le formulaire est valide
  59. if ($search_form->isValid())
  60. {
  61. // On met a jour l'objet avec les nouveaux paramétres saisie dans le form
  62. $data = $search_form->getData();
  63. // Le formulaire nous permet de récupérer uniquement les ids.
  64. // On va donc chercher les name en base pour le passer a l'objet
  65. // ElementSearch
  66. $data['tags'] = $this->getDoctrine()->getRepository('MuzichCoreBundle:Tag')
  67. ->getTagsForElementSearch(json_decode($data['tags'], true));
  68. $search_object->update($data);
  69. // Et on met a jour la "mémoire" de la recherche
  70. $this->setElementSearcherParams($search_object->getParams());
  71. }
  72. }
  73. if ($this->getRequest()->isXmlHttpRequest())
  74. {
  75. if ($form_submited)
  76. {
  77. $message = $this->trans(
  78. 'noelements.sentence_filter',
  79. array('%link_string%' => $this->trans(
  80. 'noelements.sentence_filter_link_string',
  81. array(),
  82. 'elements'
  83. )),
  84. 'elements'
  85. );
  86. }
  87. else
  88. {
  89. $message = $this->trans(
  90. 'elements.ajax.more.noelements',
  91. array(),
  92. 'elements'
  93. );
  94. }
  95. // template qui apelle doSearchElementsAction
  96. $search = $this->getElementSearcher();
  97. $search->update(array(
  98. 'count' => $this->container->getParameter('search_ajax_more'),
  99. 'id_limit' => $id_limit
  100. ));
  101. $elements = $search->getElements($this->getDoctrine(), $this->getUserId());
  102. return $this->searchElementsMore($elements, $invertcolors, $message);
  103. }
  104. else
  105. {
  106. return $this->redirect($this->generateUrl('home'));
  107. }
  108. }
  109. public function searchElementsShowAction($type, $object_id, $id_limit, $invertcolors)
  110. {
  111. if ($this->getRequest()->isXmlHttpRequest())
  112. {
  113. $object = null;
  114. $param_id = '';
  115. if ($type == 'user')
  116. {
  117. $object = $this->getDoctrine()
  118. ->getRepository('MuzichCoreBundle:User')
  119. ->findOneBy(array('id' => $object_id))
  120. ;
  121. $param_id = 'user_id';
  122. }
  123. elseif ($type == 'group')
  124. {
  125. $object = $this->getDoctrine()
  126. ->getRepository('MuzichCoreBundle:Group')
  127. ->findOneById($object_id)
  128. ;
  129. $param_id = 'group_id';
  130. }
  131. if (!$object)
  132. {
  133. throw new \Exception('Object Unknow');
  134. }
  135. $search = $this->createSearchObject(array(
  136. $param_id => $object->getId(),
  137. 'count' => $this->container->getParameter('search_ajax_more'),
  138. 'id_limit' => $id_limit
  139. ));
  140. $elements = $search->getElements($this->getDoctrine(), $this->getUserId());
  141. return $this->searchElementsMore($elements, $invertcolors,
  142. $this->trans(
  143. 'elements.ajax.more.noelements',
  144. array(),
  145. 'elements'
  146. )
  147. );
  148. }
  149. throw new \Exception('XmlHttpRequest only for this action');
  150. }
  151. /**
  152. * Ajoute le tag au début du tableau passé en paramètre si celui-ci
  153. * n'est pas a l'intérieur.
  154. *
  155. * @param array $array
  156. * @param Tag $tag
  157. * @return array
  158. */
  159. private function sort_addtop_if_isnt_in($array, $tag)
  160. {
  161. $in = false;
  162. for ($x=0;$x<=sizeof($array)-1;$x++)
  163. {
  164. if ($array[$x]['id'] == $tag['id'])
  165. {
  166. $in = true;
  167. break;
  168. }
  169. }
  170. if (!$in)
  171. {
  172. array_unshift($array, $tag);
  173. return $array;
  174. }
  175. return $array;
  176. }
  177. /**
  178. * Ajoute le tag a al fin du tableau passé en paramètre si celui-ci
  179. * n'est pas a l'intérieur.
  180. *
  181. * @param array $array
  182. * @param Tag $tag
  183. * @return array
  184. */
  185. private function sort_addbottom_if_isnt_in($array, $tag)
  186. {
  187. $in = false;
  188. for ($x=0;$x<=sizeof($array)-1;$x++)
  189. {
  190. if ($array[$x]['id'] == $tag['id'])
  191. {
  192. $in = true;
  193. break;
  194. }
  195. }
  196. if (!$in)
  197. {
  198. $array[] = $tag;
  199. return $array;
  200. }
  201. return $array;
  202. }
  203. /**
  204. * Organise le trie des tags de manière plus friendly user.
  205. *
  206. * @param array $tags
  207. * @param string $search
  208. * @return array
  209. */
  210. private function sort_search_tags($tags, $search)
  211. {
  212. $same_found = false;
  213. $canonicalizer = new StrictCanonicalizer();
  214. $tag_sorted = array();
  215. foreach ($tags as $i => $tag)
  216. {
  217. // Pas plus de trois caractères en plus de la recherche
  218. $terms = array_merge(array($search), explode(' ', $search));
  219. foreach ($terms as $word)
  220. {
  221. if (strlen($word) > 1)
  222. {
  223. if (
  224. strlen(str_replace(strtoupper($canonicalizer->canonicalize($word)), '', strtoupper($tag['slug']))) < 4
  225. && $word != $search
  226. )
  227. {
  228. $tag_sorted = $this->sort_addtop_if_isnt_in($tag_sorted, $tag);
  229. }
  230. }
  231. }
  232. }
  233. // Uniquement dans le cas de présence d'un espace/separateur ou plus
  234. // on cherche les mot composé comme lui
  235. $terms = array_merge(
  236. explode(' ', $search),
  237. explode('-', $search)
  238. );
  239. $tags_counteds = array();
  240. foreach ($tags as $i => $tag)
  241. {
  242. $terms_search = array_merge(
  243. explode(' ', $tag['slug']),
  244. explode('-', $tag['slug'])
  245. );
  246. foreach ($terms as $word)
  247. {
  248. if (
  249. strpos(strtoupper($tag['slug']), strtoupper($word)) !== false
  250. && count($terms_search) > 2
  251. )
  252. {
  253. $count = 1;
  254. if (array_key_exists($tag['id'], $tags_counteds))
  255. {
  256. $count = ($tags_counteds[$tag['id']]['count'])+1;
  257. }
  258. $tags_counteds[$tag['id']] = array(
  259. 'count' => $count,
  260. 'tag' => $tag
  261. );
  262. }
  263. }
  264. }
  265. foreach ($tags_counteds as $id => $counted)
  266. {
  267. if ($counted['count'] > 1)
  268. {
  269. // Ci-dessous on va chercher a voir si le tag et la recherche on le
  270. // même nombre de mots, si c'est le cas on pourra considérer cette
  271. // recherche comme lié a un tag connu.
  272. $words_search = array_merge(
  273. explode(' ', $search),
  274. explode('-', $search)
  275. );
  276. $words_tag = array_merge(
  277. explode(' ', $counted['tag']['slug']),
  278. explode('-', $counted['tag']['slug'])
  279. );
  280. if (count($words_search) == count($words_tag))
  281. {
  282. $same_found = true;
  283. }
  284. // Cette verif permet de ne pas ajouter les tags qui n'ont qu'un mot
  285. // Si on ajouté ce tag maintenant il ne serais pas ajouté au controle en dessous
  286. // (nom identique) et donc pas au dessus.
  287. $tag_sorted = $this->sort_addtop_if_isnt_in($tag_sorted, $counted['tag']);
  288. }
  289. }
  290. foreach ($tags as $i => $tag)
  291. {
  292. // Chaine de caractère identique
  293. $terms = array_merge(
  294. array($search),
  295. explode(' ', $search),
  296. explode('-', $search),
  297. array(str_replace(' ', '-', $search)),
  298. array(str_replace('-', ' ', $search))
  299. );
  300. foreach ($terms as $word)
  301. {
  302. if (strlen($word) > 1)
  303. {
  304. if (strtoupper($canonicalizer->canonicalize($word)) == strtoupper($tag['slug']))
  305. {
  306. // Ci-dessous on déduit si le mot étant identique au tag représente bien
  307. // le terme de recherche. De façon a si c'est le cas pouvoir dire:
  308. // oui le terme recherché est connu.
  309. if (in_array($word, array(
  310. $search,
  311. str_replace(' ', '-', $search),
  312. str_replace('-', ' ', $search)
  313. )))
  314. {
  315. $same_found = true;
  316. }
  317. $tag_sorted = $this->sort_addtop_if_isnt_in($tag_sorted, $tag);
  318. }
  319. }
  320. }
  321. }
  322. foreach ($tags as $i => $tag)
  323. {
  324. $tag_sorted = $this->sort_addbottom_if_isnt_in($tag_sorted, $tag);
  325. }
  326. return array(
  327. 'tags' => $tag_sorted,
  328. 'same_found' => $same_found
  329. );
  330. }
  331. /**
  332. *
  333. * @param string $string_search
  334. */
  335. public function searchTagAction($string_search, $timestamp)
  336. {
  337. if ($this->getUser() == 'anon.')
  338. {
  339. if ($this->getRequest()->isXmlHttpRequest())
  340. {
  341. return $this->jsonResponse(array(
  342. 'status' => 'mustbeconnected'
  343. ));
  344. }
  345. else
  346. {
  347. return $this->redirect($this->generateUrl('index'));
  348. }
  349. }
  350. $string_search = trim($string_search);
  351. $canonicalizer = new StrictCanonicalizer();
  352. if ($this->getRequest()->isXmlHttpRequest())
  353. {
  354. if (strlen($string_search) > 1)
  355. {
  356. $words = array_merge(
  357. explode(' ', $string_search),
  358. explode('-', $string_search),
  359. explode(',', $string_search),
  360. explode(', ', $string_search)
  361. );
  362. $where = '';
  363. $params = array();
  364. foreach ($words as $i => $word)
  365. {
  366. if (strlen($word) > 1)
  367. {
  368. $word = $canonicalizer->canonicalize($word);
  369. if ($where == '')
  370. {
  371. $where .= 'WHERE UPPER(t.slug) LIKE :str'.$i;
  372. }
  373. else
  374. {
  375. $where .= ' OR UPPER(t.slug) LIKE :str'.$i;
  376. }
  377. $params['str'.$i] = '%'.strtoupper($word).'%';
  378. }
  379. }
  380. $params['uid'] = '%"'.$this->getUserId().'"%';
  381. $tags = $this->getDoctrine()->getEntityManager()->createQuery("
  382. SELECT t.name, t.slug, t.id FROM MuzichCoreBundle:Tag t
  383. $where
  384. AND (t.tomoderate = '0'
  385. OR t.privateids LIKE :uid)
  386. ORDER BY t.name ASC"
  387. )->setParameters($params)
  388. ->getScalarResult()
  389. ;
  390. $tags_response = array();
  391. foreach ($tags as $tag)
  392. {
  393. $tags_response[] = array(
  394. 'name' => $tag['name'],
  395. 'id' => $tag['id'],
  396. 'slug' => $tag['slug']
  397. );
  398. }
  399. $sort_response = $this->sort_search_tags($tags_response, $string_search);
  400. $status = 'success';
  401. $error = '';
  402. $message = $this->trans(
  403. 'tags.search.message_found',
  404. array('%string%' => $string_search),
  405. 'userui'
  406. );
  407. }
  408. else
  409. {
  410. $status = 'error';
  411. $sort_response = array('tags' => array(), 'same_found' => false);
  412. $error = 'Vous devez saisir au moins deux caractères';
  413. $message = '';
  414. }
  415. $return_array = array(
  416. 'status' => $status,
  417. 'timestamp' => $timestamp,
  418. 'error' => $error,
  419. 'message' => $message,
  420. 'same_found' => $sort_response['same_found'],
  421. 'data' => $sort_response['tags']
  422. );
  423. $response = new Response(json_encode($return_array));
  424. $response->headers->set('Content-Type', 'application/json; charset=utf-8');
  425. return $response;
  426. }
  427. throw $this->createNotFoundException('Cette ressource n\'est pas accessible');
  428. }
  429. /**
  430. *
  431. * @param type $string_search
  432. * @return Response
  433. */
  434. public function searchTagIdAction($string_search)
  435. {
  436. if ($this->getRequest()->isXmlHttpRequest())
  437. {
  438. $tag_id = $this->getDoctrine()->getEntityManager()->createQuery("
  439. SELECT t.id FROM MuzichCoreBundle:Tag t
  440. WHERE t.name = :str
  441. ORDER BY t.name ASC"
  442. )->setParameter('str', $string_search)
  443. ->getSingleScalarResult()
  444. ;
  445. $response = new Response(json_encode($tag_id));
  446. $response->headers->set('Content-Type', 'application/json; charset=utf-8');
  447. return $response;
  448. }
  449. throw $this->createNotFoundException('Cette ressource n\'est pas accessible');
  450. }
  451. }