Subversion Repositories eFlore/Applications.del

Rev

Rev 1584 | Rev 1655 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1584 Rev 1604
Line 37... Line 37...
37
 * Et le test effectué doit matcher sur:
37
 * Et le test effectué doit matcher sur:
38
 * ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
38
 * ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
39
 *
39
 *
40
 * Par défaut les tags sont comma-separated (OU logique).
40
 * Par défaut les tags sont comma-separated (OU logique).
41
 * Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
41
 * Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
42
 * des tags séparés par des espaces recherche 
42
 * des tags séparés par des espaces recherche
43
 *
43
 *
44
 * TODO:
44
 * TODO:
45
 * -affiner la gestion de passage de mots-clefs dans le masque général.
45
 * -affiner la gestion de passage de mots-clefs dans le masque général.
46
 * - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
46
 * - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
47
 * plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
47
 * plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
Line 77... Line 77...
77
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
77
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
78
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
78
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
Line 79... Line 79...
79
 
79
 
Line 80... Line 80...
80
class ListeImages {
80
class ListeImages {
81
 
81
 
Line 82... Line 82...
82
    // TODO: PHP-x.y, ces variables devrait être des "const"
82
	// TODO: PHP-x.y, ces variables devrait être des "const"
Line 83... Line 83...
83
    static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
83
	static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
84
 
84
 
85
    static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points');
-
 
86
 
-
 
87
    // en plus de ceux dans DelTk
-
 
88
    static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
-
 
89
 
-
 
90
    static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
-
 
91
				   'tri' => 'date_transmission', 'ordre' => 'desc',
-
 
92
				   // spécifiques à PictoFlora:
-
 
93
				   'format' => 'XL');
-
 
94
 
-
 
95
    static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points))
-
 
96
 
-
 
97
    static $mappings = array(
-
 
98
	'observations' => array( // v_del_image
-
 
99
	    "id_observation" => 1,
-
 
100
	    "date_observation" => 1,
-
 
101
	    "date_transmission" => 1, 
-
 
102
	    "famille" => "determination.famille",
-
 
103
	    "nom_sel" => "determination.ns",
-
 
104
	    "nom_sel_nn" => "determination.nn",
-
 
105
	    "nom_referentiel" => "determination.referentiel",
-
 
106
	    "nt" => "determination.nt",
-
 
107
	    "ce_zone_geo" => "id_zone_geo",
-
 
108
	    "zone_geo" => 1,
-
 
109
	    "lieudit" => 1,
-
 
110
	    "station" => 1,
-
 
111
	    "milieu" => 1,
-
 
112
	    "mots_cles_texte" => "mots_cles_texte",
-
 
113
	    "commentaire" => 1,
-
 
114
	    "ce_utilisateur" => "auteur.id",
-
 
115
	    "nom_utilisateur" => "auteur.nom",
-
 
116
	    "prenom_utilisateur" => "auteur.prenom",
-
 
117
	),
-
 
118
	'images' => array( // v_del_image
-
 
119
	    'id_image' => 1,
-
 
Line -... Line 85...
-
 
85
	static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points');
-
 
86
 
-
 
87
	// en plus de ceux dans DelTk
-
 
88
	static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
-
 
89
 
-
 
90
	static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
-
 
91
		'tri' => 'date_transmission', 'ordre' => 'desc',
-
 
92
		// spécifiques à PictoFlora:
-
 
93
		'format' => 'XL');
-
 
94
 
-
 
95
	static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points))
-
 
96
 
-
 
97
	static $mappings = array(
-
 
98
		'observations' => array( // v_del_image
-
 
99
			"id_observation" => 1,
-
 
100
			"date_observation" => 1,
-
 
101
			"date_transmission" => 1,
-
 
102
			"famille" => "determination.famille",
-
 
103
			"nom_sel" => "determination.ns",
-
 
104
			"nom_sel_nn" => "determination.nn",
-
 
105
			"nom_referentiel" => "determination.referentiel",
-
 
106
			"nt" => "determination.nt",
-
 
107
			"ce_zone_geo" => "id_zone_geo",
-
 
108
			"zone_geo" => 1,
-
 
109
			"lieudit" => 1,
-
 
110
			"station" => 1,
-
 
111
			"milieu" => 1,
-
 
112
			"mots_cles_texte" => "mots_cles_texte",
-
 
113
			"commentaire" => 1,
-
 
114
			"ce_utilisateur" => "auteur.id",
-
 
115
			"nom_utilisateur" => "auteur.nom",
-
 
116
			"prenom_utilisateur" => "auteur.prenom",),
-
 
117
		'images' => array( // v_del_image
Line 120... Line 118...
120
	    // l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
118
			'id_image' => 1,
121
	    // mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
119
			// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
122
	    'i_mots_cles_texte' => 1
120
			// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
263
     * nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
260
	 * nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
264
     * Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
261
	 * Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
265
     * *chaque* couple (id_image, protocole) de la base afin de trouver les images
262
	 * *chaque* couple (id_image, protocole) de la base afin de trouver les images
266
     * les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
263
	 * les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
267
     */
264
	 */
268
    static function sqlOrderBy($p, $db, &$req) {
265
	static function sqlOrderBy($p, $db, &$req) {
269
		// parmi self::$tri_possible
266
		// parmi self::$tri_possible
270
		if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
267
		if ($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
271
		    $req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
268
			$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
272
		    return;
269
			return;
273
		}
270
		}
274
	
271
 
275
		if($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous
272
		if ($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous
276
		    $req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
273
			$req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
277
		    return;
274
			return;
278
		}
275
		}
279
	
276
 
280
		if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
277
		if ($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
281
		    $req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
278
			$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
282
		    return;
279
			return;
283
		}
280
		}
284
	
281
 
285
		if($p['tri'] == 'date_observation') {
282
		if ($p['tri'] == 'date_observation') {
286
		    $req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
283
			$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
287
		    return;
284
			return;
288
		}
285
		}
Line 289... Line 286...
289
	
286
 
290
		// tri == 'date_transmission'
287
		// tri == 'date_transmission'
291
		// avant cel:r1860, date_transmission pouvait être NULL
288
		// avant cel:r1860, date_transmission pouvait être NULL
292
		// or nous voulons de la cohérence (notamment pour phpunit)
289
		// or nous voulons de la cohérence (notamment pour phpunit)
293
		$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
290
		$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
294
    }
291
	}
295
 
292
 
296
    /*
293
	/*
297
     * in $p: un tableau de paramètres, dont:
294
	 * in $p: un tableau de paramètres, dont:
298
     * - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
295
	 * - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
299
     * - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
296
	 * - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
300
     * - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
297
	 * - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
301
     *
298
	 *
302
     * in/ou: $req: un tableau de structure de requête MySQL
299
	 * in/ou: $req: un tableau de structure de requête MySQL
303
     *
300
	 *
304
     * Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
301
	 * Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
305
     * ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
302
	 * ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
306
     * Soit directement $this->consulter() si des masque.tag* sont passés
303
	 * Soit directement $this->consulter() si des masque.tag* sont passés
307
     * (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
304
	 * (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
308
     * Soit via sqlAddMasqueConstraint():
305
	 * Soit via sqlAddMasqueConstraint():
309
     * (pas de split, "OR" entre chaque condition) [ comportement historique ]
306
	 * (pas de split, "OR" entre chaque condition) [ comportement historique ]
310
     * équivalent à:
307
	 * équivalent à:
311
     * (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
308
	 * (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
312
     *
-
 
313
     */
309
	 *
314
    static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
310
	 */
315
		// TODO implement dans DelTk ?
311
	static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
316
		if(!empty($p['masque.milieu'])) {
312
		// TODO implement dans DelTk ?
317
		    $req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
313
		if (!empty($p['masque.milieu'])) {
318
		}
314
			$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
319
	
315
		}
320
	
316
 
321
		/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
317
		/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
322
		   celui-ci indique sur quels votes porte l'AVG.
318
			celui-ci indique sur quels votes porte l'AVG.
323
		   (c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
319
			(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
324
		/* TODO: perf problème:
320
		/* TODO: perf problème:
325
		   1) SQL_CALC_FOUND_ROWS: fixable en:
321
			1) SQL_CALC_FOUND_ROWS: fixable en:
326
	           - dissociant le comptage de la récup d'id + javascript async
322
				- dissociant le comptage de la récup d'id + javascript async
327
	           - ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
323
				- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
328
	           (paramètre booléen "with-total" par exemple)
324
				(paramètre booléen "with-total" par exemple)
329
		   2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
325
			2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
330
	           JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
326
				JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
331
	           Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
327
				Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
332
	           jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
328
				jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
333
		   3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
329
			3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
334
	           (cel_images/cel_obs_images/cel_obs/del_image_stat)
330
				(cel_images/cel_obs_images/cel_obs/del_image_stat)
335
	           Cependant c'est à l'optimiseur de définir son ordre préféré. */
331
				Cependant c'est à l'optimiseur de définir son ordre préféré. */
336
		if($p['tri'] == 'votes' || $p['tri'] == 'points') {
332
		if ($p['tri'] == 'votes' || $p['tri'] == 'points') {
337
		    // $p['protocole'] *est* défini (cf requestFilterParams())
333
			// $p['protocole'] *est* défini (cf requestFilterParams())
338
		    // petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
334
			// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
339
		    if($p['ordre'] == 'desc') {
335
			if($p['ordre'] == 'desc') {
340
				// pas de group by nécessaire pour cette jointure
336
				// pas de group by nécessaire pour cette jointure
341
				// PRIMARY KEY (`ce_image`, `ce_protocole`)
337
				// PRIMARY KEY (`ce_image`, `ce_protocole`)
342
				$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'.
338
				$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'.
343
							 ' ON vdi.id_image = dis.ce_image'.
339
					 ' ON vdi.id_image = dis.ce_image'.
344
							 ' AND dis.ce_protocole = %d',
340
					 ' AND dis.ce_protocole = %d',
345
							 $p['protocole']);
341
					 $p['protocole']);
346
		    } else {
342
			} else {
347
				$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'.
343
				$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'.
348
							 ' ON vdi.id_image = dis.ce_image'.
344
					 ' ON vdi.id_image = dis.ce_image'.
349
							 ' AND dis.ce_protocole = %d',
345
					 ' AND dis.ce_protocole = %d',
350
							 $p['protocole']);
346
					 $p['protocole']);
351
				// nécessaire (dup ce_image dans del_image_stat)
347
				// nécessaire (dup ce_image dans del_image_stat)
352
				$req['groupby'][] = 'vdi.id_observation';
348
				$req['groupby'][] = 'vdi.id_observation';
353
		    }
349
			}
354
		}
350
		}
355
	
351
 
356
		if($p['tri'] == 'tags') {
352
		if ($p['tri'] == 'tags') {
357
		    $req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
353
			$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
Line -... Line 474...
-
 
474
			' ORDER BY %s'.
-
 
475
			' LIMIT %d, %d -- %s',
-
 
476
 
-
 
477
			$req['join'] ? implode(' ', array_unique($req['join'])) : '',
-
 
478
			$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
486
			
479
 
487
	    $req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
480
			$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
488
			
481
 
489
	    $req['orderby'],
482
			$req['orderby'],
490
			
483
 
491
	    $p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
484
			$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
492
    }
485
	}
493
 
486
 
494
    static function chargerImages($db, $idImg) {
487
	static function chargerImages($db, $idImg) {
495
		$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
488
		$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
496
		$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
489
		$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
497
		
490
 
498
		return $db->recupererTous(sprintf('SELECT '.
491
		return $db->recupererTous(sprintf('SELECT '.
499
					  ' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
492
			' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
500
					  ' %1$s, %2$s FROM v_del_image '.
493
			' %1$s, %2$s FROM v_del_image '.
501
					  ' WHERE %3$s'.
494
			' WHERE %3$s'.
502
					  ' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN()
495
			' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN()
503
					  ' -- %5$s',
496
			' -- %5$s',
504
					  $obs_fields, $image_fields,
497
			$obs_fields, $image_fields,
505
					  sprintf('id_image IN (%s)', implode(',', $idImg)),
498
			sprintf('id_image IN (%s)', implode(',', $idImg)),
506
					  sprintf('FIELD(id_image, %s)', implode(',', $idImg)),
499
			sprintf('FIELD(id_image, %s)', implode(',', $idImg)),
507
					  __FILE__ . ':' . __LINE__));
500
			__FILE__ . ':' . __LINE__));
508
    }
501
	}
509
 
502
 
510
    /* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
503
	/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
511
       de manière identique à la seule différence que:
504
		de manière identique à la seule différence que:
512
       1) ils sont combinés par des "OU" logiques plutôt que des "ET".
505
		1) ils sont combinés par des "OU" logiques plutôt que des "ET".
513
       2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
506
		2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
514
       Tous les mots-clefs doivent matcher et sont séparés par des espaces
507
		Tous les mots-clefs doivent matcher et sont séparés par des espaces
515
       (dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
508
		(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
516
       Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
509
		Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
517
    static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
510
	static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
518
		if(!empty($p['masque'])) {
511
		if (!empty($p['masque'])) {
519
		    $or_params = array('masque.auteur' => $p['masque'],
512
			$or_params = array('masque.auteur' => $p['masque'],
520
				       'masque.departement' => $p['masque'],
513
				'masque.departement' => $p['masque'],
521
				       'masque.commune' => $p['masque'], // TODO/XXX ?
514
				'masque.commune' => $p['masque'], // TODO/XXX ?
522
				       'masque.id_zone_geo' => $p['masque'],
515
				'masque.id_zone_geo' => $p['masque'],
523
	
516
 
524
				       /* tous-deux remplacent masque.tag
517
				/* tous-deux remplacent masque.tag
525
					  mais sont traité séparément des requestFilterParams() */
518
				mais sont traité séparément des requestFilterParams() */
526
				       // 'masque.tag_cel' => $p['masque'],
519
				// 'masque.tag_cel' => $p['masque'],
527
				       // 'masque.tag_pictoflora' => $p['masque'],
520
				// 'masque.tag_pictoflora' => $p['masque'],
528
	
521
 
529
				       'masque.ns' => $p['masque'],
522
				'masque.ns' => $p['masque'],
530
				       'masque.famille' => $p['masque'],
523
				'masque.famille' => $p['masque'],
531
				       'masque.date' => $p['masque'],
524
				'masque.date' => $p['masque'],
532
				       'masque.genre' => $p['masque'],
525
				'masque.genre' => $p['masque'],
533
				       'masque.milieu' => $p['masque'],
526
				'masque.milieu' => $p['masque'],
534
		    		   'masque.tag_cel' => $p['masque'],
527
				'masque.tag_cel' => $p['masque'],
535
		    	     'masque.tag_pictoflora' => $p['masque'],
528
				'masque.tag_pictoflora' => $p['masque'],
536
	
529
 
537
				       // tri est aussi nécessaire car affecte les contraintes de JOIN
530
				// tri est aussi nécessaire car affecte les contraintes de JOIN
538
				       'tri' => $p['tri'],
531
				'tri' => $p['tri'],
539
				       'ordre' => $p['ordre']);
532
				'ordre' => $p['ordre']);
540
		    if (array_key_exists('protocole', $p)) {
533
			if (array_key_exists('protocole', $p)) {
541
		    	$or_params['protocole'] = $p['protocole'];
534
				$or_params['protocole'] = $p['protocole'];
542
		    }
535
			}
543
	
536
 
544
		    /* Cependant les champs spécifiques ont priorité sur le masque général.
537
			/* Cependant les champs spécifiques ont priorité sur le masque général.
545
		       Pour cette raison nous supprimons la génération de SQL du masque général sur les
538
				Pour cette raison nous supprimons la génération de SQL du masque général sur les
546
		       champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
539
				champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
547
		    if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
540
			if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
548
		    if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
541
			if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
549
		    if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
542
			if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
Line 550... Line 543...
550
		    if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
543
			if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
551
		    if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
544
			if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
552
		    if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
545
			if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
553
		    if(isset($p['masque.date'])) unset($or_params['masque.date']);
546
			if(isset($p['masque.date'])) unset($or_params['masque.date']);
554
		    if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
547
			if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
555
		    if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
548
			if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
556
		    if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
549
			if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
557
		    if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
550
			if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
558
 
551
 
-
 
552
			$or_masque = array_merge(
559
		    $or_masque = array_merge(
553
				DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
560
				DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
554
				self::requestFilterParams($or_params)
561
				self::requestFilterParams($or_params)
555
			);
562
		    );
556
 
563
	
557
			/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
564
		    /* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
558
				postulés comme séparés par des espaces, et doivent être tous matchés. */
565
		       postulés comme séparés par des espaces, et doivent être tous matchés. */
559
			if (isset($or_params['masque.tag_cel'])) {
566
		    if(isset($or_params['masque.tag_cel']))
560
				$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
567
			$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
561
			}
568
		    if(isset($or_params['masque.tag_pictoflora']))
562
			if (isset($or_params['masque.tag_pictoflora'])) {
569
			$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
563
				$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
570
	
564
			}
571
	
565
 
572
		    // pas de select, groupby & co ici: uniquement 'join' et 'where'
566
			// pas de select, groupby & co ici: uniquement 'join' et 'where'
573
		    $or_req = array('join' => array(), 'where' => array());
567
			$or_req = array('join' => array(), 'where' => array());
574
		    DelTk::sqlAddConstraint($or_masque, $db, $or_req);
568
			DelTk::sqlAddConstraint($or_masque, $db, $or_req);
575
	
569
 
576
		    self::sqlAddConstraint($or_masque, $db, $or_req);
570
			self::sqlAddConstraint($or_masque, $db, $or_req);
577
	
571
 
578
		    if($or_req['where']) {
572
			if ($or_req['where']) {
579
			$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
573
				$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
580
			// utile au cas ou des jointures seraient rajoutées
574
				// utile au cas ou des jointures seraient rajoutées
581
			$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
575
				$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
582
		    }
576
			}
583
		}
577
		}
584
    }
578
	}
585
 
579
 
586
 
580
 
587
    // cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
581
	// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
588
    // (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
582
	// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
589
    static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
583
	static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
590
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
584
		// XXX: cf Observation.php::consulter(), nous pourriouns ici
591
		// conserver les valeurs vides (pour les phptests notamment, ou non)
585
		// conserver les valeurs vides (pour les phptests notamment, ou non)
592
		// $obs = array_map('array_filter', $obs);
586
		// $obs = array_map('array_filter', $obs);
593
		$obs_merged = $obs_keyed_by_id_image = array();
587
		$obs_merged = $obs_keyed_by_id_image = array();
594
		foreach($obs as $o) {
588
		foreach ($obs as $o) {
595
		    // ceci nous complique la tâche pour le reste du processing...
589
			// ceci nous complique la tâche pour le reste du processing...
596
		    $id = $o['jsonindex'];
590
			$id = $o['jsonindex'];
597
		    // ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
591
			// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
598
		    // et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
592
			// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
599
		    // mais tout deux partage leur référence à "protocole"
593
			// mais tout deux partage leur référence à "protocole"
600
		    $image = array(
-
 
601
				'id_image' => $o['id_image'],		
-
 
602
				'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
-
 
603
				'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
594
			$image = array(
604
		    );
-
 
605
	
595
				'id_image' => $o['id_image'],
606
		    unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
-
 
-
 
596
				'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
Line -... Line 597...
-
 
597
				'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
-
 
598
			);
Line -... Line 599...
-
 
599
 
-
 
600
			unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
Line 607... Line 601...
607
		    if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
601
			if (!isset($obs_merged[$id])) {
608
		    $obs_merged[$id]['observation'] = $o;
602
				$obs_merged[$id] = $image;
609
		    $obs_merged[$id]['protocoles_votes'] = array();
603
			}
610
				
604
			$obs_merged[$id]['observation'] = $o;
611
		    $obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
605
			$obs_merged[$id]['protocoles_votes'] = array();
612
		}
606
 
613
	
607
			$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] =& $obs_merged[$id]['protocoles_votes'];
614
		return array($obs_merged,$obs_keyed_by_id_image);
608
		}
615
    }
609
 
616
 
610
		return array($obs_merged,$obs_keyed_by_id_image);
617
 
611
	}
-
 
612
 
618
 
613
	// complete & override DelTk::requestFilterParams() (même usage)
-
 
614
	static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
619
    // complete & override DelTk::requestFilterParams() (même usage)
615
		if ($parametres_autorises) { // filtrage de toute clef inconnue
620
    static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
616
			$params = array_intersect_key($params, array_flip($parametres_autorises));
621
		if($parametres_autorises) { // filtrage de toute clef inconnue
617
		}
622
		    $params = array_intersect_key($params, array_flip($parametres_autorises));
618
 
623
		}
619
		$p = array();
624
	
620
		$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
625
		$p = array();
621
		$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
626
		$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
622
 
627
		$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
623
		// "milieu" inutile pour IdentiPlantes ?
628
	
624
		if (isset($params['masque.milieu'])) {
629
		// "milieu" inutile pour IdentiPlantes ?
625
			$p['masque.milieu'] = trim($params['masque.milieu']);
630
		if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
626
		}
631
	
627
 
632
		// compatibilité
628
		// compatibilité
633
		if(isset($params['masque.tag'])) {
-
 
634
		    $params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
-
 
635
		}
-
 
636
	
-
 
Line -... Line 629...
-
 
629
		if (isset($params['masque.tag'])) {
-
 
630
			$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
Line 637... Line -...
637
		if($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {
-
 
638
		    // ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
-
 
639
		    if(!isset($params['protocole']) || !is_numeric($params['protocole'])) {
-
 
640
				$p['protocole'] = self::$default_proto;
-
 
641
		    } else {
-
 
642
				$p['protocole'] = intval($params['protocole']);
-
 
643
		    }
-
 
644
		}
-
 
645
	
-
 
646
		return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
-
 
647
    }
-
 
648
 
-
 
649
 
-
 
650
 
-
 
651
    // met à jour *toutes* les stats de nombre de tags et de moyenne des votes
-
 
652
    static function _update_statistics($db) {
-
 
653
	$db->requeter("TRUNCATE TABLE del_image_stat");
-
 
654
	$db->requeter(<<<EOF
-
 
655
INSERT INTO `del_image_stat` (
-
 
656
	SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags 
-
 
657
	FROM `tb_cel`.`cel_images` ci 
-
 
658
	LEFT JOIN 
-
 
659
	( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes FROM del_image_vote 
-
 
660
	  GROUP BY ce_image, ce_protocole ) AS divo
631
		}
-
 
632
 
661
	ON ci.id_image = divo.ce_image 
633
		if ($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {