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 |
|