Subversion Repositories eFlore/Applications.del

Rev

Rev 1871 | Rev 1881 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1840 jpm 1
<?php
2
// declare(encoding='UTF-8');
3
/**
4
 * Classe contenant des méthodes permettant de construire les requêtes SQL complexe concernant les images et obs.
5
 *
6
 * @category  DEL
7
 * @package   Services
8
 * @package   Bibliotheque
9
 * @version   0.1
10
 * @author    Mathias CHOUET <mathias@tela-botanica.org>
11
 * @author    Jean-Pascal MILCENT <jpm@tela-botanica.org>
12
 * @author    Aurelien PERONNET <aurelien@tela-botanica.org>
13
 * @license   GPL v3 <http://www.gnu.org/licenses/gpl.txt>
14
 * @license   CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
15
 * @copyright 1999-2014 Tela Botanica <accueil@tela-botanica.org>
16
 */
17
class Sql {
18
 
1871 jpm 19
	const APPLI_IMG = 'IMG';
20
	const APPLI_OBS = 'OBS';
21
 
1840 jpm 22
	private $conteneur;
23
	private $bdd;
24
	private $parametres = array();
1871 jpm 25
	private $appli;
1840 jpm 26
	private $requete = array(
27
		'select' => array(),
28
		'join' => array(),
29
		'where' => array(),
30
		'groupby' => array(),
31
		'orderby' => array());
32
 
1871 jpm 33
	private $champsPrenom = array('du.prenom', 'prenom_utilisateur');
34
	private $champsNom = array('du.nom',  'nom_utilisateur');
1840 jpm 35
 
36
 
37
	public function __construct(Conteneur $conteneur) {
38
		$this->conteneur = $conteneur;
39
		$this->bdd = $this->conteneur->getBdd();
40
	}
41
 
42
	public function setParametres(Array $parametres) {
43
		$this->parametres = $parametres;
44
	}
45
 
1871 jpm 46
	public function setAppli($appliType) {
47
		if ($appliType == 'IMG' || $appliType == 'OBS') {
48
			$this->appli = $appliType;
49
		} else {
50
			throw new Exception("Les types d'appli disponible sont : IMG (pour PictoFlora) et OBS (pour IdentiPlante)");
51
		}
52
	}
53
 
54
	private function getPrefixe() {
55
		return $this->appli === 'IMG' ? 'di' : 'do';
56
	}
57
 
58
	private function etreAppliImg() {
59
		return $this->appli === 'IMG' ? true : false;
60
	}
61
 
62
	private function etreAppliObs() {
63
		return $this->appli === 'OBS' ? true : false;
64
	}
65
 
1840 jpm 66
	public function getRequeteSql() {
67
		return $this->requete;
68
	}
69
 
70
	private function addJoin($join) {
1871 jpm 71
		if (!isset($this->requete['join'][$join])) {
72
			$this->requete['join'][] = $join;
73
		}
1840 jpm 74
	}
75
 
76
	public function getJoin() {
77
		return ($this->requete['join'] ? implode(' ', array_unique($this->requete['join'])).' ' : '');
78
	}
79
 
80
	private function addJoinDis($join) {
81
		$this->requete['join']['dis'] = $join;
82
	}
83
 
84
	private function addWhere($idParam, $where) {
85
		if (isset($this->parametres['_parametres_condition_or_'])
86
				&& in_array($idParam, $this->parametres['_parametres_condition_or_'])) {
87
			$this->requete['where']['OR'][] = $where;
88
		} else {
89
			$this->requete['where']['AND'][] = $where;
90
		}
91
	}
92
	public function getWhere() {
93
		if (isset($this->requete['where']['OR']) && count($this->requete['where']['OR']) > 0) {
94
			$this->requete['where']['AND'][] = '('.implode(' OR ', $this->requete['where']['OR']).')';
95
		}
96
 
97
		$where = ' TRUE ';
98
		if (isset($this->requete['where']['AND']) && count($this->requete['where']['AND']) > 0) {
99
			$where = implode(' AND ', $this->requete['where']['AND']).' ';
100
		}
101
		return $where;
102
	}
103
 
104
	private function addGroupBy($groupBy) {
105
		$this->requete['groupby'][] = $groupBy;
106
	}
107
 
108
	public function getGroupBy() {
109
		$groupby = '';
110
		if (isset($this->requete['groupby']) && count($this->requete['groupby']) > 0) {
111
			$groupby = 'GROUP BY '.implode(', ', array_unique($this->requete['groupby'])).' ';
112
		}
113
		return $groupby;
114
	}
115
 
116
	private function addOrderBy($orderby) {
117
		$this->requete['orderby'][] = $orderby;
118
	}
119
 
120
	public function getOrderBy() {
121
		$orderby = '';
122
		if (isset($this->requete['orderby']) && count($this->requete['orderby']) > 0) {
123
			$orderby = 'ORDER BY '.implode(', ', array_unique($this->requete['orderby'])).' ';
124
		}
125
		return $orderby;
126
	}
127
 
128
	public function getLimit() {
129
		return 'LIMIT '.$this->parametres['navigation.depart'].','.$this->parametres['navigation.limite'].' ';
130
	}
131
 
132
	/**
133
	 * - Rempli le tableau des contraintes "where" et "join" nécessaire
134
	 * à la *recherche* des observations demandées ($req) utilisées par self::getIdObs()
135
	 *
136
	 * Attention, cela signifie que toutes les tables ne sont pas *forcément*
137
	 * join'ées, par exemple si aucune contrainte ne le nécessite.
138
	 * $req tel qu'il est rempli ici est utile pour récupéré la seule liste des
139
	 * id d'observation qui match.
140
	 * Pour la récupération effective de "toutes" les données correspondante, il faut
141
	 * réinitialiser $req["join"] afin d'y ajouter toutes les autres tables.
142
	 *
143
	 * Note: toujours rajouter les préfixes de table (vdi,du,doi ou di), en fonction de ce que défini
144
	 * les JOIN qui sont utilisés.
145
	 * le préfix de v_del_image est "vdi" (cf: "FROM" de self::getIdObs())
146
	 * le préfix de del_utilisateur sur id_utilisateur = vdi.ce_utilisateur est "du"
147
	 *
148
	 * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
149
	 * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
150
	 */
151
	public function ajouterContraintes() {
152
		$this->ajouterContrainteAuteur();
153
		$this->ajouterContrainteDate();
154
		$this->ajouterContrainteDepartement();
155
		$this->ajouterContrainteIdZoneGeo();
156
		$this->ajouterContrainteGenre();
157
		$this->ajouterContrainteFamille();
158
		$this->ajouterContrainteNs();
159
		$this->ajouterContrainteNn();
160
		$this->ajouterContrainteReferentiel();
161
		$this->ajouterContrainteCommune();
162
	}
163
 
164
	private function ajouterContrainteAuteur() {
165
		if (isset($this->parametres['masque.auteur'])) {
166
			$auteur = $this->parametres['masque.auteur'];
167
			// id du poster de l'obs
1871 jpm 168
			$prefixe = $this->getPrefixe();
169
			$this->addJoin("LEFT JOIN del_utilisateur AS du ON (du.id_utilisateur = $prefixe.ce_utilisateur) ");
1840 jpm 170
 
171
			if (is_numeric($auteur)) {
172
				$this->ajouterContrainteAuteurId();
1871 jpm 173
			} elseif(preg_match('/@[a-z0-9-]+(?:\.[a-z0-9-]+)*\.[a-z]{2,}$/i', $auteur)) {
1840 jpm 174
				$this->ajouterContrainteAuteurEmail();
175
			} else {
176
				$this->ajouterContrainteAuteurIntitule();
177
			}
178
		}
179
	}
180
 
181
	private function ajouterContrainteAuteurId() {
182
		$id = $this->parametres['masque.auteur'];
1871 jpm 183
		$prefixe = $this->getPrefixe();
184
		$sqlTpl = "(du.id_utilisateur = %1\$d OR $prefixe.ce_utilisateur = %1\$d)";
1840 jpm 185
		$whereAuteur = sprintf($sqlTpl, $id);
186
		$this->addWhere('masque.auteur', $whereAuteur);
187
	}
188
 
189
	private function ajouterContrainteAuteurEmail() {
190
		$email = $this->parametres['masque.auteur'];
1871 jpm 191
		$prefixe = $this->getPrefixe();
192
		$sqlTpl = "(du.courriel LIKE %1\$s OR $prefixe.courriel_utilisateur LIKE %1\$s )";
1840 jpm 193
		$emailP = $this->bdd->proteger("$email%");
194
		$whereAuteur = sprintf($sqlTpl, $emailP);
195
		$this->addWhere('masque.auteur', $whereAuteur);
196
	}
197
 
198
	/**
199
	 * Retourne une clause where du style:
200
	 * CONCAT(IF(du.prenom IS NULL, "", du.prenom), [...] vdi.i_nomutilisateur) REGEXP 'xxx'
201
	 * Note; i_(nom|prenom_utilisateur), alias pour cel_images.(nom|prenom), n'est pas traité
202
	 * car cette information est redondante dans cel_image et devrait être supprimée.
203
	 */
204
	private function ajouterContrainteAuteurIntitule() {
205
		$auteurExplode = explode(' ', $this->parametres['masque.auteur']);
206
		$nbreMots = count($auteurExplode);
207
 
208
		if ($nbreMots == 1) {
209
			$this->ajouterContrainteAuteurPrenomOuNom();
210
		} else if ($nbreMots == 2) {
211
			$this->ajouterContrainteAuteurPrenomEtNom();
212
		}
213
	}
214
 
215
	private function ajouterContrainteAuteurPrenomOuNom() {
216
		$prenomOuNom = $this->parametres['masque.auteur'];
217
 
218
		$sqlTpl = 'CONCAT(%s,%s) LIKE %s';
1871 jpm 219
		$prefixe = $this->getPrefixe();
220
		$champsPrenomSql = self::ajouterIfNullPourConcat($this->champsPrenom, $prefixe);
221
		$champsNomSql = self::ajouterIfNullPourConcat($this->champsNom, $prefixe);
1840 jpm 222
		$auteurMotif = $this->bdd->proteger("%$prenomOuNom%");
223
 
224
		$auteurWhere = sprintf($sqlTpl, $champsPrenomSql, $champsNomSql, $auteurMotif);
225
		$this->addWhere('masque.auteur', $auteurWhere);
226
	}
227
 
228
	private function ajouterContrainteAuteurPrenomEtNom() {
229
		list($prenom, $nom) = explode(' ', $this->parametres['masque.auteur']);
230
 
231
		$sqlTpl = '(CONCAT(%1$s,%2$s) LIKE %3$s AND CONCAT(%1$s,%2$s) LIKE %4$s)';
1871 jpm 232
		$prefixe = $this->getPrefixe();
233
		$champsPrenomSql = self::ajouterIfNullPourConcat($this->champsPrenom, $prefixe);
234
		$champsNomSql = self::ajouterIfNullPourConcat($this->champsNom, $prefixe);
1840 jpm 235
		$prenomMotif = $this->bdd->proteger("%$prenom%");
236
		$nomMotif = $this->bdd->proteger("%$nom%");
237
 
238
		$auteurWhere = sprintf($sqlTpl, $champsPrenomSql, $champsNomSql, $prenomMotif, $nomMotif);
239
		$this->addWhere('masque.auteur', $auteurWhere);
240
	}
241
 
242
		/**
243
	 * Lorsque l'on concatène des champs, un seul NULL prend le dessus,
244
	 * Il faut donc utiliser la syntaxe IFNULL(%s, "").
245
	 * (Cette fonction effectue aussi l'implode() "final"
246
	 */
1871 jpm 247
	private static function ajouterIfNullPourConcat($champs, $prefixe) {
1840 jpm 248
		$champsProteges = array();
249
		foreach ($champs as $champ) {
1871 jpm 250
			if (strstr($champ, '.') === false) {
251
				$champ = "$prefixe.$champ";
252
			}
1840 jpm 253
			$champsProteges[] = "IFNULL($champ, '')";
254
		}
255
		return implode(',', $champsProteges);
256
	}
257
 
258
	private function ajouterContrainteDate() {
259
		if (isset($this->parametres['masque.date'])) {
260
			$date = $this->parametres['masque.date'];
1871 jpm 261
			if (preg_match('/^\d{4}$/', $date) && $date < 2030 && $date > 1600) {
262
				$sqlTpl = "YEAR(do.date_observation) = %d";
1840 jpm 263
				$dateWhere = sprintf($sqlTpl, $date);
264
				$this->addWhere('masque.date', $dateWhere);
265
			} else {
1871 jpm 266
				$sqlTpl = "do.date_observation = %s";
267
				$dateP = $this->bdd->proteger($date);
1840 jpm 268
				$dateWhere = sprintf($sqlTpl, $dateP);
269
				$this->addWhere('masque.date', $dateWhere);
270
			}
1871 jpm 271
 
272
			if ($this->etreAppliImg()) {
273
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
274
			}
1840 jpm 275
		}
276
	}
277
 
278
	private function ajouterContrainteDepartement() {
279
		if (isset($this->parametres['masque.departement'])) {
280
			$dept = $this->parametres['masque.departement'];
281
			$deptMotif = $this->bdd->proteger("INSEE-C:$dept");
1871 jpm 282
			$this->addWhere('masque.departement', "do.ce_zone_geo LIKE $deptMotif");
283
 
284
			if ($this->etreAppliImg()) {
285
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
286
			}
1840 jpm 287
		}
288
	}
289
 
290
	private function ajouterContrainteIdZoneGeo() {
291
		if (isset($this->parametres['masque.id_zone_geo'])) {
292
			$idZgMotif = $this->bdd->proteger($this->parametres['masque.id_zone_geo']);
1871 jpm 293
			$this->addWhere('masque.id_zone_geo', "do.ce_zone_geo = $idZgMotif");
294
 
295
			if ($this->etreAppliImg()) {
296
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
297
			}
1840 jpm 298
		}
299
	}
300
 
301
	private function ajouterContrainteGenre() {
302
		if (isset($this->parametres['masque.genre'])) {
303
			$genre = $this->parametres['masque.genre'];
304
			$genreMotif = $this->bdd->proteger("%$genre% %");
1871 jpm 305
			$this->addWhere('masque.genre', "do.nom_sel LIKE $genreMotif");
306
 
307
			if ($this->etreAppliImg()) {
308
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
309
			}
1840 jpm 310
		}
311
	}
312
 
313
	private function ajouterContrainteFamille() {
314
		if (isset($this->parametres['masque.famille'])) {
315
			$familleMotif = $this->bdd->proteger($this->parametres['masque.famille']);
1871 jpm 316
			$this->addWhere('masque.famille', "do.famille = $familleMotif");
317
 
318
			if ($this->etreAppliImg()) {
319
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
320
			}
1840 jpm 321
		}
322
	}
323
 
324
	private function ajouterContrainteNs() {
325
		if (isset($this->parametres['masque.ns'])) {
326
			$ns = $this->parametres['masque.ns'];
327
			$nsMotif = $this->bdd->proteger("$ns%");
1871 jpm 328
			$this->addWhere('masque.ns', "do.nom_sel LIKE $nsMotif");
329
 
330
			if ($this->etreAppliImg()) {
331
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
332
			}
1840 jpm 333
		}
334
	}
335
 
336
	private function ajouterContrainteNn() {
337
		if (isset($this->parametres['masque.nn'])) {
1871 jpm 338
			$sqlTpl = '(do.nom_sel_nn = %1$d OR do.nom_ret_nn = %1$d)';
1840 jpm 339
			$nnWhere = sprintf($sqlTpl, $this->parametres['masque.nn']);
340
			$this->addWhere('masque.nn', $nnWhere);
1871 jpm 341
 
342
			if ($this->etreAppliImg()) {
343
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
344
			}
1840 jpm 345
		}
346
	}
347
 
348
	private function ajouterContrainteReferentiel() {
349
		if (isset($this->parametres['masque.referentiel'])) {
350
			$ref = $this->parametres['masque.referentiel'];
351
			$refMotif = $this->bdd->proteger("$ref%");
1871 jpm 352
			$this->addWhere('masque.referentiel', "do.nom_referentiel LIKE $refMotif");
353
 
354
			if ($this->etreAppliImg()) {
355
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
356
			}
1840 jpm 357
		}
358
	}
359
 
360
	private function ajouterContrainteCommune() {
361
		if (isset($this->parametres['masque.commune'])) {
362
			$commune = $this->parametres['masque.commune'];
363
			$communeMotif = $this->bdd->proteger("$commune%");
1871 jpm 364
			$this->addWhere('masque.commune', "do.zone_geo LIKE $communeMotif");
365
 
366
			if ($this->etreAppliImg()) {
367
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
368
			}
1840 jpm 369
		}
370
	}
371
 
1845 jpm 372
	public function ajouterConstrainteAppliObs() {
373
		$this->ajouterContrainteTagCel();
374
		$this->ajouterContrainteType();
375
		// TODO : ATTENTION -> vue que l'on utilise une vue basée sur les images, nous devons grouper par obs
1871 jpm 376
		$this->addGroupBy('do.id_observation');
1845 jpm 377
	}
378
 
379
		/**
380
	 * @param $req: la représentation de la requête MySQL complète, à amender.
381
	 */
382
	private function ajouterContrainteType() {
1871 jpm 383
		if (isset($this->parametres['masque.type'])) {
384
			if (array_key_exists('adeterminer', $this->parametres['masque.type'])) {
385
				// Récupèration de toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
386
				// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
387
				$this->addWhere('masque.type', '('.
388
					'do.certitude = "aDeterminer" '.
389
					'OR do.certitude = "douteux" '.
390
					'OR do.mots_cles_texte LIKE "%aDeterminer%" '.
391
					'OR do.nom_sel_nn IS NULL '.
392
					'OR do.nom_sel_nn = 0 '.// il ne DEVRAIT pas y avoir d'entrées à 0, mais il y en a quand-même !!
393
					')');
394
			}
395
			if (array_key_exists('validees', $this->parametres['masque.type'])) {
396
				// Récupèration de toutes les observations ayant un commentaire doté de proposition_retenue = 1
397
				$this->addJoin('INNER JOIN del_commentaire AS dc '.
398
					'ON (do.id_observation = dc.ce_observation AND dc.proposition_retenue = 1) ');
399
			}
1845 jpm 400
 
1871 jpm 401
			if (array_key_exists('endiscussion', $this->parametres['masque.type'])) {
402
				$nbreCommentaire =(int) ($this->conteneur->getParametre('observations.nb_commentaires_discussion'));
403
				$this->addWhere('masque.type', '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc '.
404
					"WHERE ce_observation = id_observation) > $nbreCommentaire ");
405
			}
406
 
407
			if ($this->etreAppliImg()) {
408
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
409
			}
1845 jpm 410
		}
411
	}
412
 
1840 jpm 413
	/**
414
	 * in $p: un tableau de paramètres, dont:
415
	 * - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
416
	 * - 'masque.tag_del': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
417
	 * - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
418
	 *
419
	 * in/ou: $req: un tableau de structure de requête MySQL
420
	 *
421
	 * Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
422
	 * ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
423
	 * Soit directement $this->consulter() si des masque.tag* sont passés
424
	 * (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
425
	 * Soit via sqlAddMasqueConstraint():
426
	 * (pas de split, "OR" entre chaque condition) [ comportement historique ]
427
	 * équivalent à:
428
	 * (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
429
	 *
430
	 */
431
	public function ajouterConstrainteAppliImg() {
432
		$this->ajouterContrainteMilieu();
433
		$this->ajouterContrainteTri();
434
		$this->ajouterContrainteTagCel();
435
		$this->ajouterContrainteTagDel();
436
	}
437
 
438
	private function ajouterContrainteMilieu() {
439
		if (isset($this->parametres['masque.milieu'])) {
440
			$milieu = $this->parametres['masque.milieu'];
441
			$milieuMotif = $this->bdd->proteger("%$milieu%");
1871 jpm 442
			$this->addWhere('masque.milieu', "do.milieu LIKE $milieuMotif");
443
 
444
			if ($this->etreAppliImg()) {
445
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
446
			}
1840 jpm 447
		}
448
	}
449
 
450
	private function ajouterContrainteTri() {
451
		if (isset($this->parametres['tri'])) {
452
			$tri = $this->parametres['tri'];
453
 
1863 jpm 454
			if (isset($this->parametres['protocole'])  && ($tri == 'moyenne-arithmetique' || $tri == 'points')) {
1840 jpm 455
				// $this->parametres['protocole'] *est* défini (cf Outils::filtrerUrlsParams...())
1871 jpm 456
				$sqlTpl = 'LEFT JOIN del_image_stat AS dis ON di.id_image = dis.ce_image AND dis.ce_protocole = %d';
1840 jpm 457
				$triSql = sprintf($sqlTpl, $this->parametres['protocole']);
458
				$this->addJoinDis($triSql);
459
			}
460
 
461
			if (isset($this->parametres['ordre']) && $tri == 'tags') {
462
				$typeJointure = ($this->parametres['ordre'] == 'desc') ? 'INNER' : 'LEFT';
1871 jpm 463
				$this->addJoin("$typeJointure JOIN del_image_stat AS dis ON di.id_image = dis.ce_image");
1840 jpm 464
				// nécessaire (dup ce_image dans del_image_stat)
1871 jpm 465
				$this->addGroupBy('di.ce_observation');
1840 jpm 466
			}
467
		}
468
	}
469
 
470
	/**
471
	 * Car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
472
	 * sont toujours présentes; bien que parfois NULL.
473
	 */
474
	// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ? et auquel cas laisser au client le choix du couteux "%" ?
475
	private function ajouterContrainteTagCel() {
1845 jpm 476
		if (isset($this->parametres['masque.tag_cel'])) {
1840 jpm 477
			if (isset($this->parametres['masque.tag_cel']['AND'])) {
478
				$tags = $this->parametres['masque.tag_cel']['AND'];
479
				$clausesWhere = array();
480
				foreach ($tags as $tag) {
481
					$tagMotif = $this->bdd->proteger("%$tag%");
1871 jpm 482
					$sqlTpl = "CONCAT(IFNULL(do.mots_cles_texte,''),IFNULL(di.mots_cles_texte,'')) LIKE %s";
1840 jpm 483
					$clausesWhere[] = sprintf($sqlTpl, $tagMotif);
484
				}
485
				$whereTags = implode(' AND ', $clausesWhere);
486
				$this->addWhere('masque.tag_cel', "($whereTags)");
487
			} else if (isset($this->parametres['masque.tag_cel']['OR'])) {
488
				$tags = $this->parametres['masque.tag_cel']['OR'];
1871 jpm 489
				$sqlTpl = "CONCAT(IFNULL(do.mots_cles_texte,''),IFNULL(di.mots_cles_texte,'')) REGEXP %s";
1840 jpm 490
				$tagMotif = $this->bdd->proteger(implode('|', $tags));
491
				$tagSql = sprintf($sqlTpl, $tagMotif);
492
				$this->addWhere('masque.tag_cel', $tagSql);
493
			}
1871 jpm 494
			if ($this->etreAppliImg()) {
495
				$this->addJoin('LEFT JOIN del_observation AS do ON (di.ce_observation = do.id_observation) ');
496
			}
497
			if ($this->etreAppliObs()) {
498
				$this->addJoin('LEFT JOIN del_image AS di ON (do.id_observation = di.ce_observation) ');
499
			}
1840 jpm 500
		}
501
	}
502
 
503
	/**
1845 jpm 504
	 * Plusieurs solutions sont disponibles dans les anciennes versions (voir DelTk).
1840 jpm 505
	 */
506
	private function ajouterContrainteTagDel() {
507
		if (isset($this->parametres['masque.tag_del'])) {
1845 jpm 508
			if (isset($this->parametres['masque.tag_del']['AND'])) {
509
				$tags = $this->parametres['masque.tag_del']['AND'];
510
				// optimisation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
511
				// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
512
				sort($tags);
513
				$tagsMotif = $this->bdd->proteger(implode('.*', $tags));
514
				$requete = 'SELECT ce_image '.
515
					'FROM del_image_tag '.
516
					'WHERE actif = 1 '.
517
					'GROUP BY ce_image '.
518
					"HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP $tagsMotif ".
519
					' -- '.__FILE__.' : '.__LINE__;
520
				$sql = $this->recupererSqlContrainteTag($requete);
521
				$this->addWhere('masque.tag_del', $sql);
1840 jpm 522
 
1845 jpm 523
			} else if (isset($this->parametres['masque.tag_del']['OR'])) {
524
				$tags = $this->parametres['masque.tag_del']['OR'];
525
				$tagsMotif = $this->bdd->proteger(implode('|', $tags));
526
				$requete = 'SELECT ce_image '.
527
					'FROM del_image_tag '.
528
					'WHERE actif = 1 '.
529
					'GROUP BY ce_image '.
530
					"HAVING GROUP_CONCAT(tag_normalise) REGEXP $tagsMotif ".
531
					' -- '.__FILE__.' : '.__LINE__;
1871 jpm 532
 
1845 jpm 533
				$sql = $this->recupererSqlContrainteTag($requete);
534
				$this->addWhere('masque.tag_del', $sql);
1840 jpm 535
			}
536
		}
537
	}
538
 
539
	private function recupererSqlContrainteTag($requete) {
540
		$resultats = $this->bdd->recupererTous($requete);
541
		$ids = array();
542
		foreach ($resultats as $resultat) {
543
			$ids[] = $resultat['ce_image'];
544
		}
545
 
546
		if (!empty($ids)) {
547
			$clauseIn = implode(',', $ids);
548
		} else {
549
			$clauseIn = 'SELECT ce_image FROM del_image_tag WHERE false';
550
		}
1871 jpm 551
		return "di.id_image IN ($clauseIn)";
1840 jpm 552
	}
553
 
554
	/**
555
	 * Partie spécifique à PictoFlora:
1863 jpm 556
	 * Attention : si le critère de tri n'est pas suffisant, les résultats affichés peuvent varier à chaque appel
557
	 * de la même page de résultat de PictoFlora.
1840 jpm 558
	 */
559
	public function definirOrdreSqlAppliImg() {
560
		$ordre = $this->parametres['ordre'];
561
 
562
		switch ($this->parametres['tri']) {
1863 jpm 563
			case 'moyenne-arithmetique' :
564
				$this->addOrderBy("dis.moyenne $ordre, dis.nb_votes $ordre, id_image DESC");
1840 jpm 565
				break;
566
			case 'points' :
1863 jpm 567
				$this->addOrderBy("dis.nb_points $ordre, dis.moyenne $ordre, dis.nb_votes $ordre, id_image DESC");
1840 jpm 568
				break;
569
			case 'tags' :
1863 jpm 570
				$this->addOrderBy("dis.nb_tags $ordre, id_image DESC");
1840 jpm 571
				break;
572
			case 'date_observation' :
1871 jpm 573
				$this->addOrderBy("date_observation $ordre, ce_observation $ordre");
1840 jpm 574
				break;
1863 jpm 575
			case 'date_transmission' :
1840 jpm 576
			default:
1871 jpm 577
				$this->addOrderBy("di.date_transmission $ordre, ce_observation $ordre");
1840 jpm 578
		}
579
	}
580
 
1845 jpm 581
	public function definirOrdreSqlAppliObs() {
582
		$ordre = $this->parametres['ordre'];
583
 
584
		// parmi self::$tri_possible
585
		switch ($this->parametres['tri']) {
586
			case 'date_observation' :
587
				$this->addOrderBy("date_observation $ordre, id_observation $ordre");
588
				break;
589
			default:
1871 jpm 590
				$this->addOrderBy("do.date_transmission $ordre, id_observation $ordre");
1845 jpm 591
		}
592
	}
593
 
1840 jpm 594
	public function getAliasDesChamps($champsEtAlias, $select = null, $prefix = null) {
595
		$arr = ($select) ? array_intersect_key($champsEtAlias, array_flip($select)) :  $champsEtAlias;
596
		$keys = array_keys($arr);
597
 
598
		if ($prefix) {
599
			array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
600
		} else {
601
			array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
602
		}
603
 
604
		return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
605
	}
606
 
607
		// Charger les images et leurs votes associés
608
	public function getVotesDesImages($idsImages, $protocole = null) {
609
		if (!$idsImages) return;
610
 
611
		$mappingVotes = $this->conteneur->getParametreTableau('votes.mapping');
612
		$mappingProtocoles = $this->conteneur->getParametreTableau('protocoles.mapping');
613
 		$selectVotes = array('id_vote', 'ce_image', 'ce_protocole', 'ce_utilisateur', 'valeur', 'date');
614
		$selectProtocole = array('id_protocole', 'intitule', 'descriptif', 'tag');
615
		$voteChamps = $this->getAliasDesChamps($mappingVotes, $selectVotes, 'v'); // "v": cf alias dans la requête
616
		$protoChamps = $this->getAliasDesChamps($mappingProtocoles, $selectProtocole, 'p');
617
		$idImgsConcat = implode(',', $idsImages);
618
 
619
		$requete = "SELECT $voteChamps, $protoChamps ".
620
			'FROM del_image_vote AS v '.
621
			'	INNER JOIN del_image_protocole AS p ON (v.ce_protocole = p.id_protocole) '.
622
			"WHERE v.ce_image IN ($idImgsConcat) ".
623
			($protocole ? "	AND v.ce_protocole = $protocole " : '').
624
			"ORDER BY FIELD(v.ce_image, $idImgsConcat) ".
625
			'-- '.__FILE__.':'.__LINE__;
626
 
627
		return $this->bdd->recupererTous($requete);
628
	}
629
 
630
	/**
631
	 * Ajoute les informations sur le protocole et les votes aux images.
632
	 *
633
	 * ATTENTION : Subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien
634
	 * plus pratique pour associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation.
635
	 * Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation)
636
	 * cf ListeImages::reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions
637
	 * simultanément lorsque c'est encore possible.
638
	 */
639
	public function ajouterInfosVotesProtocoles($votes, &$images) {
640
		if (!$votes) return;
1845 jpm 641
 
1840 jpm 642
		$mappingVotes = $this->conteneur->getParametreTableau('votes.mapping');
643
		$mappingProtocoles = $this->conteneur->getParametreTableau('protocoles.mapping');
644
 
645
		// pour chaque vote
646
		foreach ($votes as $vote) {
647
			$imgId = $vote['image.id'];
648
			$protoId = $vote['protocole.id'];
649
 
650
			if (!array_key_exists('protocoles_votes', $images[$imgId]) ||
1863 jpm 651
					!array_key_exists($protoId, $images[$imgId]['protocoles_votes'])) {
1840 jpm 652
				// extrait les champs spécifique au protocole (le LEFT JOIN de chargerVotesImage les ramène en doublons
653
				$protocole = array_intersect_key($vote, array_flip($mappingProtocoles));
654
				$images[$imgId]['protocoles_votes'][$protoId] = $protocole;
655
			}
656
 
657
			$chpsVotes = array('id_vote', 'ce_image', 'ce_utilisateur', 'valeur', 'date');
658
			$voteSelection = array_intersect_key($mappingVotes, array_flip($chpsVotes));
659
			$vote = array_intersect_key($vote, array_flip($voteSelection));
660
			$images[$imgId]['protocoles_votes'][$protoId]['votes'][$vote['vote.id']] = $vote;
661
		}
662
	}
1845 jpm 663
 
664
	public function getTotalLignesTrouvees() {
665
		$resultat = $this->bdd->recuperer('SELECT FOUND_ROWS() AS nbre');
666
		return intval($resultat['nbre']);
667
	}
1840 jpm 668
}