Line 3... |
Line 3... |
3 |
* @author Raphaël Droz <raphael@tela-botanica.org>
|
3 |
* @author Raphaël Droz <raphael@tela-botanica.org>
|
4 |
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
|
4 |
* @copyright Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
|
5 |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
|
5 |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
|
6 |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
|
6 |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
|
7 |
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
|
7 |
* @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Images
|
- |
|
8 |
* @see http://www.tela-botanica.org/wikini/identiplante/wakka.php?wiki=IdentiPlante_PictoFlora_MoteurRecherche
|
8 |
*
|
9 |
*
|
9 |
* Backend pour PictoFlora (del.html#page_recherche_images)
|
10 |
* Backend pour PictoFlora (del.html#page_recherche_images)
|
10 |
*
|
11 |
*
|
11 |
*
|
12 |
*
|
12 |
* == Notes ==
|
13 |
* == Notes ==
|
Line 76... |
Line 77... |
76 |
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
|
77 |
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3
|
77 |
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
|
78 |
// del/services/0.1/images?navigation.depart=0&navigation.limite=12&tri=votes&ordre=desc&protocole=3&masque=plop
|
Line 78... |
Line 79... |
78 |
|
79 |
|
Line 79... |
Line 80... |
79 |
class ListeImages2 {
|
80 |
class ListeImages2 {
|
80 |
|
81 |
|
Line 81... |
Line 82... |
81 |
// TODO: PHP-x.y, ces variables devrait être des "const"
|
82 |
// TODO: PHP-x.y, ces variables devrait être des "const"
|
Line 82... |
Line 83... |
82 |
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
|
83 |
static $format_image_possible = array('O','CRX2S','CRS','CXS','CS','XS','S','M','L','XL','X2L','X3L');
|
83 |
|
84 |
|
Line 84... |
Line 85... |
84 |
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags');
|
85 |
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags');
|
85 |
|
86 |
|
86 |
// en plus de ceux dans DelTk
|
87 |
// en plus de ceux dans DelTk
|
87 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
88 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
88 |
|
89 |
|
89 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
90 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
90 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
91 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
91 |
// spécifiques à PictoFlora:
|
92 |
// spécifiques à PictoFlora:
|
92 |
'format' => 'XL');
|
93 |
'format' => 'XL');
|
93 |
|
94 |
|
94 |
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes))
|
95 |
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes))
|
95 |
|
96 |
|
96 |
static $mappings = array(
|
97 |
static $mappings = array(
|
97 |
'observations' => array( // v_del_image
|
98 |
'observations' => array( // v_del_image
|
98 |
"id_observation" => 1,
|
99 |
"id_observation" => 1,
|
99 |
"date_observation" => 1,
|
100 |
"date_observation" => 1,
|
100 |
"date_transmission" => 1,
|
101 |
"date_transmission" => 1,
|
101 |
"famille" => "determination.famille",
|
102 |
"famille" => "determination.famille",
|
102 |
"nom_sel" => "determination.ns",
|
103 |
"nom_sel" => "determination.ns",
|
103 |
"nom_sel_nn" => "determination.nn",
|
104 |
"nom_sel_nn" => "determination.nn",
|
104 |
"nom_referentiel" => "determination.referentiel",
|
105 |
"nom_referentiel" => "determination.referentiel",
|
105 |
"nt" => "determination.nt",
|
106 |
"nt" => "determination.nt",
|
106 |
"ce_zone_geo" => "id_zone_geo",
|
107 |
"ce_zone_geo" => "id_zone_geo",
|
107 |
"zone_geo" => 1,
|
108 |
"zone_geo" => 1,
|
108 |
"lieudit" => 1,
|
109 |
"lieudit" => 1,
|
109 |
"station" => 1,
|
110 |
"station" => 1,
|
110 |
"milieu" => 1,
|
111 |
"milieu" => 1,
|
111 |
"mots_cles_texte" => "mots_cles_texte",
|
112 |
"mots_cles_texte" => "mots_cles_texte",
|
112 |
"commentaire" => 1,
|
113 |
"commentaire" => 1,
|
113 |
"ce_utilisateur" => "auteur.id",
|
114 |
"ce_utilisateur" => "auteur.id",
|
114 |
"nom_utilisateur" => "auteur.nom",
|
115 |
"nom_utilisateur" => "auteur.nom",
|
115 |
"prenom_utilisateur" => "auteur.prenom",
|
116 |
"prenom_utilisateur" => "auteur.prenom",
|
116 |
),
|
117 |
),
|
117 |
'images' => array( // v_del_image
|
118 |
'images' => array( // v_del_image
|
118 |
'id_image' => 1,
|
119 |
'id_image' => 1,
|
119 |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
|
120 |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
|
120 |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
|
121 |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
|
121 |
'i_mots_cles_texte' => 1
|
122 |
'i_mots_cles_texte' => 1
|
122 |
));
|
123 |
));
|
123 |
|
124 |
|
124 |
|
125 |
|
125 |
public function __construct(Conteneur $conteneur = null) {
|
126 |
public function __construct(Conteneur $conteneur = null) {
|
126 |
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
|
127 |
$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
|
127 |
$this->conteneur->chargerConfiguration('config_images.ini');
|
128 |
$this->conteneur->chargerConfiguration('config_images.ini');
|
128 |
$this->gestionBdd = $conteneur->getGestionBdd();
|
129 |
$this->gestionBdd = $conteneur->getGestionBdd();
|
129 |
$this->bdd = $this->gestionBdd->getBdd();
|
130 |
$this->bdd = $this->gestionBdd->getBdd();
|
130 |
}
|
131 |
}
|
131 |
|
132 |
|
132 |
public function consulter($ressources, $parametres) {
|
133 |
public function consulter($ressources, $parametres) {
|
133 |
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas
|
134 |
/* Certes nous sélectionnons ici (nom|prenom|courriel)_utilisateur de cel_obs, mais il ne nous intéressent pas
|
134 |
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur.
|
135 |
Par contre, ci-dessous nous prenons i_(nom|prenom|courriel)_utilisateur.
|
135 |
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur
|
136 |
Notons cependant qu'aucun moyen ne devrait permettre que i_*_utilisateur != *_utilisateur
|
136 |
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */
|
137 |
Le propriétaire d'une obs et de l'image associée est *toujours* le même. */
|
137 |
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
138 |
array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
138 |
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
139 |
array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
139 |
// pour les votes, les mappings de "Observation" nous suffisent
|
140 |
// pour les votes, les mappings de "Observation" nous suffisent
|
140 |
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
141 |
array_walk(Observation::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
|
141 |
|
142 |
|
142 |
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité
|
143 |
// la nécessité du 'groupby' dépend des 'join's utilisés (LEFT ou INNER) ainsi que de la cardinalité
|
143 |
// de `ce_image` dans ces tables jointes.
|
144 |
// de `ce_image` dans ces tables jointes.
|
144 |
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY
|
145 |
// Contrairement à IdentiPlantes, nous n'avons de HAVING pour PictoFlora, mais par contre un ORDER BY
|
145 |
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array());
|
146 |
$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'orderby' => array());
|
146 |
|
147 |
|
147 |
|
148 |
|
148 |
$db = $this->bdd;
|
149 |
$db = $this->bdd;
|
149 |
|
150 |
|
150 |
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
|
151 |
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
|
151 |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
|
152 |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
|
152 |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
|
153 |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
|
153 |
// même si ceux-ci sont improbables (pas d'<input> pour cela).
|
154 |
// même si ceux-ci sont improbables (pas d'<input> pour cela).
|
154 |
$params_ip = DelTk::requestFilterParams($parametres,
|
155 |
$params_ip = DelTk::requestFilterParams($parametres,
|
155 |
array_diff(DelTk::$parametres_autorises,
|
156 |
array_diff(DelTk::$parametres_autorises,
|
156 |
array('masque.type')),
|
157 |
array('masque.type')),
|
157 |
$this->conteneur);
|
158 |
$this->conteneur);
|
158 |
|
159 |
|
159 |
// notre propre filtrage sur l'INPUT
|
160 |
// notre propre filtrage sur l'INPUT
|
160 |
$params_pf = self::requestFilterParams($parametres,
|
161 |
$params_pf = self::requestFilterParams($parametres,
|
161 |
array_merge(DelTk::$parametres_autorises,
|
162 |
array_merge(DelTk::$parametres_autorises,
|
162 |
self::$parametres_autorises));
|
163 |
self::$parametres_autorises));
|
163 |
|
164 |
|
Line 164... |
Line 165... |
164 |
/* filtrage des tags + sémantique des valeurs multiples:
|
165 |
/* filtrage des tags + sémantique des valeurs multiples:
|
165 |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
|
166 |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
|
166 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
167 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
167 |
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
168 |
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
168 |
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
169 |
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
Line 169... |
Line 170... |
169 |
|
170 |
|
170 |
$params = array_merge(
|
171 |
$params = array_merge(
|
171 |
DelTk::$default_params, // paramètre par défaut Identiplante
|
172 |
DelTk::$default_params, // paramètre par défaut Identiplante
|
172 |
self::$default_params, // paramètres par défaut PictoFlora
|
173 |
self::$default_params, // paramètres par défaut PictoFlora
|
173 |
$params_ip, // les paramètres passés, traités par Identiplante
|
174 |
$params_ip, // les paramètres passés, traités par Identiplante
|
174 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
175 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
175 |
|
176 |
|
176 |
// XXX: temp tweak
|
177 |
// XXX: temp tweak
|
177 |
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('url_images'),
|
178 |
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('url_images'),
|
178 |
"%09d", $params['format']));*/
|
179 |
"%09d", $params['format']));*/
|
179 |
|
180 |
|
180 |
// création des contraintes (génériques de DelTk)
|
181 |
// création des contraintes (génériques de DelTk)
|
181 |
DelTk::sqlAddConstraint($params, $db, $req);
|
182 |
DelTk::sqlAddConstraint($params, $db, $req);
|
182 |
// création des contraintes spécifiques (sur les tags essentiellement)
|
183 |
// création des contraintes spécifiques (sur les tags essentiellement)
|
183 |
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
|
184 |
self::sqlAddConstraint($params, $db, $req, $this->conteneur);
|
184 |
// création des contraintes spécifiques impliquées par le masque général
|
185 |
// création des contraintes spécifiques impliquées par le masque général
|
185 |
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
|
186 |
self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
|
186 |
// l'ORDER BY s'avére complexe
|
187 |
// l'ORDER BY s'avére complexe
|
187 |
self::sqlOrderBy($params, $db, $req);
|
188 |
self::sqlOrderBy($params, $db, $req);
|
188 |
|
189 |
|
189 |
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
|
190 |
// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
|
190 |
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db);
|
191 |
// $idobs_tab = ListeObservations::getIdObs($params, $req, $db);
|
191 |
$idobs_tab = self::getIdImages($params, $req, $db);
|
192 |
$idobs_tab = self::getIdImages($params, $req, $db);
|
192 |
|
193 |
|
193 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
194 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
194 |
if(!$idobs_tab) {
|
195 |
if(!$idobs_tab) {
|
195 |
$resultat = new ResultatService();
|
196 |
$resultat = new ResultatService();
|
196 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
|
197 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
|
Line 197... |
Line 198... |
197 |
'resultats' => array());
|
198 |
'resultats' => array());
|
198 |
return $resultat;
|
199 |
return $resultat;
|
199 |
/*
|
200 |
/*
|
Line 200... |
Line 201... |
200 |
header('HTTP/1.0 404 Not Found');
|
201 |
header('HTTP/1.0 404 Not Found');
|
Line 201... |
Line 202... |
201 |
// don't die (phpunit)
|
202 |
// don't die (phpunit)
|
202 |
throw(new Exception()); */
|
203 |
throw(new Exception()); */
|
203 |
}
|
204 |
}
|
204 |
|
205 |
|
205 |
|
206 |
|
206 |
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
|
207 |
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
|
207 |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
|
208 |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
|
208 |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
|
209 |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
|
209 |
|
210 |
|
210 |
$liaisons = self::chargerImages($db, $idobs);
|
211 |
$liaisons = self::chargerImages($db, $idobs);
|
211 |
|
212 |
|
212 |
/*
|
213 |
/*
|
213 |
// Q&D
|
214 |
// Q&D
|
214 |
$images = array();
|
215 |
$images = array();
|
215 |
$o = new Observation($this->conteneur);
|
216 |
$o = new Observation($this->conteneur);
|
216 |
foreach($idobs as $i) {
|
217 |
foreach($idobs as $i) {
|
217 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
218 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
218 |
}
|
219 |
}
|
219 |
*/
|
220 |
*/
|
220 |
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
|
221 |
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
|
221 |
$liaisons,
|
222 |
$liaisons,
|
222 |
$this->conteneur->getParametre('url_images'),
|
223 |
$this->conteneur->getParametre('url_images'),
|
223 |
$params['format']);
|
224 |
$params['format']);
|
224 |
|
225 |
|
225 |
|
226 |
|
226 |
// on charge les votes pour ces images et pour *tous* les protocoles
|
227 |
// on charge les votes pour ces images et pour *tous* les protocoles
|
227 |
$votes = Observation::chargerVotesImage($db, $liaisons, NULL);
|
228 |
$votes = Observation::chargerVotesImage($db, $liaisons, NULL);
|
228 |
|
229 |
|
229 |
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour
|
230 |
// subtilité, nous passons ici le tableau d'images indexé par id_image qui est bien plus pratique pour
|
230 |
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation.
|
231 |
// associer les vote à un tableau, puisque nous ne connaissons pas les id d'observation.
|
231 |
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation)
|
232 |
// Mais magiquement (par référence), cela va remplir notre tableau indexé par couple d'id (id_image, id_observation)
|
232 |
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque
|
233 |
// cf reformateImagesDoubleIndex() à qui revient la tâche de créer ces deux versions simultanément lorsque
|
233 |
// c'est encore possible.
|
234 |
// c'est encore possible.
|
234 |
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
|
235 |
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
|
235 |
|
236 |
|
236 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
237 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
237 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
238 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
238 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
239 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
239 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
240 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
240 |
$resultat = new ResultatService();
|
241 |
$resultat = new ResultatService();
|
241 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
242 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
242 |
'resultats' => $images);
|
243 |
'resultats' => $images);
|
243 |
return $resultat;
|
244 |
return $resultat;
|
244 |
}
|
245 |
}
|
245 |
|
246 |
|
246 |
/**
|
247 |
/**
|
247 |
* TODO: partie spécifique liées à la complexité de PictoFlora:
|
248 |
* TODO: partie spécifique liées à la complexité de PictoFlora:
|
248 |
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req)
|
249 |
* génération de la clause ORDER BY (génère la valeur de la clef orderby' de $req)
|
Line 249... |
Line 250... |
249 |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
|
250 |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
|
250 |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
|
251 |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
|
251 |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
|
252 |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
|
252 |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
|
253 |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
|
253 |
*/
|
254 |
*/
|
254 |
static function sqlOrderBy($p, $db, &$req) {
|
255 |
static function sqlOrderBy($p, $db, &$req) {
|
255 |
// parmi self::$tri_possible
|
256 |
// parmi self::$tri_possible
|
256 |
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
|
257 |
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
|
257 |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
258 |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
258 |
return;
|
259 |
return;
|
259 |
}
|
260 |
}
|
260 |
|
261 |
|
261 |
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
|
262 |
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
|
262 |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
|
263 |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
|
263 |
return;
|
264 |
return;
|
264 |
}
|
265 |
}
|
265 |
|
266 |
|
266 |
if($p['tri'] == 'date_observation') {
|
267 |
if($p['tri'] == 'date_observation') {
|
267 |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
268 |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
268 |
return;
|
269 |
return;
|
269 |
}
|
270 |
}
|
270 |
|
271 |
|
271 |
// tri == 'date_transmission'
|
272 |
// tri == 'date_transmission'
|
272 |
// avant cel:r1860, date_transmission pouvait être NULL
|
273 |
// avant cel:r1860, date_transmission pouvait être NULL
|
273 |
// or nous voulons de la consistence (notamment pour phpunit)
|
274 |
// or nous voulons de la consistence (notamment pour phpunit)
|
274 |
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
275 |
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
275 |
}
|
276 |
}
|
276 |
|
277 |
|
277 |
/*
|
278 |
/*
|
278 |
* in $p: un tableau de paramètres, dont:
|
279 |
* in $p: un tableau de paramètres, dont:
|
279 |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
|
280 |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
|
280 |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
|
281 |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
|
281 |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
|
282 |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
|
282 |
*
|
283 |
*
|
283 |
* in/ou: $req: un tableau de structure de requête MySQL
|
284 |
* in/ou: $req: un tableau de structure de requête MySQL
|
284 |
*
|
285 |
*
|
285 |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
|
286 |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
|
286 |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
|
287 |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
|
287 |
* Soit directement $this->consulter() si des masque.tag* sont passés
|
288 |
* Soit directement $this->consulter() si des masque.tag* sont passés
|
288 |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
|
289 |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
|
289 |
* Soit via sqlAddMasqueConstraint():
|
290 |
* Soit via sqlAddMasqueConstraint():
|
290 |
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
|
291 |
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
|
291 |
* équivalent à:
|
292 |
* équivalent à:
|
292 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
293 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
293 |
*
|
294 |
*
|
294 |
*/
|
295 |
*/
|
295 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
296 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
296 |
// TODO implement dans DelTk ?
|
297 |
// TODO implement dans DelTk ?
|
297 |
if(!empty($p['masque.milieu'])) {
|
298 |
if(!empty($p['masque.milieu'])) {
|
298 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
299 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
299 |
}
|
300 |
}
|
300 |
|
301 |
|
301 |
|
302 |
|
302 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
303 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
303 |
celui-ci indique sur quels votes porte l'AVG.
|
304 |
celui-ci indique sur quels votes porte l'AVG.
|
304 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
305 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
305 |
/* TODO: perf problème:
|
306 |
/* TODO: perf problème:
|
306 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
307 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
307 |
- dissociant le comptage de la récup d'id + javascript async
|
308 |
- dissociant le comptage de la récup d'id + javascript async
|
308 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
309 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
309 |
(paramètre booléen "with-total" par exemple)
|
310 |
(paramètre booléen "with-total" par exemple)
|
310 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
311 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
311 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
312 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
312 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
313 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
313 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
314 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
314 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
315 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
315 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
316 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
316 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
317 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
317 |
if($p['tri'] == 'votes') {
|
318 |
if($p['tri'] == 'votes') {
|
318 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
319 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
319 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
320 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
320 |
if($p['ordre'] == 'desc') {
|
321 |
if($p['ordre'] == 'desc') {
|
321 |
// pas de group by nécessaire pour cette jointure
|
322 |
// pas de group by nécessaire pour cette jointure
|
322 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
323 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
323 |
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
|
324 |
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
|
324 |
' ON vdi.id_image = dis.ce_image'.
|
325 |
' ON vdi.id_image = dis.ce_image'.
|
325 |
' AND dis.ce_protocole = %d',
|
326 |
' AND dis.ce_protocole = %d',
|
326 |
$p['protocole']);
|
327 |
$p['protocole']);
|
327 |
} else {
|
328 |
} else {
|
328 |
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
|
329 |
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
|
329 |
' ON vdi.id_image = dis.ce_image'.
|
330 |
' ON vdi.id_image = dis.ce_image'.
|
330 |
' AND dis.ce_protocole = %d',
|
331 |
' AND dis.ce_protocole = %d',
|
331 |
$p['protocole']);
|
332 |
$p['protocole']);
|
332 |
// nécessaire (dup ce_image dans del_image_stat)
|
333 |
// nécessaire (dup ce_image dans del_image_stat)
|
333 |
$req['groupby'][] = 'vdi.id_observation';
|
334 |
$req['groupby'][] = 'vdi.id_observation';
|
334 |
}
|
335 |
}
|
335 |
}
|
336 |
}
|
336 |
|
337 |
|
337 |
if($p['tri'] == 'tags') {
|
338 |
if($p['tri'] == 'tags') {
|
338 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
339 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
339 |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
|
340 |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
|
340 |
// nécessaire (dup ce_image dans del_image_stat)
|
341 |
// nécessaire (dup ce_image dans del_image_stat)
|
341 |
$req['groupby'][] = 'vdi.id_observation';
|
342 |
$req['groupby'][] = 'vdi.id_observation';
|
342 |
}
|
343 |
}
|
343 |
|
344 |
|
344 |
// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
|
345 |
// car il ne sont pas traités par la générique requestFilterParams() les clefs "masque.tag_*"
|
345 |
// sont toujours présentes; bien que parfois NULL.
|
346 |
// sont toujours présentes; bien que parfois NULL.
|
346 |
if($p['masque.tag_cel']) {
|
347 |
if($p['masque.tag_cel']) {
|
347 |
if(isset($p['masque.tag_cel']['AND'])) {
|
348 |
if(isset($p['masque.tag_cel']['AND'])) {
|
348 |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
|
349 |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
|
349 |
// et auquel cas laisser au client le choix du couteux "%" ?
|
350 |
// et auquel cas laisser au client le choix du couteux "%" ?
|
350 |
$tags = $p['masque.tag_cel']['AND'];
|
351 |
$tags = $p['masque.tag_cel']['AND'];
|
- |
|
352 |
array_walk($tags, create_function('&$val, $k, $db',
|
- |
|
353 |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
|
- |
|
354 |
$db->proteger("%".$val."%"));'),
|
351 |
array_walk($tags, create_function('&$val, $k, $db',
|
355 |
$db);
|
- |
|
356 |
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
|
352 |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
|
357 |
}
|
- |
|
358 |
else {
|
353 |
$db->proteger("%".$val."%"));'),
|
359 |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
|
- |
|
360 |
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
|
- |
|
361 |
}
|
- |
|
362 |
}
|
- |
|
363 |
|
- |
|
364 |
if($p['masque.tag_pictoflora']) {
|
354 |
$db);
|
365 |
// inutilisable pour l'instant
|
- |
|
366 |
// self::sqlAddPictoFloraTagConstraint1($p, $db, $req);
|
- |
|
367 |
|
- |
|
368 |
// intéressante, mais problème d'optimiseur MySQL 5.5 (dependant subquery)
|
355 |
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
|
369 |
// self::sqlAddPictoFloraTagConstraint2($p, $db, $req);
|
356 |
}
|
370 |
|
- |
|
371 |
// approche fiable mais sous-optimale
|
- |
|
372 |
self::sqlAddPictoFloraTagConstraint3($p, $db, $req);
|
- |
|
373 |
}
|
- |
|
374 |
}
|
- |
|
375 |
|
- |
|
376 |
/* approche intéressante si les deux problèmes suivants peuvent être résolu:
|
- |
|
377 |
- LEFT JOIN => dup => *gestion de multiples GROUP BY* (car in-fine un LIMIT est utilisé)
|
- |
|
378 |
- dans le cas d'un ET logique, comment chercher les observations correspondantes ? */
|
- |
|
379 |
static function sqlAddPictoFloraTagConstraint1($p, $db, &$req) {
|
- |
|
380 |
// XXX: utiliser tag plutôt que tag_normalise ?
|
357 |
else {
|
381 |
$req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
|
- |
|
382 |
$req['where'][] = 'dit.actif = 1';
|
358 |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
|
383 |
$req['groupby'][] = 'vdi.id_image'; // TODO: nécessaire (car dup') mais risque de conflict en cas de tri (multiple GROUP BY)
|
- |
|
384 |
// XXX: en cas de ET, possibilité du GROUP_CONCAT(), mais probablement sans grand intérêt, d'où une boucle
|
- |
|
385 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
359 |
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
|
386 |
// TODO/XXX : comment matcher les observations ayant tous les mots-clef passés ?
|
360 |
}
|
387 |
// ... le LEFT-JOIN n'y semble pas adapté
|
361 |
}
|
388 |
}
|
362 |
|
389 |
else {
|
363 |
|
390 |
$protected_tags = array();
|
364 |
// XXX: utiliser tag plutôt que tag_normalise ?
|
391 |
foreach($p['masque.tag_pictoflora']['OR'] as $tag) $protected_tags[] = $db->proteger(strtolower($tag));
|
365 |
if($p['masque.tag_pictoflora']) {
|
392 |
$req['where'][] = sprintf('tag_normalise IN (%s)', implode(',', $protected_tags));
|
366 |
// pas de LEFT JOIN ? ou bien peut-être en cas de tri, mais nous parlons bien ici d'un masque
|
- |
|
367 |
/* $req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
|
- |
|
368 |
$req['where'][] = 'dit.actif = 1'; */
|
- |
|
369 |
|
- |
|
370 |
|
393 |
}
|
371 |
// Note à propos des 4 "@ instruction" ci-dessous (notamment sur recupererTous())
|
394 |
}
|
372 |
// REGEXP permet un puissant mécanisme de sélection des obs/image à qui sait
|
395 |
|
373 |
// l'utiliser, mais peut sortir une erreur en cas de REGEXP invalide
|
396 |
// inutilisé pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro)
|
374 |
// ex: REGEX "^(".
|
397 |
static function sqlAddPictoFloraTagConstraint2($p, $db, &$req) {
|
375 |
// Pour l'heure nous ignorons ce type d'erreur car aucun de nos champ de recherche
|
398 |
// Note à propos des 4 "@ instruction" ci-dessous (notamment sur recupererTous())
|
376 |
// ne peuvent (ou ne devrait) comporter des meta-caractères
|
399 |
// REGEXP permet un puissant mécanisme de sélection des obs/image à qui sait
|
377 |
// ([])?*+\\
|
400 |
// l'utiliser, mais peut sortir une erreur en cas de REGEXP invalide
|
378 |
|
401 |
// ex: REGEX "^(".
|
379 |
|
402 |
// Pour l'heure nous ignorons ce type d'erreur car aucun de nos champ de recherche
|
380 |
// ==== commenté pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro) ====
|
403 |
// ne peuvent (ou ne devrait) comporter des meta-caractères
|
381 |
/*
|
404 |
// ([])?*+\\
|
382 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
405 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
383 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
406 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
384 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
407 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
385 |
sort($p['masque.tag_pictoflora']['AND']);
|
408 |
sort($p['masque.tag_pictoflora']['AND']);
|
386 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
409 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
387 |
" GROUP BY ce_image".
|
410 |
" GROUP BY ce_image".
|
- |
|
411 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
|
388 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
|
412 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
|
389 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
|
413 |
}
|
390 |
}
|
414 |
else {
|
391 |
else {
|
415 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
392 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
416 |
" GROUP BY ce_image".
|
393 |
" GROUP BY ce_image".
|
417 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
|
394 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
|
418 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
|
395 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
|
419 |
}
|
396 |
}
|
420 |
}
|
397 |
*/
|
421 |
|
398 |
|
422 |
// si l'on est bassiné par les "DEPENDENT SUBQUERY", nous la faisons donc indépendemment via cette fonction
|
399 |
// ==== XXX: puisque on est bassiné par cette "DEPENDENT SUBQUERY", nous la faisons donc indépendemment ====
|
423 |
static function sqlAddPictoFloraTagConstraint3($p, $db, &$req) {
|
400 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
424 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
401 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
425 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
402 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
426 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
403 |
sort($p['masque.tag_pictoflora']['AND']);
|
427 |
sort($p['masque.tag_pictoflora']['AND']);
|
404 |
|
428 |
|
405 |
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ...
|
429 |
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ...
|
406 |
$ids = @$db->recupererTous(sprintf(
|
430 |
$ids = @$db->recupererTous(sprintf(
|
407 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
431 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
408 |
" GROUP BY ce_image".
|
432 |
" GROUP BY ce_image".
|
409 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s",
|
433 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s",
|
410 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND']))));
|
434 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND']))));
|
411 |
|
435 |
|
412 |
// puis:
|
436 |
// puis:
|
413 |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
437 |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
414 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
- |
|
415 |
|
- |
|
416 |
}
|
438 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
417 |
else {
|
439 |
|
418 |
$ids = @$db->recupererTous(sprintf(
|
- |
|
419 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
440 |
}
|
420 |
" GROUP BY ce_image".
|
441 |
else {
|
421 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s",
|
442 |
$ids = @$db->recupererTous(sprintf(
|
422 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR']))));
|
443 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
423 |
|
444 |
" GROUP BY ce_image".
|
424 |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
445 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s",
|
425 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
446 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR']))));
|
426 |
}
|
447 |
|
427 |
|
448 |
$ids = @array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
428 |
}
|
449 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
Line 429... |
Line 450... |
429 |
}
|
450 |
}
|
430 |
|
451 |
}
|
Line 431... |
Line 452... |
431 |
|
452 |
|
Line 432... |
Line 453... |
432 |
static function getIdImages($p, $req, $db) {
|
453 |
static function getIdImages($p, $req, $db) {
|
Line 433... |
Line 454... |
433 |
return $db->recupererTous(sprintf(
|
454 |
return $db->recupererTous(sprintf(
|
Line 434... |
Line 455... |
434 |
'SELECT SQL_CALC_FOUND_ROWS id_image' .
|
455 |
'SELECT SQL_CALC_FOUND_ROWS id_image' .
|
Line 435... |
Line 456... |
435 |
' FROM v_del_image vdi'.
|
456 |
' FROM v_del_image vdi'.
|
436 |
' %s' . // LEFT JOIN if any
|
457 |
' %s' . // LEFT JOIN if any
|
437 |
' WHERE %s'. // where-clause ou TRUE
|
458 |
' WHERE %s'. // where-clause ou TRUE
|
Line 438... |
Line 459... |
438 |
' %s'. // group-by
|
459 |
' %s'. // group-by
|
439 |
' ORDER BY %s'.
|
460 |
' ORDER BY %s'.
|
440 |
' LIMIT %d, %d -- %s',
|
461 |
' LIMIT %d, %d -- %s',
|
441 |
|
462 |
|
442 |
$req['join'] ? implode(' ', array_unique($req['join'])) : '',
|
463 |
$req['join'] ? implode(' ', array_unique($req['join'])) : '',
|
443 |
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
|
464 |
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
|
444 |
|
465 |
|
445 |
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
|
466 |
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
|
446 |
|
467 |
|
447 |
$req['orderby'],
|
468 |
$req['orderby'],
|
448 |
|
469 |
|
449 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
470 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
450 |
|
471 |
|
451 |
}
|
472 |
}
|
452 |
|
473 |
|
453 |
static function chargerImages($db, $idImg) {
|
474 |
static function chargerImages($db, $idImg) {
|
454 |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
475 |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
455 |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
476 |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
456 |
|
477 |
|
457 |
return $db->recupererTous(sprintf('SELECT '.
|
478 |
return $db->recupererTous(sprintf('SELECT '.
|
458 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
479 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
459 |
' %1$s, %2$s FROM v_del_image '.
|
480 |
' %1$s, %2$s FROM v_del_image '.
|
460 |
' WHERE %3$s'.
|
481 |
' WHERE %3$s'.
|
461 |
' -- %4$s',
|
482 |
' -- %4$s',
|
462 |
$obs_fields, $image_fields,
|
483 |
$obs_fields, $image_fields,
|
463 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
484 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
464 |
__FILE__ . ':' . __LINE__));
|
485 |
__FILE__ . ':' . __LINE__));
|
465 |
|
486 |
|
466 |
}
|
487 |
}
|
467 |
|
488 |
|
468 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
489 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
469 |
de manière identique à la seule différence que:
|
490 |
de manière identique à la seule différence que:
|
470 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
491 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
471 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
492 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
472 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
493 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
473 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
494 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
474 |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
|
495 |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
|
475 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
496 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
476 |
if(!empty($p['masque'])) {
|
497 |
if(!empty($p['masque'])) {
|
477 |
$or_params = array('masque.auteur' => $p['masque'],
|
498 |
$or_params = array('masque.auteur' => $p['masque'],
|
478 |
'masque.departement' => $p['masque'],
|
499 |
'masque.departement' => $p['masque'],
|
479 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
500 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
480 |
'masque.id_zone_geo' => $p['masque'],
|
501 |
'masque.id_zone_geo' => $p['masque'],
|
481 |
|
502 |
|
482 |
/* tous-deux remplacent masque.tag
|
503 |
/* tous-deux remplacent masque.tag
|
483 |
mais sont traité séparément des requestFilterParams() */
|
504 |
mais sont traité séparément des requestFilterParams() */
|
484 |
// 'masque.tag_cel' => $p['masque'],
|
505 |
// 'masque.tag_cel' => $p['masque'],
|
485 |
// 'masque.tag_pictoflora' => $p['masque'],
|
506 |
// 'masque.tag_pictoflora' => $p['masque'],
|
486 |
|
507 |
|
487 |
'masque.ns' => $p['masque'],
|
508 |
'masque.ns' => $p['masque'],
|
488 |
'masque.famille' => $p['masque'],
|
509 |
'masque.famille' => $p['masque'],
|
489 |
'masque.date' => $p['masque'],
|
510 |
'masque.date' => $p['masque'],
|
490 |
'masque.genre' => $p['masque'],
|
511 |
'masque.genre' => $p['masque'],
|
491 |
'masque.milieu' => $p['masque'],
|
512 |
'masque.milieu' => $p['masque'],
|
492 |
|
513 |
|
493 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
514 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
494 |
'tri' => $p['tri'],
|
515 |
'tri' => $p['tri'],
|
495 |
'ordre' => $p['ordre']);
|
516 |
'ordre' => $p['ordre']);
|
496 |
|
517 |
|
497 |
$or_masque = array_merge(
|
518 |
$or_masque = array_merge(
|
498 |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
- |
|
499 |
self::requestFilterParams($or_params));
|
519 |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
- |
|
520 |
self::requestFilterParams($or_params));
|
Line 500... |
Line 521... |
500 |
|
521 |
|
501 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
522 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
502 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
523 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
503 |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
524 |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
504 |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
525 |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
505 |
|
526 |
|
506 |
|
527 |
|
507 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
528 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
508 |
$or_req = array('join' => array(), 'where' => array());
|
529 |
$or_req = array('join' => array(), 'where' => array());
|
509 |
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
|
530 |
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
|
510 |
self::sqlAddConstraint($or_masque, $db, $or_req);
|
531 |
self::sqlAddConstraint($or_masque, $db, $or_req);
|
511 |
|
532 |
|
512 |
if($or_req['where']) {
|
533 |
if($or_req['where']) {
|
513 |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
|
534 |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
|
514 |
// utile au cas ou des jointures seraient rajoutées
|
535 |
// utile au cas ou des jointures seraient rajoutées
|
515 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
536 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
516 |
}
|
537 |
}
|
517 |
}
|
538 |
}
|
518 |
}
|
539 |
}
|
519 |
|
540 |
|
520 |
|
541 |
|
521 |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
|
542 |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
|
Line 522... |
Line 543... |
522 |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
|
543 |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
|
523 |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
|
- |
|
524 |
// XXX: cf Observation.php::consulter(), nous pourriouns ici
|
- |
|
525 |
// conserver les valeurs vides (pour les phptests notamment, ou non)
|
- |
|
526 |
// $obs = array_map('array_filter', $obs);
|
544 |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
|
Line -... |
Line 545... |
- |
|
545 |
// XXX: cf Observation.php::consulter(), nous pourriouns ici
|
- |
|
546 |
// conserver les valeurs vides (pour les phptests notamment, ou non)
|
Line 527... |
Line -... |
527 |
$obs_merged = $obs_keyed_by_id_image = array();
|
- |
|
528 |
foreach($obs as $o) {
|
- |
|
529 |
// ceci nous complique la tâche pour le reste du processing...
|
- |
|
530 |
$id = $o['jsonindex'];
|
- |
|
531 |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
|
- |
|
Line 532... |
Line 547... |
532 |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
|
547 |
// $obs = array_map('array_filter', $obs);
|
533 |
// mais tout deux partage leur référence à "protocole"
|
548 |
$obs_merged = $obs_keyed_by_id_image = array();
|
- |
|
549 |
foreach($obs as $o) {
|
534 |
$image = array(
|
550 |
// ceci nous complique la tâche pour le reste du processing...
|
- |
|
551 |
$id = $o['jsonindex'];
|
Line -... |
Line 552... |
- |
|
552 |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
|
535 |
'id_image' => $o['id_image'],
|
553 |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
|
536 |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
|
554 |
// mais tout deux partage leur référence à "protocole"
|
Line 537... |
Line -... |
537 |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
|
- |
|
538 |
);
|
555 |
$image = array(
|
539 |
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
|
556 |
'id_image' => $o['id_image'],
|
540 |
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
|
- |
|
Line 541... |
Line 557... |
541 |
$obs_merged[$id]['observation'] = $o;
|
557 |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
|
542 |
$obs_merged[$id]['protocoles_votes'] = array();
|
- |
|
543 |
|
558 |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
|
544 |
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
|
- |
|
545 |
}
|
- |
|
546 |
|
559 |
);
|
547 |
return array($obs_merged,$obs_keyed_by_id_image);
|
560 |
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
|
Line -... |
Line 561... |
- |
|
561 |
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
|
- |
|
562 |
$obs_merged[$id]['observation'] = $o;
|
548 |
}
|
563 |
$obs_merged[$id]['protocoles_votes'] = array();
|
- |
|
564 |
|
- |
|
565 |
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
|
- |
|
566 |
}
|
549 |
|
567 |
|
Line -... |
Line 568... |
- |
|
568 |
return array($obs_merged,$obs_keyed_by_id_image);
|
- |
|
569 |
}
|
Line -... |
Line 570... |
- |
|
570 |
|
550 |
|
571 |
|
551 |
|
572 |
|
552 |
// complete & override DelTk::requestFilterParams() (même usage)
|
573 |
// complete & override DelTk::requestFilterParams() (même usage)
|
553 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
574 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
554 |
if($parametres_autorises) { // filtrage de toute clef inconnue
|
575 |
if($parametres_autorises) { // filtrage de toute clef inconnue
|
555 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
576 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
556 |
}
|
577 |
}
|
557 |
|
578 |
|
558 |
$p = array();
|
579 |
$p = array();
|
Line 594... |
Line 615... |
594 |
LEFT JOIN
|
615 |
LEFT JOIN
|
595 |
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag
|
616 |
( SELECT ce_image, COUNT(id_tag) as ctags FROM del_image_tag
|
596 |
GROUP BY ce_image ) AS dit
|
617 |
GROUP BY ce_image ) AS dit
|
597 |
ON ci.id_image = dit.ce_image )
|
618 |
ON ci.id_image = dit.ce_image )
|
598 |
EOF
|
619 |
EOF
|
599 |
);
|
620 |
);
|
600 |
}
|
621 |
}
|
Line 601... |
Line 622... |
601 |
|
622 |
|
602 |
static function revOrderBy($orderby) {
|
623 |
static function revOrderBy($orderby) {
|
603 |
return $orderby == 'asc' ? 'desc' : 'asc';
|
624 |
return $orderby == 'asc' ? 'desc' : 'asc';
|
604 |
}
|
625 |
}
|