39,7 → 39,7 |
* |
* Par défaut les tags sont comma-separated (OU logique). |
* Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur |
* des tags séparés par des espaces recherche |
* des tags séparés par des espaces recherche |
* |
* TODO: |
* -affiner la gestion de passage de mots-clefs dans le masque général. |
79,596 → 79,568 |
|
class ListeImages { |
|
// TODO: PHP-x.y, ces variables devrait être des "const" |
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L'); |
// TODO: PHP-x.y, ces variables devrait être des "const" |
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L'); |
|
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points'); |
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points'); |
|
// en plus de ceux dans DelTk |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu'); |
// en plus de ceux dans DelTk |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu'); |
|
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10, |
'tri' => 'date_transmission', 'ordre' => 'desc', |
// spécifiques à PictoFlora: |
'format' => 'XL'); |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10, |
'tri' => 'date_transmission', 'ordre' => 'desc', |
// spécifiques à PictoFlora: |
'format' => 'XL'); |
|
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points)) |
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points)) |
|
static $mappings = array( |
'observations' => array( // v_del_image |
"id_observation" => 1, |
"date_observation" => 1, |
"date_transmission" => 1, |
"famille" => "determination.famille", |
"nom_sel" => "determination.ns", |
"nom_sel_nn" => "determination.nn", |
"nom_referentiel" => "determination.referentiel", |
"nt" => "determination.nt", |
"ce_zone_geo" => "id_zone_geo", |
"zone_geo" => 1, |
"lieudit" => 1, |
"station" => 1, |
"milieu" => 1, |
"mots_cles_texte" => "mots_cles_texte", |
"commentaire" => 1, |
"ce_utilisateur" => "auteur.id", |
"nom_utilisateur" => "auteur.nom", |
"prenom_utilisateur" => "auteur.prenom", |
), |
'images' => array( // v_del_image |
'id_image' => 1, |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex) |
'i_mots_cles_texte' => 1 |
)); |
static $mappings = array( |
'observations' => array( // v_del_image |
"id_observation" => 1, |
"date_observation" => 1, |
"date_transmission" => 1, |
"famille" => "determination.famille", |
"nom_sel" => "determination.ns", |
"nom_sel_nn" => "determination.nn", |
"nom_referentiel" => "determination.referentiel", |
"nt" => "determination.nt", |
"ce_zone_geo" => "id_zone_geo", |
"zone_geo" => 1, |
"lieudit" => 1, |
"station" => 1, |
"milieu" => 1, |
"mots_cles_texte" => "mots_cles_texte", |
"commentaire" => 1, |
"ce_utilisateur" => "auteur.id", |
"nom_utilisateur" => "auteur.nom", |
"prenom_utilisateur" => "auteur.prenom",), |
'images' => array( // v_del_image |
'id_image' => 1, |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex) |
'i_mots_cles_texte' => 1) |
); |
|
|
public function __construct(Conteneur $conteneur = null) { |
public function __construct(Conteneur $conteneur = null) { |
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur; |
$this->conteneur->chargerConfiguration('config_images.ini'); |
$this->gestionBdd = $conteneur->getGestionBdd(); |
$this->bdd = $this->gestionBdd->getBdd(); |
} |
$this->bdd = $conteneur->getGestionBdd()->getBdd(); |
} |
|
public function consulter($ressources, $parametres) { |
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas |
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur. |
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur |
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */ |
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
// pour les votes, les mappings de "Observation" nous suffisent |
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
public function consulter($ressources, $parametres) { |
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas |
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur. |
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur |
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */ |
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
// pour les votes, les mappings de "Observation" nous suffisent |
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;')); |
|
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité |
// de `ce_image` dans ces tables jointes. |
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY |
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array()); |
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité |
// de `ce_image` dans ces tables jointes. |
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY |
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array()); |
|
$db = $this->bdd; |
|
$db = $this->bdd; |
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs. |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag |
// même si ceux-ci sont improbables (pas d'<input> pour cela). |
$params_ip = DelTk::requestFilterParams($parametres, |
array_diff(DelTk::$parametres_autorises, array('masque.type')), |
$this->conteneur); |
|
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs. |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag |
// même si ceux-ci sont improbables (pas d'<input> pour cela). |
$params_ip = DelTk::requestFilterParams($parametres, |
array_diff(DelTk::$parametres_autorises, |
array('masque.type')), |
$this->conteneur); |
// notre propre filtrage sur l'INPUT |
$params_pf = self::requestFilterParams($parametres, |
array_merge(DelTk::$parametres_autorises, self::$parametres_autorises)); |
|
// notre propre filtrage sur l'INPUT |
$params_pf = self::requestFilterParams($parametres, |
array_merge(DelTk::$parametres_autorises, |
self::$parametres_autorises)); |
/* filtrage des tags + sémantique des valeurs multiples: |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */ |
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ','); |
|
/* filtrage des tags + sémantique des valeurs multiples: |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */ |
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ','); |
|
|
if(!isset($parametres['masque.tag_pictoflora']) && isset($parametres['masque.tag'])) { |
$parametres['masque.tag_pictoflora'] = $parametres['masque.tag']; |
} |
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ','); |
if(!isset($parametres['masque.tag_pictoflora']) && isset($parametres['masque.tag'])) { |
$parametres['masque.tag_pictoflora'] = $parametres['masque.tag']; |
} |
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ','); |
|
$params = array_merge( |
DelTk::$default_params, // paramètre par défaut Identiplante |
self::$default_params, // paramètres par défaut PictoFlora |
$params_ip, // les paramètres passés, traités par Identiplante |
$params_pf); // les paramètres passés, traités par PictoFlora |
$params = array_merge( |
DelTk::$default_params, // paramètre par défaut Identiplante |
self::$default_params, // paramètres par défaut PictoFlora |
$params_ip, // les paramètres passés, traités par Identiplante |
$params_pf); // les paramètres passés, traités par PictoFlora |
|
if(isset($parametres['format'])) { |
$params['format'] = $parametres['format']; |
} |
if (isset($parametres['format'])) { |
$params['format'] = $parametres['format']; |
} |
|
// création des contraintes (génériques de DelTk) |
DelTk::sqlAddConstraint($params, $db, $req); |
// création des contraintes spécifiques (sur les tags essentiellement) |
self::sqlAddConstraint($params, $db, $req, $this->conteneur); |
// création des contraintes spécifiques impliquées par le masque général |
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur); |
// l'ORDER BY s'avére complexe |
self::sqlOrderBy($params, $db, $req); |
// création des contraintes (génériques de DelTk) |
DelTk::sqlAddConstraint($params, $db, $req); |
// création des contraintes spécifiques (sur les tags essentiellement) |
self::sqlAddConstraint($params, $db, $req, $this->conteneur); |
// création des contraintes spécifiques impliquées par le masque général |
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur); |
// l'ORDER BY s'avére complexe |
self::sqlOrderBy($params, $db, $req); |
|
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS) |
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db); |
$idobs_tab = self::getIdImages($params, $req, $db); |
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS) |
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db); |
$idobs_tab = self::getIdImages($params, $req, $db); |
|
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images |
if(!$idobs_tab) { |
$resultat = new ResultatService(); |
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')), |
'resultats' => array()); |
return $resultat; |
/* |
header('HTTP/1.0 404 Not Found'); |
// don't die (phpunit) |
throw(new Exception()); */ |
} |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images |
if (!$idobs_tab) { |
$resultat = new ResultatService(); |
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')), |
'resultats' => array()); |
return $resultat; |
} |
|
// idobs est une liste (toujours ordonnée) des id d'observations recherchées |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab)); |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']); |
|
// idobs est une liste (toujours ordonnée) des id d'observations recherchées |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab)); |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']); |
$liaisons = self::chargerImages($db, $idobs); |
|
$liaisons = self::chargerImages($db, $idobs); |
|
// debug: infos de score |
/*$infosScore = array(); |
foreach ($idobs_tab as $iot) { |
$infosScore[$iot['id_image']] = array( |
'nb_votes' => $iot['nb_votes'], |
'nb_points' => $iot['nb_points'], |
'moyenne' => $iot['moyenne'] |
); |
}*/ |
/* |
// Q&D |
$images = array(); |
$o = new Observation($this->conteneur); |
foreach($idobs as $i) { |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1)); |
} |
*/ |
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex( |
$liaisons, |
$this->conteneur->getParametre('images.url_images'), |
$params['format']); |
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex( |
$liaisons, |
$this->conteneur->getParametre('images.url_images'), |
$params['format']); |
|
// on charge les votes pour ces images et pour *tous* les protocoles |
$votes = Observation::chargerVotesImage($db, $liaisons, NULL); |
// on charge les votes pour ces images et pour *tous* les protocoles |
$votes = Observation::chargerVotesImage($db, $liaisons, NULL); |
|
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour |
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation. |
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation) |
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque |
// c'est encore possible. |
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image); |
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour |
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation. |
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation) |
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque |
// c'est encore possible. |
if ($votes) { |
Observation::mapVotesToImages($votes, $images_keyed_by_id_image); |
} |
|
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête. |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe) |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'], |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora']))); |
$resultat = new ResultatService(); |
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')), |
'resultats' => $images); |
return $resultat; |
} |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête. |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe) |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'], |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora']))); |
$resultat = new ResultatService(); |
$resultat->corps = array( |
'entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')), |
'resultats' => $images); |
return $resultat; |
} |
|
/** |
* TODO: partie spécifique liées à la complexité de PictoFlora: |
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req) |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...). |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT()) |
*/ |
static function sqlOrderBy($p, $db, &$req) { |
public function supprimer($ressources) { |
$idImage = $ressources[0]; |
$controlAcces = $this->conteneur->getControleAcces(); |
|
$retour = false; |
if ($controlAcces->controlerIpAutorisees()) { |
if ($controlAcces->etreUtilisateurAvecDroitAdmin()) { |
$urlServiceBase = $this->conteneur->getParametre('url_service_suppression_image'); |
$url = $urlServiceBase.$idImage; |
|
$clientHttp = $this->conteneur->getRestClient(); |
echo $url; |
$retour = $clientHttp->supprimer($url); |
echo $retour; |
} else { |
$message = "Vous ne pouvez pas accéder à ce service car vous n'avez pas les droits d'administrateur !\n"; |
$code = RestServeur::HTTP_CODE_ACCES_NON_AUTORISE; |
throw new Exception($message, $code); |
} |
} |
return $retour; |
} |
|
/** |
* TODO: partie spécifique liées à la complexité de PictoFlora: |
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req) |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...). |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT()) |
*/ |
static function sqlOrderBy($p, $db, &$req) { |
// parmi self::$tri_possible |
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre']; |
return; |
if ($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre']; |
return; |
} |
|
if($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre']; |
return; |
|
if ($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre']; |
return; |
} |
|
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre']; |
return; |
|
if ($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre']; |
return; |
} |
|
if($p['tri'] == 'date_observation') { |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre']; |
return; |
|
if ($p['tri'] == 'date_observation') { |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre']; |
return; |
} |
|
|
// tri == 'date_transmission' |
// avant cel:r1860, date_transmission pouvait être NULL |
// or nous voulons de la cohérence (notamment pour phpunit) |
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre']; |
} |
} |
|
/* |
* in $p: un tableau de paramètres, dont: |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON |
* |
* in/ou: $req: un tableau de structure de requête MySQL |
* |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête: |
* Soit directement $this->consulter() si des masque.tag* sont passés |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag) |
* Soit via sqlAddMasqueConstraint(): |
* (pas de split, "OR" entre chaque condition) [ comportement historique ] |
* équivalent à: |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag) |
* |
*/ |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) { |
/* |
* in $p: un tableau de paramètres, dont: |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON |
* |
* in/ou: $req: un tableau de structure de requête MySQL |
* |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête: |
* Soit directement $this->consulter() si des masque.tag* sont passés |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag) |
* Soit via sqlAddMasqueConstraint(): |
* (pas de split, "OR" entre chaque condition) [ comportement historique ] |
* équivalent à: |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag) |
* |
*/ |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) { |
// TODO implement dans DelTk ? |
if(!empty($p['masque.milieu'])) { |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%'); |
if (!empty($p['masque.milieu'])) { |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%'); |
} |
|
|
|
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné, |
celui-ci indique sur quels votes porte l'AVG. |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */ |
celui-ci indique sur quels votes porte l'AVG. |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */ |
/* TODO: perf problème: |
1) SQL_CALC_FOUND_ROWS: fixable en: |
- dissociant le comptage de la récup d'id + javascript async |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination |
(paramètre booléen "with-total" par exemple) |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission". |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue: |
(cel_images/cel_obs_images/cel_obs/del_image_stat) |
Cependant c'est à l'optimiseur de définir son ordre préféré. */ |
if($p['tri'] == 'votes' || $p['tri'] == 'points') { |
// $p['protocole'] *est* défini (cf requestFilterParams()) |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin |
if($p['ordre'] == 'desc') { |
1) SQL_CALC_FOUND_ROWS: fixable en: |
- dissociant le comptage de la récup d'id + javascript async |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination |
(paramètre booléen "with-total" par exemple) |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission". |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue: |
(cel_images/cel_obs_images/cel_obs/del_image_stat) |
Cependant c'est à l'optimiseur de définir son ordre préféré. */ |
if ($p['tri'] == 'votes' || $p['tri'] == 'points') { |
// $p['protocole'] *est* défini (cf requestFilterParams()) |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin |
if($p['ordre'] == 'desc') { |
// pas de group by nécessaire pour cette jointure |
// PRIMARY KEY (`ce_image`, `ce_protocole`) |
$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'. |
' ON vdi.id_image = dis.ce_image'. |
' AND dis.ce_protocole = %d', |
$p['protocole']); |
} else { |
' ON vdi.id_image = dis.ce_image'. |
' AND dis.ce_protocole = %d', |
$p['protocole']); |
} else { |
$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'. |
' ON vdi.id_image = dis.ce_image'. |
' AND dis.ce_protocole = %d', |
$p['protocole']); |
' ON vdi.id_image = dis.ce_image'. |
' AND dis.ce_protocole = %d', |
$p['protocole']); |
// nécessaire (dup ce_image dans del_image_stat) |
$req['groupby'][] = 'vdi.id_observation'; |
} |
} |
} |
|
if($p['tri'] == 'tags') { |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image', |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT'); |
// nécessaire (dup ce_image dans del_image_stat) |
$req['groupby'][] = 'vdi.id_observation'; |
|
if ($p['tri'] == 'tags') { |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image', |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT'); |
// nécessaire (dup ce_image dans del_image_stat) |
$req['groupby'][] = 'vdi.id_observation'; |
} |
|
|
// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*" |
// sont toujours présentes; bien que parfois NULL. |
if($p['masque.tag_cel']) { |
if(isset($p['masque.tag_cel']['AND'])) { |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ? |
// et auquel cas laisser au client le choix du couteux "%" ? |
$tags = $p['masque.tag_cel']['AND']; |
array_walk($tags, create_function('&$val, $k, $db', |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s", |
$db->proteger("%".$val."%"));'), |
$db); |
$req['where'][] = '(' . implode(' AND ', $tags) . ')'; |
} |
else { |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s", |
$db->proteger(implode('|', $p['masque.tag_cel']['OR']))); |
} |
if ($p['masque.tag_cel']) { |
if (isset($p['masque.tag_cel']['AND'])) { |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ? |
// et auquel cas laisser au client le choix du couteux "%" ? |
$tags = $p['masque.tag_cel']['AND']; |
array_walk($tags, create_function('&$val, $k, $db', |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s", |
$db->proteger("%".$val."%"));'), |
$db); |
$req['where'][] = '(' . implode(' AND ', $tags) . ')'; |
} else { |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s", |
$db->proteger(implode('|', $p['masque.tag_cel']['OR']))); |
} |
} |
|
if($p['masque.tag_pictoflora']) { |
// inutilisable pour l'instant |
// self::sqlAddPictoFloraTagConstraint1($p, $db, $req); |
|
// intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery) |
// self::sqlAddPictoFloraTagConstraint2($p, $db, $req); |
|
// approche fiable mais sous-optimale |
self::sqlAddPictoFloraTagConstraint3($p, $db, $req); |
|
if ($p['masque.tag_pictoflora']) { |
// inutilisable pour l'instant |
// self::sqlAddPictoFloraTagConstraint1($p, $db, $req); |
|
// intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery) |
// self::sqlAddPictoFloraTagConstraint2($p, $db, $req); |
|
// approche fiable mais sous-optimale |
self::sqlAddPictoFloraTagConstraint3($p, $db, $req); |
} |
} |
} |
|
/* 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 ? */ |
static function sqlAddPictoFloraTagConstraint1($p, $db, &$req) { |
// 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_pictoflora']['AND'])) { |
// TODO/XXX : comment matcher les observations ayant tous les mots-clef passés ? |
// ... le LEFT-JOIN n'y semble pas adapté |
/* 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 ? */ |
static function sqlAddPictoFloraTagConstraint1($p, $db, &$req) { |
// 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_pictoflora']['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_pictoflora']['OR'] as $tag) { |
$protected_tags[] = $db->proteger(strtolower($tag)); |
} |
$req['where'][] = sprintf('tag_normalise IN (%s)', implode(',', $protected_tags)); |
} |
} |
else { |
$protected_tags = array(); |
foreach($p['masque.tag_pictoflora']['OR'] as $tag) $protected_tags[] = $db->proteger(strtolower($tag)); |
$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) |
static function sqlAddPictoFloraTagConstraint2($p, $db, &$req) { |
// 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_pictoflora']['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_pictoflora']['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_pictoflora']['AND']))); |
// inutilisé pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro) |
static function sqlAddPictoFloraTagConstraint2($p, $db, &$req) { |
// 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_pictoflora']['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_pictoflora']['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_pictoflora']['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_pictoflora']['OR']))); |
} |
} |
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_pictoflora']['OR']))); |
} |
} |
|
// si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction |
static function sqlAddPictoFloraTagConstraint3($p, $db, &$req) { |
if(isset($p['masque.tag_pictoflora']['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_pictoflora']['AND']); |
// si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction |
static function sqlAddPictoFloraTagConstraint3($p, $db, &$req) { |
if (isset($p['masque.tag_pictoflora']['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_pictoflora']['AND']); |
|
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ... |
$ids = @$db->recupererTous(sprintf( |
"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_pictoflora']['AND'])))); |
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ... |
$ids = @$db->recupererTous(sprintf( |
"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_pictoflora']['AND'])))); |
|
// puis: |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids); |
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false'; |
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids); |
// puis: |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids); |
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false'; |
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids); |
} else { |
$ids = @$db->recupererTous(sprintf( |
"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_pictoflora']['OR'])))); |
|
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids); |
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false'; |
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids); |
} |
} |
else { |
$ids = @$db->recupererTous(sprintf( |
"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_pictoflora']['OR'])))); |
|
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids); |
$ids = !empty($ids) ? implode(',', $ids) : 'SELECT ce_image FROM del_image_tag WHERE false'; |
$req['where'][] = sprintf("vdi.id_image IN (%s)", $ids); |
static function getIdImages($p, $req, $db) { |
return $db->recupererTous(sprintf( |
'SELECT SQL_CALC_FOUND_ROWS id_image' . |
//', dis.moyenne, dis.nb_points, dis.nb_votes' . // debug |
' FROM v_del_image vdi'. |
' %s' . // LEFT JOIN if any |
' WHERE %s'. // where-clause ou TRUE |
' %s'. // group-by |
' ORDER BY %s'. |
' LIMIT %d, %d -- %s', |
|
$req['join'] ? implode(' ', array_unique($req['join'])) : '', |
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE', |
|
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '', |
|
$req['orderby'], |
|
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__)); |
} |
} |
|
static function getIdImages($p, $req, $db) { |
return $db->recupererTous(sprintf( |
'SELECT SQL_CALC_FOUND_ROWS id_image' . |
//', dis.moyenne, dis.nb_points, dis.nb_votes' . // debug |
' FROM v_del_image vdi'. |
' %s' . // LEFT JOIN if any |
' WHERE %s'. // where-clause ou TRUE |
' %s'. // group-by |
' ORDER BY %s'. |
' LIMIT %d, %d -- %s', |
|
$req['join'] ? implode(' ', array_unique($req['join'])) : '', |
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE', |
|
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '', |
|
$req['orderby'], |
|
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__)); |
} |
|
static function chargerImages($db, $idImg) { |
static function chargerImages($db, $idImg) { |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL); |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL); |
|
|
return $db->recupererTous(sprintf('SELECT '. |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'. |
' %1$s, %2$s FROM v_del_image '. |
' WHERE %3$s'. |
' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN() |
' -- %5$s', |
$obs_fields, $image_fields, |
sprintf('id_image IN (%s)', implode(',', $idImg)), |
sprintf('FIELD(id_image, %s)', implode(',', $idImg)), |
__FILE__ . ':' . __LINE__)); |
} |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'. |
' %1$s, %2$s FROM v_del_image '. |
' WHERE %3$s'. |
' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN() |
' -- %5$s', |
$obs_fields, $image_fields, |
sprintf('id_image IN (%s)', implode(',', $idImg)), |
sprintf('FIELD(id_image, %s)', implode(',', $idImg)), |
__FILE__ . ':' . __LINE__)); |
} |
|
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités |
de manière identique à la seule différence que: |
1) ils sont combinés par des "OU" logiques plutôt que des "ET". |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique: |
Tous les mots-clefs doivent matcher et sont séparés par des espaces |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql) |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */ |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) { |
if(!empty($p['masque'])) { |
$or_params = array('masque.auteur' => $p['masque'], |
'masque.departement' => $p['masque'], |
'masque.commune' => $p['masque'], // TODO/XXX ? |
'masque.id_zone_geo' => $p['masque'], |
|
/* tous-deux remplacent masque.tag |
mais sont traité séparément des requestFilterParams() */ |
// 'masque.tag_cel' => $p['masque'], |
// 'masque.tag_pictoflora' => $p['masque'], |
|
'masque.ns' => $p['masque'], |
'masque.famille' => $p['masque'], |
'masque.date' => $p['masque'], |
'masque.genre' => $p['masque'], |
'masque.milieu' => $p['masque'], |
'masque.tag_cel' => $p['masque'], |
'masque.tag_pictoflora' => $p['masque'], |
|
// tri est aussi nécessaire car affecte les contraintes de JOIN |
'tri' => $p['tri'], |
'ordre' => $p['ordre']); |
if (array_key_exists('protocole', $p)) { |
$or_params['protocole'] = $p['protocole']; |
} |
|
/* Cependant les champs spécifiques ont priorité sur le masque général. |
Pour cette raison nous supprimons la génération de SQL du masque général sur les |
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */ |
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']); |
if(isset($p['masque.departement'])) unset($or_params['masque.departement']); |
if(isset($p['masque.commune'])) unset($or_params['masque.commune']); |
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']); |
if(isset($p['masque.ns'])) unset($or_params['masque.ns']); |
if(isset($p['masque.famille'])) unset($or_params['masque.famille']); |
if(isset($p['masque.date'])) unset($or_params['masque.date']); |
if(isset($p['masque.genre'])) unset($or_params['masque.genre']); |
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']); |
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']); |
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']); |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités |
de manière identique à la seule différence que: |
1) ils sont combinés par des "OU" logiques plutôt que des "ET". |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique: |
Tous les mots-clefs doivent matcher et sont séparés par des espaces |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql) |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */ |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) { |
if (!empty($p['masque'])) { |
$or_params = array('masque.auteur' => $p['masque'], |
'masque.departement' => $p['masque'], |
'masque.commune' => $p['masque'], // TODO/XXX ? |
'masque.id_zone_geo' => $p['masque'], |
|
$or_masque = array_merge( |
/* tous-deux remplacent masque.tag |
mais sont traité séparément des requestFilterParams() */ |
// 'masque.tag_cel' => $p['masque'], |
// 'masque.tag_pictoflora' => $p['masque'], |
|
'masque.ns' => $p['masque'], |
'masque.famille' => $p['masque'], |
'masque.date' => $p['masque'], |
'masque.genre' => $p['masque'], |
'masque.milieu' => $p['masque'], |
'masque.tag_cel' => $p['masque'], |
'masque.tag_pictoflora' => $p['masque'], |
|
// tri est aussi nécessaire car affecte les contraintes de JOIN |
'tri' => $p['tri'], |
'ordre' => $p['ordre']); |
if (array_key_exists('protocole', $p)) { |
$or_params['protocole'] = $p['protocole']; |
} |
|
/* Cependant les champs spécifiques ont priorité sur le masque général. |
Pour cette raison nous supprimons la génération de SQL du masque général sur les |
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */ |
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']); |
if(isset($p['masque.departement'])) unset($or_params['masque.departement']); |
if(isset($p['masque.commune'])) unset($or_params['masque.commune']); |
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']); |
if(isset($p['masque.ns'])) unset($or_params['masque.ns']); |
if(isset($p['masque.famille'])) unset($or_params['masque.famille']); |
if(isset($p['masque.date'])) unset($or_params['masque.date']); |
if(isset($p['masque.genre'])) unset($or_params['masque.genre']); |
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']); |
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']); |
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']); |
|
$or_masque = array_merge( |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */), |
self::requestFilterParams($or_params) |
); |
|
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont |
postulés comme séparés par des espaces, et doivent être tous matchés. */ |
if(isset($or_params['masque.tag_cel'])) |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' '); |
if(isset($or_params['masque.tag_pictoflora'])) |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' '); |
|
|
// pas de select, groupby & co ici: uniquement 'join' et 'where' |
$or_req = array('join' => array(), 'where' => array()); |
DelTk::sqlAddConstraint($or_masque, $db, $or_req); |
|
self::sqlAddConstraint($or_masque, $db, $or_req); |
|
if($or_req['where']) { |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')'; |
// utile au cas ou des jointures seraient rajoutées |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join'])); |
} |
); |
|
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont |
postulés comme séparés par des espaces, et doivent être tous matchés. */ |
if (isset($or_params['masque.tag_cel'])) { |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' '); |
} |
if (isset($or_params['masque.tag_pictoflora'])) { |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' '); |
} |
|
// pas de select, groupby & co ici: uniquement 'join' et 'where' |
$or_req = array('join' => array(), 'where' => array()); |
DelTk::sqlAddConstraint($or_masque, $db, $or_req); |
|
self::sqlAddConstraint($or_masque, $db, $or_req); |
|
if ($or_req['where']) { |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')'; |
// utile au cas ou des jointures seraient rajoutées |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join'])); |
} |
} |
} |
} |
|
|
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation() |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...) |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') { |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation() |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...) |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') { |
// XXX: cf Observation.php::consulter(), nous pourriouns ici |
// conserver les valeurs vides (pour les phptests notamment, ou non) |
// $obs = array_map('array_filter', $obs); |
$obs_merged = $obs_keyed_by_id_image = array(); |
foreach($obs as $o) { |
// ceci nous complique la tâche pour le reste du processing... |
$id = $o['jsonindex']; |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs) |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages() |
// mais tout deux partage leur référence à "protocole" |
$image = array( |
'id_image' => $o['id_image'], |
foreach ($obs as $o) { |
// ceci nous complique la tâche pour le reste du processing... |
$id = $o['jsonindex']; |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs) |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages() |
// mais tout deux partage leur référence à "protocole" |
$image = array( |
'id_image' => $o['id_image'], |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format), |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus |
); |
|
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']); |
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image; |
$obs_merged[$id]['observation'] = $o; |
$obs_merged[$id]['protocoles_votes'] = array(); |
|
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes']; |
); |
|
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']); |
if (!isset($obs_merged[$id])) { |
$obs_merged[$id] = $image; |
} |
$obs_merged[$id]['observation'] = $o; |
$obs_merged[$id]['protocoles_votes'] = array(); |
|
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] =& $obs_merged[$id]['protocoles_votes']; |
} |
|
|
return array($obs_merged,$obs_keyed_by_id_image); |
} |
} |
|
// complete & override DelTk::requestFilterParams() (même usage) |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) { |
if ($parametres_autorises) { // filtrage de toute clef inconnue |
$params = array_intersect_key($params, array_flip($parametres_autorises)); |
} |
|
|
// complete & override DelTk::requestFilterParams() (même usage) |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) { |
if($parametres_autorises) { // filtrage de toute clef inconnue |
$params = array_intersect_key($params, array_flip($parametres_autorises)); |
} |
|
$p = array(); |
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible); |
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible); |
|
|
// "milieu" inutile pour IdentiPlantes ? |
if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']); |
|
if (isset($params['masque.milieu'])) { |
$p['masque.milieu'] = trim($params['masque.milieu']); |
} |
|
// compatibilité |
if(isset($params['masque.tag'])) { |
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag']; |
if (isset($params['masque.tag'])) { |
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag']; |
} |
|
if($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') { |
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné |
if(!isset($params['protocole']) || !is_numeric($params['protocole'])) { |
|
if ($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') { |
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné |
if(!isset($params['protocole']) || !is_numeric($params['protocole'])) { |
$p['protocole'] = self::$default_proto; |
} else { |
} else { |
$p['protocole'] = intval($params['protocole']); |
} |
} |
} |
|
|
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);')); |
} |
} |
|
|
|
// met à jour *toutes* les stats de nombre de tags et de moyenne des votes |
static function _update_statistics($db) { |
$db->requeter("TRUNCATE TABLE del_image_stat"); |
$db->requeter(<<<EOF |
INSERT INTO `del_image_stat` ( |
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags |
FROM `tb_cel`.`cel_images` ci |
LEFT JOIN |
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes FROM del_image_vote |
GROUP BY ce_image, ce_protocole ) AS divo |
ON ci.id_image = divo.ce_image |
LEFT JOIN |
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag |
GROUP BY ce_image ) AS dit |
ON ci.id_image = dit.ce_image ) |
EOF |
); |
} |
|
static function revOrderBy($orderby) { |
// @TODO plutôt 'desc' ? '' : 'desc', non ? |
return $orderby == 'asc' ? 'desc' : 'asc'; |
} |
} |
?> |