ElementSearcherQueryBuilder.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. <?php
  2. namespace Muzich\CoreBundle\Searcher;
  3. use Doctrine\ORM\EntityManager;
  4. use Doctrine\ORM\QueryBuilder;
  5. use Doctrine\ORM\Query\Expr\Join;
  6. class ElementSearcherQueryBuilder
  7. {
  8. /**
  9. *
  10. * @var \Doctrine\ORM\EntityManager
  11. */
  12. protected $em;
  13. protected $user_id;
  14. /**
  15. *
  16. * @var array
  17. */
  18. protected $parameters_ids = array();
  19. /**
  20. *
  21. * @var array
  22. */
  23. protected $parameters_elements = array();
  24. /**
  25. *
  26. * @var ElementSearcher
  27. */
  28. protected $es;
  29. /**
  30. *
  31. * @var QueryBuilder
  32. */
  33. protected $query_ids;
  34. /**
  35. *
  36. * @var QueryBuilder
  37. */
  38. protected $query_elements;
  39. /**
  40. *
  41. * @var array
  42. */
  43. protected $builder_params = array();
  44. /**
  45. *
  46. * @param EntityManager $em
  47. * @param ElementSearcher $es
  48. */
  49. public function __construct(EntityManager $em, ElementSearcher $es, $user_id, $builder_params = array())
  50. {
  51. $this->em = $em;
  52. $this->es = $es;
  53. $this->user_id = $user_id;
  54. $this->builder_params = $builder_params;
  55. }
  56. private function buildTags()
  57. {
  58. if (count($tags = $this->es->getTags()))
  59. {
  60. // Un truc pas propre fait que des fois (quand ajax on dirais)
  61. // on a du string a la place du tableau
  62. if (!is_array($tags))
  63. {
  64. $tags_decoded = json_decode($tags);
  65. $tags = array();
  66. foreach ($tags_decoded as $tag_id)
  67. {
  68. $tags[$tag_id] = $tag_id;
  69. }
  70. }
  71. if (count($tags))
  72. {
  73. $str_or = $this->query_ids->expr()->orx();
  74. $this->query_ids->leftJoin('e.tags', 't');
  75. foreach ($tags as $tag_id => $tag_name)
  76. {
  77. $str_or->add($this->query_ids->expr()->eq('t.id', ":itag".$tag_id));
  78. $this->parameters_ids["itag".$tag_id] = $tag_id;
  79. }
  80. $this->query_ids->andWhere($str_or);
  81. }
  82. }
  83. }
  84. private function buildString()
  85. {
  86. if (($string = $this->es->getString()))
  87. {
  88. // On prépare notre liste de mots
  89. $words = array_unique(array_merge(
  90. explode(' ', $string),
  91. explode('-', $string),
  92. explode('- ', $string),
  93. explode(' -', $string),
  94. explode(' - ', $string),
  95. explode(',', $string),
  96. explode(', ', $string),
  97. explode(' ,', $string),
  98. explode(' , ', $string)
  99. ));
  100. // On récupère les ids des elements correspondants
  101. $word_min_length = 0;
  102. if (isset($this->builder_params['word_min_length']))
  103. {
  104. $word_min_length = $this->builder_params['word_min_length'];
  105. }
  106. // On prépare un sous-where avec des or
  107. $str_or = $this->query_ids->expr()->orx();
  108. foreach ($words as $i => $word)
  109. {
  110. if (strlen($word) >= $word_min_length)
  111. {
  112. // On ajoute un or pour chaque mots
  113. $str_or->add($this->query_ids->expr()->like('UPPER(e.name)', ":str".$i));
  114. $this->parameters_ids['str'.$i] = '%'.strtoupper($word).'%';
  115. }
  116. }
  117. $this->query_ids->andWhere($str_or);
  118. }
  119. }
  120. private function buildNetwork()
  121. {
  122. if ($this->es->getNetwork() == ElementSearcher::NETWORK_PERSONAL && $this->user_id)
  123. {
  124. $this->query_ids
  125. ->join('e.owner', 'o')
  126. ->leftJoin('e.group', 'g')
  127. ->leftJoin('o.followers_users', 'f')
  128. ->leftJoin('g.followers', 'gf')
  129. ;
  130. $this->query_ids->andWhere('f.follower = :userid OR gf.follower = :useridg');
  131. $this->parameters_ids['userid'] = $this->user_id;
  132. $this->parameters_ids['useridg'] = $this->user_id;
  133. }
  134. }
  135. private function buildFavorite()
  136. {
  137. if ($this->es->isFavorite())
  138. {
  139. if (($favorite_user_id = $this->es->getUserId()) && !$this->es->getGroupId())
  140. {
  141. $this->query_ids->leftJoin('e.elements_favorites', 'fav2');
  142. $this->query_ids->andWhere('fav2.user = :fuid');
  143. $this->parameters_ids['fuid'] = $favorite_user_id;
  144. }
  145. else if (($favorite_group_id = $this->es->getGroupId()) && !$this->es->getUserId())
  146. {
  147. // TODO: Faire en sorte que ça affiche les favoris des gens suivant
  148. // le groupe
  149. }
  150. else
  151. {
  152. throw new Exception('For use favorite search element, you must specify an user_id or group_id');
  153. }
  154. }
  155. }
  156. private function buildUserAndGroup()
  157. {
  158. // (flou) Si on recherche les élements d'un user
  159. if (($search_user_id = $this->es->getUserId()) && !$this->es->isFavorite())
  160. {
  161. $this->query_ids->andWhere('e.owner = :suid');
  162. $this->parameters_ids['suid'] = $search_user_id;
  163. }
  164. // (flou) Si on recherche les éléments d'un groupe
  165. if (($search_group_id = $this->es->getGroupId()) && !$this->es->isFavorite())
  166. {
  167. $this->query_ids->andWhere('e.group = :sgid');
  168. $this->parameters_ids['sgid'] = $search_group_id;
  169. }
  170. }
  171. private function buildLimits()
  172. {
  173. // Si id_limit est précisé c'est que l'on demande "la suite" ou "les nouveaux"
  174. if (($id_limit = $this->es->getIdLimit()) && !$this->es->isSearchingNew())
  175. {
  176. $this->query_ids->andWhere("e.id < :id_limit");
  177. $this->parameters_ids['id_limit'] = $id_limit;
  178. }
  179. elseif ($id_limit && $this->es->isSearchingNew())
  180. {
  181. $this->query_ids->andWhere("e.id > :id_limit");
  182. $this->parameters_ids['id_limit'] = $id_limit;
  183. $this->query_ids->orderBy("e.created", 'ASC')
  184. ->addOrderBy("e.id", 'ASC');
  185. }
  186. }
  187. private function buildStrict()
  188. {
  189. // Recherche strict ou non ?
  190. if ($this->es->getTagStrict() && count(($tags = $this->es->getTags())))
  191. {
  192. // On a besoin de récupérer la liste des element_id qui ont les tags
  193. // demandés.
  194. $tag_ids = '';
  195. if (count($tags))
  196. {
  197. foreach ($tags as $tag_id => $tag_name)
  198. {
  199. if ($tag_ids === '')
  200. {
  201. $tag_ids .= (int)$tag_id;
  202. }
  203. else
  204. {
  205. $tag_ids .= ','.(int)$tag_id;
  206. }
  207. }
  208. }
  209. $sql = "SELECT et.element_id FROM elements_tag et "
  210. ."WHERE et.tag_id IN ($tag_ids) group by et.element_id "
  211. ."having count(distinct et.tag_id) = ".count($tags);
  212. $rsm = new \Doctrine\ORM\Query\ResultSetMapping;
  213. $rsm->addScalarResult('element_id', 'element_id');
  214. $strict_element_ids_result = $this->em
  215. ->createNativeQuery($sql, $rsm)
  216. //->setParameter('ids', $tag_ids)
  217. ->getScalarResult()
  218. ;
  219. $strict_element_ids = array();
  220. if (count($strict_element_ids_result))
  221. {
  222. foreach ($strict_element_ids_result as $strict_id)
  223. {
  224. $strict_element_ids[] = $strict_id['element_id'];
  225. }
  226. }
  227. if (count($strict_element_ids))
  228. {
  229. $this->query_ids->andWhere('e.id IN (:tag_strict_ids)');
  230. $this->parameters_ids['tag_strict_ids'] = $strict_element_ids;
  231. }
  232. // Ce else palie au bug du au cas ou $strict_element_ids est egal a array();
  233. else
  234. {
  235. return false;
  236. }
  237. }
  238. }
  239. private function buildNeedTags()
  240. {
  241. // Si id_limit est précisé c'est que l'on demande "la suite" ou "les nouveaux"
  242. if ($this->es->isNeedTags())
  243. {
  244. $this->query_ids->andWhere("e.need_tags = '1'");
  245. }
  246. }
  247. private function buildIdsLimits()
  248. {
  249. if ($this->es->hasIds())
  250. {
  251. $this->query_ids->andWhere('e.id IN (:limiteds_ids)');
  252. $this->parameters_ids['limiteds_ids'] = $this->es->getIds();
  253. }
  254. }
  255. /**
  256. *
  257. * @param boolean $disable_limit
  258. */
  259. protected function proceedIdsQuery($disable_limit = false)
  260. {
  261. $this->query_ids = $this->em->createQueryBuilder()
  262. ->select('e.id')
  263. ->from('MuzichCoreBundle:Element', 'e')
  264. ->groupBy('e.id')
  265. ->orderBy('e.created', 'DESC')
  266. ->addOrderBy('e.id', 'DESC')
  267. ;
  268. if (!$disable_limit)
  269. {
  270. $this->query_ids->setMaxResults($this->es->getCount());
  271. }
  272. // Prise en compte des tags
  273. $this->buildTags();
  274. // Si on effectue une recherche avec un string
  275. $this->buildString();
  276. // Paramètrage en fonction du réseau
  277. $this->buildNetwork();
  278. // Si on recherche des elements d'user ou de groupe
  279. $this->buildUserAndGroup();
  280. // Si on recherche des elements mis en favoris
  281. $this->buildFavorite();
  282. // Pour les demandes de "more" ou "nouveaux" elements.
  283. $this->buildLimits();
  284. // Si on recherche les tags de manière stricte
  285. if ($this->buildStrict() === false)
  286. {
  287. return false;
  288. }
  289. // Si on recherche des partages en demande de tags
  290. $this->buildNeedTags();
  291. // Si on a fournis des ids dés le départ
  292. $this->buildIdsLimits();
  293. $this->query_ids->setParameters($this->parameters_ids);
  294. }
  295. /**
  296. *
  297. * @param boolean $disable_limit
  298. * @return \Doctrine\ORM\Query
  299. * @throws \Exception
  300. */
  301. public function getIdsQuery($disable_limit = false)
  302. {
  303. //// Contrôle de la demande
  304. //if ($this->es->hasIds())
  305. //{
  306. // throw new \Exception("Vous demandez un Query_ids avec un ElementSearcher "
  307. // ."possédant déjà une liste d'ids");
  308. //}
  309. if ($this->proceedIdsQuery($disable_limit) === false)
  310. {
  311. return false;
  312. }
  313. return $this->query_ids->getQuery();
  314. }
  315. protected function proceedElementsQuery()
  316. {
  317. // On récupère les ids d'éléments
  318. if (($ids_query = $this->getIdsQuery()) === false)
  319. {
  320. return false;
  321. }
  322. // On va récupérer les ids en base en fonction des paramètres
  323. $q_ids = $ids_query->getArrayResult();
  324. $element_ids = array();
  325. if (count($q_ids))
  326. {
  327. // On prépare les ids pour la requete des éléments
  328. foreach ($q_ids as $r_id)
  329. {
  330. $element_ids[] = $r_id['id'];
  331. }
  332. }
  333. if (!count($element_ids))
  334. {
  335. // Si on a pas d'ids on retourne une requete qui ne donnera rien
  336. return false;
  337. }
  338. // On prépare des paramètres de la requete d'éléments
  339. $this->parameters_elements['ids'] = $element_ids;
  340. // On prépare la requete des elements
  341. $this->query_elements = $this->em->createQueryBuilder();
  342. if ($this->user_id)
  343. {
  344. $this->query_elements->select('e', 'p', 'po', 't', 'o', 'g', 'fav');
  345. }
  346. else{
  347. $this->query_elements->select('e', 'p', 'po', 't', 'o', 'g');
  348. }
  349. $this->query_elements
  350. ->from('MuzichCoreBundle:Element', 'e')
  351. ->leftJoin('e.group', 'g')
  352. ->leftJoin('e.parent', 'p')
  353. ->leftJoin('p.owner', 'po')
  354. ;
  355. if ($this->user_id)
  356. {
  357. $this->parameters_elements['uidt'] = '%"'. $this->user_id.'"%';
  358. $this->parameters_elements['uid'] = $this->user_id;
  359. $this->query_elements
  360. ->leftJoin('e.tags', 't', Join::WITH,
  361. "(t.tomoderate = 'FALSE' OR t.tomoderate IS NULL OR t.privateids LIKE :uidt)")
  362. ->leftJoin('e.elements_favorites', 'fav', Join::WITH,
  363. 'fav.user = :uid')
  364. ;
  365. }
  366. else
  367. {
  368. $this->query_elements
  369. ->leftJoin('e.tags', 't', Join::WITH,
  370. "(t.tomoderate = 'FALSE' OR t.tomoderate IS NULL)")
  371. ;
  372. }
  373. $this->query_elements
  374. ->join('e.owner', 'o')
  375. ->where('e.id IN (:ids)')
  376. ->orderBy("e.created", 'DESC')
  377. ->addOrderBy("e.id", 'DESC')
  378. ;
  379. // Ce code est désactivé: Les ids ont déjà été filtré par la id_query.
  380. // // Ce cas de figure se présente lorsque l'on fait un ajax de "plus d'éléments"
  381. // if (($id_limit = $this->es->getIdLimit()))
  382. // {
  383. // $this->query_elements->andWhere("e.id < :id_limit");
  384. // $this->query_elements->setMaxResults($this->es->getCount());
  385. // $this->parameters_elements['id_limit'] = $id_limit;
  386. // }
  387. // Lorsque l'on impose les ids (typiquement affichage des éléments avec un commentaire etc)
  388. // On charge les tags proposés dés la requete pour économiser les échanges avec la bdd
  389. if (($ids_display = $this->es->getIdsDisplay()))
  390. {
  391. $this->query_elements
  392. ->addSelect('tp', 'tpu', 'tpt')
  393. ->leftJoin('e.tags_propositions', 'tp')
  394. ->leftJoin('tp.user', 'tpu')
  395. ->leftJoin('tp.tags', 'tpt')
  396. ;
  397. }
  398. $this->query_elements->setParameters($this->parameters_elements);
  399. }
  400. public function getElementsQuery()
  401. {
  402. if ($this->proceedElementsQuery() === false)
  403. {
  404. return $this->em
  405. ->createQuery("SELECT e FROM MuzichCoreBundle:Element e WHERE 1 = 2")
  406. ;
  407. }
  408. return $this->query_elements->getQuery();
  409. }
  410. }