Subversion Repositories eFlore/Applications.del

Rev

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

Rev 1564 Rev 1584
Line 310... Line 310...
310
     * équivalent à:
310
     * équivalent à:
311
     * (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
311
     * (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
312
     *
312
     *
313
     */
313
     */
314
    static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
314
    static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
315
	// TODO implement dans DelTk ?
315
		// TODO implement dans DelTk ?
316
	if(!empty($p['masque.milieu'])) {
316
		if(!empty($p['masque.milieu'])) {
317
	    $req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
317
		    $req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
318
	}
318
		}
319
 
319
	
320
 
320
	
321
	/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
321
		/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
322
	   celui-ci indique sur quels votes porte l'AVG.
322
		   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) */
323
		   (c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
324
	/* TODO: perf problème:
324
		/* TODO: perf problème:
325
	   1) SQL_CALC_FOUND_ROWS: fixable en:
325
		   1) SQL_CALC_FOUND_ROWS: fixable en:
326
           - dissociant le comptage de la récup d'id + javascript async
326
	           - 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
327
	           - 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)
328
	           (paramètre booléen "with-total" par exemple)
329
	   2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
329
		   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".
330
	           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
331
	           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é
332
	           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:
333
		   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)
334
	           (cel_images/cel_obs_images/cel_obs/del_image_stat)
335
           Cependant c'est à l'optimiseur de définir son ordre préféré. */
335
	           Cependant c'est à l'optimiseur de définir son ordre préféré. */
336
	if($p['tri'] == 'votes' || $p['tri'] == 'points') {
336
		if($p['tri'] == 'votes' || $p['tri'] == 'points') {
337
	    // $p['protocole'] *est* défini (cf requestFilterParams())
337
		    // $p['protocole'] *est* défini (cf requestFilterParams())
338
	    // petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
338
		    // petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
339
	    if($p['ordre'] == 'desc') {
339
		    if($p['ordre'] == 'desc') {
340
		// pas de group by nécessaire pour cette jointure
340
				// pas de group by nécessaire pour cette jointure
341
		// PRIMARY KEY (`ce_image`, `ce_protocole`)
341
				// PRIMARY KEY (`ce_image`, `ce_protocole`)
342
		$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
342
				$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'.
343
					 ' ON vdi.id_image = dis.ce_image'.
343
							 ' ON vdi.id_image = dis.ce_image'.
344
					 ' AND dis.ce_protocole = %d',
344
							 ' AND dis.ce_protocole = %d',
345
					 $p['protocole']);
345
							 $p['protocole']);
346
	    } else {
346
		    } else {
347
		$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
347
				$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'.
348
					 ' ON vdi.id_image = dis.ce_image'.
348
							 ' ON vdi.id_image = dis.ce_image'.
349
					 ' AND dis.ce_protocole = %d',
349
							 ' AND dis.ce_protocole = %d',
350
					 $p['protocole']);
350
							 $p['protocole']);
351
		// nécessaire (dup ce_image dans del_image_stat)
351
				// nécessaire (dup ce_image dans del_image_stat)
352
		$req['groupby'][] = 'vdi.id_observation';
352
				$req['groupby'][] = 'vdi.id_observation';
353
	    }
353
		    }
354
	}
354
		}
355
 
355
	
356
	if($p['tri'] == 'tags') {
356
		if($p['tri'] == 'tags') {
357
	    $req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
357
		    $req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
358
				     ($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
358
					     ($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
359
	    // nécessaire (dup ce_image dans del_image_stat)
359
		    // nécessaire (dup ce_image dans del_image_stat)
360
	    $req['groupby'][] = 'vdi.id_observation';
360
		    $req['groupby'][] = 'vdi.id_observation';
361
	}
361
		}
362
 
362
	
363
	// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
363
		// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
364
	// sont toujours présentes; bien que parfois NULL.
364
		// sont toujours présentes; bien que parfois NULL.
365
	if($p['masque.tag_cel']) {
365
		if($p['masque.tag_cel']) {
366
	    if(isset($p['masque.tag_cel']['AND'])) {
366
		    if(isset($p['masque.tag_cel']['AND'])) {
367
		// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
367
			// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
368
		// et auquel cas laisser au client le choix du couteux "%" ?
368
			// et auquel cas laisser au client le choix du couteux "%" ?
369
		$tags = $p['masque.tag_cel']['AND'];
369
			$tags = $p['masque.tag_cel']['AND'];
370
		array_walk($tags, create_function('&$val, $k, $db',
370
			array_walk($tags, create_function('&$val, $k, $db',
371
						  '$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
371
							  '$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
372
																  $db->proteger("%".$val."%"));'),
372
																	  $db->proteger("%".$val."%"));'),
373
			   $db);
373
				   $db);
374
		$req['where'][] = '(' . implode(' AND ', $tags) . ')';
374
			$req['where'][] = '(' . implode(' AND ', $tags) . ')';
375
	    }
375
		    }
376
	    else {
376
		    else {
377
		$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
377
			$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
378
					  $db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
378
						  $db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
379
	    }
379
		    }
380
	}
380
		}
381
 
381
	
382
	if($p['masque.tag_pictoflora']) {
382
		if($p['masque.tag_pictoflora']) {
383
	    // inutilisable pour l'instant
383
		    // inutilisable pour l'instant
384
	    // self::sqlAddPictoFloraTagConstraint1($p, $db, $req);
384
		    // self::sqlAddPictoFloraTagConstraint1($p, $db, $req);
385
 
385
	
386
	    // intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery)
386
		    // intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery)
387
	    // self::sqlAddPictoFloraTagConstraint2($p, $db, $req);
387
		    // self::sqlAddPictoFloraTagConstraint2($p, $db, $req);
388
 
388
	
389
	    // approche fiable mais sous-optimale
389
		    // approche fiable mais sous-optimale
390
	    self::sqlAddPictoFloraTagConstraint3($p, $db, $req);
390
		    self::sqlAddPictoFloraTagConstraint3($p, $db, $req);
391
	}
391
		}
392
    }
392
    }
Line 393... Line 393...
393
 
393
 
394
    /* approche intéressante si les deux problèmes suivants peuvent être résolu:
394
    /* approche intéressante si les deux problèmes suivants peuvent être résolu:
395
       - LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
395
       - LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
Line 513... Line 513...
513
       2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
513
       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
514
       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)
515
       (dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
516
       Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
516
       Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
517
    static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
517
    static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
518
	if(!empty($p['masque'])) {
518
		if(!empty($p['masque'])) {
519
	    $or_params = array('masque.auteur' => $p['masque'],
519
		    $or_params = array('masque.auteur' => $p['masque'],
520
			       'masque.departement' => $p['masque'],
520
				       'masque.departement' => $p['masque'],
521
			       'masque.commune' => $p['masque'], // TODO/XXX ?
521
				       'masque.commune' => $p['masque'], // TODO/XXX ?
522
			       'masque.id_zone_geo' => $p['masque'],
522
				       'masque.id_zone_geo' => $p['masque'],
523
 
523
	
524
			       /* tous-deux remplacent masque.tag
524
				       /* tous-deux remplacent masque.tag
525
				  mais sont traité séparément des requestFilterParams() */
525
					  mais sont traité séparément des requestFilterParams() */
526
			       // 'masque.tag_cel' => $p['masque'],
526
				       // 'masque.tag_cel' => $p['masque'],
527
			       // 'masque.tag_pictoflora' => $p['masque'],
527
				       // 'masque.tag_pictoflora' => $p['masque'],
528
 
528
	
529
			       'masque.ns' => $p['masque'],
529
				       'masque.ns' => $p['masque'],
530
			       'masque.famille' => $p['masque'],
530
				       'masque.famille' => $p['masque'],
531
			       'masque.date' => $p['masque'],
531
				       'masque.date' => $p['masque'],
532
			       'masque.genre' => $p['masque'],
532
				       'masque.genre' => $p['masque'],
533
			       'masque.milieu' => $p['masque'],
533
				       'masque.milieu' => $p['masque'],
534
	    		   'masque.tag_cel' => $p['masque'],
534
		    		   'masque.tag_cel' => $p['masque'],
535
	    	     'masque.tag_pictoflora' => $p['masque'],
535
		    	     'masque.tag_pictoflora' => $p['masque'],
536
 
536
	
537
			       // tri est aussi nécessaire car affecte les contraintes de JOIN
537
				       // tri est aussi nécessaire car affecte les contraintes de JOIN
538
			       'tri' => $p['tri'],
538
				       'tri' => $p['tri'],
539
			       'ordre' => $p['ordre']);
539
				       'ordre' => $p['ordre']);
-
 
540
		    if (array_key_exists('protocole', $p)) {
-
 
541
		    	$or_params['protocole'] = $p['protocole'];
-
 
542
		    }
540
 
543
	
541
	    /* Cependant les champs spécifiques ont priorité sur le masque général.
544
		    /* Cependant les champs spécifiques ont priorité sur le masque général.
542
	       Pour cette raison nous supprimons la génération de SQL du masque général sur les
545
		       Pour cette raison nous supprimons la génération de SQL du masque général sur les
543
	       champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
546
		       champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
544
	    if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
547
		    if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
545
	    if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
548
		    if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
546
	    if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
549
		    if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
547
	    if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
550
		    if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
548
	    if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
551
		    if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
549
	    if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
552
		    if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
550
	    if(isset($p['masque.date'])) unset($or_params['masque.date']);
553
		    if(isset($p['masque.date'])) unset($or_params['masque.date']);
551
	    if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
554
		    if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
552
	    if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
555
		    if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
553
	    if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
556
		    if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
554
	    if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
557
		    if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
555
 
558
 
556
	    $or_masque = array_merge(
559
		    $or_masque = array_merge(
557
		DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
560
				DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
558
		self::requestFilterParams($or_params));
561
				self::requestFilterParams($or_params)
-
 
562
		    );
559
 
563
	
560
	    /* 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
561
	       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. */
562
	    if(isset($or_params['masque.tag_cel']))
566
		    if(isset($or_params['masque.tag_cel']))
563
		$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
567
			$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
564
	    if(isset($or_params['masque.tag_pictoflora']))
568
		    if(isset($or_params['masque.tag_pictoflora']))
565
		$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
569
			$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
566
 
570
	
567
 
571
	
568
	    // pas de select, groupby & co ici: uniquement 'join' et 'where'
572
		    // pas de select, groupby & co ici: uniquement 'join' et 'where'
569
	    $or_req = array('join' => array(), 'where' => array());
573
		    $or_req = array('join' => array(), 'where' => array());
570
	    DelTk::sqlAddConstraint($or_masque, $db, $or_req);
574
		    DelTk::sqlAddConstraint($or_masque, $db, $or_req);
-
 
575
	
571
	    self::sqlAddConstraint($or_masque, $db, $or_req);
576
		    self::sqlAddConstraint($or_masque, $db, $or_req);
572
 
577
	
573
	    if($or_req['where']) {
578
		    if($or_req['where']) {
574
		$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
579
			$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
575
		// utile au cas ou des jointures seraient rajoutées
580
			// utile au cas ou des jointures seraient rajoutées
576
		$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
581
			$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
577
	    }
582
		    }
578
	}
583
		}
579
    }
584
    }
Line 580... Line 585...
580
 
585
 
581
 
586
 
Line 611... Line 616...
611
 
616
 
612
 
617
 
613
 
618
 
614
    // complete & override DelTk::requestFilterParams() (même usage)
619
    // complete & override DelTk::requestFilterParams() (même usage)
615
    static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
620
    static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
616
	if($parametres_autorises) { // filtrage de toute clef inconnue
621
		if($parametres_autorises) { // filtrage de toute clef inconnue
617
	    $params = array_intersect_key($params, array_flip($parametres_autorises));
622
		    $params = array_intersect_key($params, array_flip($parametres_autorises));
618
	}
623
		}
619
 
624
	
620
	$p = array();
625
		$p = array();
621
	$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
626
		$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
622
	$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
627
		$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
623
 
628
	
624
	// "milieu" inutile pour IdentiPlantes ?
629
		// "milieu" inutile pour IdentiPlantes ?
625
	if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
630
		if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
626
 
631
	
627
	// compatibilité
632
		// compatibilité
628
	if(isset($params['masque.tag'])) {
633
		if(isset($params['masque.tag'])) {
629
	    $params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
634
		    $params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
630
	}
635
		}
631
 
636
	
632
	if($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {
637
		if($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {
633
	    // ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
638
		    // ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
634
	    if(!isset($params['protocole']) || !is_numeric($params['protocole']))
639
		    if(!isset($params['protocole']) || !is_numeric($params['protocole'])) {
-
 
640
				$p['protocole'] = self::$default_proto;
635
		$p['protocole'] = self::$default_proto;
641
		    } else {
636
	    else
642
				$p['protocole'] = intval($params['protocole']);
637
		$p['protocole'] = intval($params['protocole']);
643
		    }
638
	}
644
		}
Line 639... Line 645...
639
 
645