Subversion Repositories eFlore/Applications.del

Compare Revisions

Ignore whitespace Rev 1844 → Rev 1845

/trunk/services/bibliotheque/Sql.php
302,6 → 302,41
}
}
 
public function ajouterConstrainteAppliObs() {
$this->ajouterContrainteTagCel();
$this->ajouterContrainteType();
// TODO : ATTENTION -> vue que l'on utilise une vue basée sur les images, nous devons grouper par obs
$this->addGroupBy('vdi.id_observation');
}
 
/**
* @param $req: la représentation de la requête MySQL complète, à amender.
*/
private function ajouterContrainteType() {
if (array_key_exists('adeterminer', $this->parametres['masque.type'])) {
// Récupèration de toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
$this->addWhere('masque.type', '('.
'vdi.certitude = "aDeterminer" '.
'OR vdi.certitude = "douteux" '.
'OR vdi.mots_cles_texte LIKE "%aDeterminer%" '.
'OR vdi.nom_sel_nn IS NULL '.
'OR vdi.nom_sel_nn = 0 '.// il ne DEVRAIT pas y avoir d'entrées à 0, mais il y en a quand-même !!
')');
}
if (array_key_exists('validees', $this->parametres['masque.type'])) {
// Récupèration de toutes les observations ayant un commentaire doté de proposition_retenue = 1
$this->addJoin('INNER JOIN del_commentaire AS dc '.
'ON (vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1) ');
}
 
if (array_key_exists('endiscussion', $this->parametres['masque.type'])) {
$nbreCommentaire =(int) ($this->conteneur->getParametre('observations.nb_commentaires_discussion'));
$this->addWhere('masque.type', '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc '.
"WHERE ce_observation = id_observation) > $nbreCommentaire ");
}
}
 
/**
* in $p: un tableau de paramètres, dont:
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
377,7 → 412,7
*/
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ? et auquel cas laisser au client le choix du couteux "%" ?
private function ajouterContrainteTagCel() {
if ($this->parametres['masque.tag_cel']) {
if (isset($this->parametres['masque.tag_cel'])) {
if (isset($this->parametres['masque.tag_cel']['AND'])) {
$tags = $this->parametres['masque.tag_cel']['AND'];
$clausesWhere = array();
399,99 → 434,40
}
 
/**
* Plusieurs solutions disponibles pour la gestion des contraintes des tags DEL :
* - inutilisable pour l'instant : ajouterContrainteTagDelSolution1();
* - intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery) : ajouterContrainteTagDelSolution2();
* - approche fiable mais sous-optimale : ajouterContrainteTagDelSolution3();
* Plusieurs solutions sont disponibles dans les anciennes versions (voir DelTk).
*/
private function ajouterContrainteTagDel() {
if (isset($this->parametres['masque.tag_del'])) {
$this->ajouterContrainteTagDelSolution3();
}
}
if (isset($this->parametres['masque.tag_del']['AND'])) {
$tags = $this->parametres['masque.tag_del']['AND'];
// optimisation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($tags);
$tagsMotif = $this->bdd->proteger(implode('.*', $tags));
$requete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'GROUP BY ce_image '.
"HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP $tagsMotif ".
' -- '.__FILE__.' : '.__LINE__;
$sql = $this->recupererSqlContrainteTag($requete);
$this->addWhere('masque.tag_del', $sql);
 
/** Approche intéressante si les deux problèmes suivants peuvent être résolu:
* - LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
* - dans le cas d'un ET logique, comment chercher les observations correspondantes ?
*/
private function ajouterContrainteTagDelSolution1() {
// XXX: utiliser tag plutôt que tag_normalise ?
$req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
$req['where'][] = 'dit.actif = 1';
$req['groupby'][] = 'vdi.id_image'; // TODO: nécessaire (car dup') mais risque de conflict en cas de tri (multiple GROUP BY)
// XXX: en cas de ET, possibilité du GROUP_CONCAT(), mais probablement sans grand intérêt, d'où une boucle
if (isset($p['masque.tag_del']['AND'])) {
// TODO/XXX : comment matcher les observations ayant tous les mots-clef passés ?
// ... le LEFT-JOIN n'y semble pas adapté
} else {
$protected_tags = array();
foreach ($p['masque.tag_del']['OR'] as $tag) {
$protected_tags[] = $db->proteger(strtolower($tag));
} else if (isset($this->parametres['masque.tag_del']['OR'])) {
$tags = $this->parametres['masque.tag_del']['OR'];
$tagsMotif = $this->bdd->proteger(implode('|', $tags));
$requete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'GROUP BY ce_image '.
"HAVING GROUP_CONCAT(tag_normalise) REGEXP $tagsMotif ".
' -- '.__FILE__.' : '.__LINE__;
$sql = $this->recupererSqlContrainteTag($requete);
$this->addWhere('masque.tag_del', $sql);
}
$req['where'][] = sprintf('tag_normalise IN (%s)', implode(',', $protected_tags));
}
}
 
/**
* Inutilisé pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro)
*/
private function ajouterContrainteTagDelSolution2() {
// Note à propos des 4 "@ instruction" ci-dessous (notamment sur recupererTous())
// REGEXP permet un puissant mécanisme de sélection des obs/image à qui sait
// l'utiliser, mais peut sortir une erreur en cas de REGEXP invalide
// ex: REGEX "^(".
// Pour l'heure nous ignorons ce type d'erreur car aucun de nos champ de recherche
// ne peuvent (ou ne devrait) comporter des meta-caractères
// ([])?*+\\
if (isset($p['masque.tag_del']['AND'])) {
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($p['masque.tag_del']['AND']);
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
$db->proteger(implode('.*', $p['masque.tag_del']['AND'])));
} else {
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
" GROUP BY ce_image".
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
$db->proteger(implode('|', $p['masque.tag_del']['OR'])));
}
}
 
/**
* Si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction
*/
private function ajouterContrainteTagDelSolution3() {
if (isset($this->parametres['masque.tag_del']['AND'])) {
$tags = $this->parametres['masque.tag_del']['AND'];
// optimisation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
sort($tags);
$tagsMotif = $this->bdd->proteger(implode('.*', $tags));
$requete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'GROUP BY ce_image '.
"HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP $tagsMotif ".
' -- '.__FILE__.' : '.__LINE__;
$sql = $this->recupererSqlContrainteTag($requete);
$this->addWhere('masque.tag_del', $sql);
 
} else if (isset($this->parametres['masque.tag_del']['OR'])) {
$tags = $this->parametres['masque.tag_del']['OR'];
$tagsMotif = $this->bdd->proteger(implode('|', $tags));
$requete = 'SELECT ce_image '.
'FROM del_image_tag '.
'WHERE actif = 1 '.
'GROUP BY ce_image '.
"HAVING GROUP_CONCAT(tag_normalise) REGEXP $tagsMotif ".
' -- '.__FILE__.' : '.__LINE__;
$sql = $this->recupererSqlContrainteTag($requete);
$this->addWhere('masque.tag_del', $sql);
}
}
 
private function recupererSqlContrainteTag($requete) {
$resultats = $this->bdd->recupererTous($requete);
$ids = array();
537,6 → 513,19
}
}
 
public function definirOrdreSqlAppliObs() {
$ordre = $this->parametres['ordre'];
 
// parmi self::$tri_possible
switch ($this->parametres['tri']) {
case 'date_observation' :
$this->addOrderBy("date_observation $ordre, id_observation $ordre");
break;
default:
$this->addOrderBy("date_transmission $ordre, id_observation $ordre");
}
}
 
public function getAliasDesChamps($champsEtAlias, $select = null, $prefix = null) {
$arr = ($select) ? array_intersect_key($champsEtAlias, array_flip($select)) : $champsEtAlias;
$keys = array_keys($arr);
584,7 → 573,7
*/
public function ajouterInfosVotesProtocoles($votes, &$images) {
if (!$votes) return;
 
$mappingVotes = $this->conteneur->getParametreTableau('votes.mapping');
$mappingProtocoles = $this->conteneur->getParametreTableau('protocoles.mapping');
 
606,4 → 595,9
$images[$imgId]['protocoles_votes'][$protoId]['votes'][$vote['vote.id']] = $vote;
}
}
 
public function getTotalLignesTrouvees() {
$resultat = $this->bdd->recuperer('SELECT FOUND_ROWS() AS nbre');
return intval($resultat['nbre']);
}
}
/trunk/services/bibliotheque/ParametresFiltrage.php
35,74 → 35,6
$this->parametres = $this->contexte->getQS();
}
 
 
/**
* Construit un (vulgaire) abstract syntax tree:
* "AND" => [ "tag1", "tag2" ]
* Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
* nous aurions:
* "AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
*
* Ici nous devons traiter les cas suivants:
* tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
* Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
* ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
*
* Théorie:
* 1) tags passés par "champ tag":
* - support du ET/OU, et explode par virgule.
* - si pas d'opérande détectée: "OU"
*
* 2) tags passés par "recherche générale":
* - support du ET/OU, et explode par whitespace.
* - si pas d'opérande détectée: "ET"
*
* La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
* Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
* la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
* * a,b,c => "a" $default_op "b" $default_op "c"
* * a,b AND c => "a" AND "b" AND "c"
* * a OR b AND c,d => "a" AND "b" AND "c" AND "d"
* C'est à dire par ordre décroissant de priorité:
* 1) opérande contenu dans la chaîne
* 2) opérande par défaut
* 3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
*
* // TODO: support des parenthèses, imbrications & co: "(", ")"
* // http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
* // http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
*
* @param $str: la chaîne à "parser"
* @param $operateur_par_defaut: "AND" ou "OR"
* @param $separateur_additionnel: séparateur de mots:
*/
public function construireTableauTags($str = NULL, $operateur_par_defaut, $separateur_additionnel = ',') {
if (!$str) return;
$op = $this->definirOperateurParDefaut($str, $operateur_par_defaut);
 
$mots = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
if ($separateur_additionnel) {
foreach ($mots as $index => $mot) {
$mot = trim($mot);
$mots_separes = preg_split("/$separateur_additionnel/", $mot, -1, PREG_SPLIT_NO_EMPTY);
$mots[$index] = array_shift($mots_separes);
$mots = array_merge($mots, $mots_separes);
}
}
$mots = array_filter($mots);
return array($op => $mots);
}
 
public function definirOperateurParDefaut($str, $operateur_par_defaut) {
$op = $operateur_par_defaut;
if (preg_match('/\b(ET|AND)\b/', $str)) {
$op = 'AND';
} else if(preg_match('/\b(OU|OR)\b/', $str)) {
$op = 'OR';
}
return $op;
}
 
public function filtrerUrlParamsAppliImg() {
$this->maintenirCompatibilitesParametres();
 
139,7 → 71,7
 
$this->filtrerUrlParamsGeneraux();
 
$trisPossibles = $this->conteneur->getParametre('appli_obs.tris_possibles');
$trisPossibles = $this->conteneur->getParametreTableau('appli_obs.tris_possibles');
$this->detruireParametreInvalide('tri', $trisPossibles);
 
$this->supprimerParametresFiltresInvalides();
419,6 → 351,74
}
}
 
 
/**
* Construit un (vulgaire) abstract syntax tree:
* "AND" => [ "tag1", "tag2" ]
* Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
* nous aurions:
* "AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
*
* Ici nous devons traiter les cas suivants:
* tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
* Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
* ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
*
* Théorie:
* 1) tags passés par "champ tag":
* - support du ET/OU, et explode par virgule.
* - si pas d'opérande détectée: "OU"
*
* 2) tags passés par "recherche générale":
* - support du ET/OU, et explode par whitespace.
* - si pas d'opérande détectée: "ET"
*
* La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
* Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
* la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
* * a,b,c => "a" $default_op "b" $default_op "c"
* * a,b AND c => "a" AND "b" AND "c"
* * a OR b AND c,d => "a" AND "b" AND "c" AND "d"
* C'est à dire par ordre décroissant de priorité:
* 1) opérande contenu dans la chaîne
* 2) opérande par défaut
* 3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
*
* // TODO: support des parenthèses, imbrications & co: "(", ")"
* // http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
* // http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
*
* @param $str: la chaîne à "parser"
* @param $operateur_par_defaut: "AND" ou "OR"
* @param $separateur_additionnel: séparateur de mots:
*/
public function construireTableauTags($str = null, $operateur_par_defaut, $separateur_additionnel = ',') {
if (!$str) return;
$op = $this->definirOperateurParDefaut($str, $operateur_par_defaut);
 
$mots = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
if ($separateur_additionnel) {
foreach ($mots as $index => $mot) {
$mot = trim($mot);
$mots_separes = preg_split("/$separateur_additionnel/", $mot, -1, PREG_SPLIT_NO_EMPTY);
$mots[$index] = array_shift($mots_separes);
$mots = array_merge($mots, $mots_separes);
}
}
$mots = array_filter($mots);
return array($op => $mots);
}
 
private function definirOperateurParDefaut($str, $operateur_par_defaut) {
$op = $operateur_par_defaut;
if (preg_match('/\b(ET|AND)\b/', $str)) {
$op = 'AND';
} else if(preg_match('/\b(OU|OR)\b/', $str)) {
$op = 'OR';
}
return $op;
}
 
// masque.type: ['adeterminer', 'aconfirmer', 'endiscussion', 'validees']
private function filtrerType() {
if(isset($this->parametres['masque.type'])) {