Line 37... |
Line 37... |
37 |
* Et le test effectué doit matcher sur:
|
37 |
* Et le test effectué doit matcher sur:
|
38 |
* ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
|
38 |
* ^(expression)% *OU* %(expression)% [cf getConditionsImages()]
|
39 |
*
|
39 |
*
|
40 |
* Par défaut les tags sont comma-separated (OU logique).
|
40 |
* Par défaut les tags sont comma-separated (OU logique).
|
41 |
* Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
|
41 |
* Cependant pour conserver le comportement du masque général qui sous-entend un ET logique sur
|
42 |
* des tags séparés par des espaces recherche
|
42 |
* des tags séparés par des espaces recherche
|
43 |
*
|
43 |
*
|
44 |
* TODO:
|
44 |
* TODO:
|
45 |
* -affiner la gestion de passage de mots-clefs dans le masque général.
|
45 |
* -affiner la gestion de passage de mots-clefs dans le masque général.
|
46 |
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
|
46 |
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
|
47 |
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
|
47 |
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
|
Line 77... |
Line 77... |
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
|
78 |
// 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 79... |
Line 79... |
79 |
|
79 |
|
Line 80... |
Line 80... |
80 |
class ListeImages {
|
80 |
class ListeImages {
|
81 |
|
81 |
|
Line 82... |
Line 82... |
82 |
// TODO: PHP-x.y, ces variables devrait être des "const"
|
82 |
// TODO: PHP-x.y, ces variables devrait être des "const"
|
Line 83... |
Line 83... |
83 |
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');
|
84 |
|
84 |
|
85 |
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points');
|
- |
|
86 |
|
- |
|
87 |
// en plus de ceux dans DelTk
|
- |
|
88 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
- |
|
89 |
|
- |
|
90 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
- |
|
91 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
- |
|
92 |
// spécifiques à PictoFlora:
|
- |
|
93 |
'format' => 'XL');
|
- |
|
94 |
|
- |
|
95 |
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points))
|
- |
|
96 |
|
- |
|
97 |
static $mappings = array(
|
- |
|
98 |
'observations' => array( // v_del_image
|
- |
|
99 |
"id_observation" => 1,
|
- |
|
100 |
"date_observation" => 1,
|
- |
|
101 |
"date_transmission" => 1,
|
- |
|
102 |
"famille" => "determination.famille",
|
- |
|
103 |
"nom_sel" => "determination.ns",
|
- |
|
104 |
"nom_sel_nn" => "determination.nn",
|
- |
|
105 |
"nom_referentiel" => "determination.referentiel",
|
- |
|
106 |
"nt" => "determination.nt",
|
- |
|
107 |
"ce_zone_geo" => "id_zone_geo",
|
- |
|
108 |
"zone_geo" => 1,
|
- |
|
109 |
"lieudit" => 1,
|
- |
|
110 |
"station" => 1,
|
- |
|
111 |
"milieu" => 1,
|
- |
|
112 |
"mots_cles_texte" => "mots_cles_texte",
|
- |
|
113 |
"commentaire" => 1,
|
- |
|
114 |
"ce_utilisateur" => "auteur.id",
|
- |
|
115 |
"nom_utilisateur" => "auteur.nom",
|
- |
|
116 |
"prenom_utilisateur" => "auteur.prenom",
|
- |
|
117 |
),
|
- |
|
118 |
'images' => array( // v_del_image
|
- |
|
119 |
'id_image' => 1,
|
- |
|
Line -... |
Line 85... |
- |
|
85 |
static $tri_possible = array('date_transmission', 'date_observation', 'votes', 'tags', 'points');
|
- |
|
86 |
|
- |
|
87 |
// en plus de ceux dans DelTk
|
- |
|
88 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
- |
|
89 |
|
- |
|
90 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
- |
|
91 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
- |
|
92 |
// spécifiques à PictoFlora:
|
- |
|
93 |
'format' => 'XL');
|
- |
|
94 |
|
- |
|
95 |
static $default_proto = 3; // proto par défaut: capitalisation d'img (utilisé uniquement pour tri=(tags|votes|points))
|
- |
|
96 |
|
- |
|
97 |
static $mappings = array(
|
- |
|
98 |
'observations' => array( // v_del_image
|
- |
|
99 |
"id_observation" => 1,
|
- |
|
100 |
"date_observation" => 1,
|
- |
|
101 |
"date_transmission" => 1,
|
- |
|
102 |
"famille" => "determination.famille",
|
- |
|
103 |
"nom_sel" => "determination.ns",
|
- |
|
104 |
"nom_sel_nn" => "determination.nn",
|
- |
|
105 |
"nom_referentiel" => "determination.referentiel",
|
- |
|
106 |
"nt" => "determination.nt",
|
- |
|
107 |
"ce_zone_geo" => "id_zone_geo",
|
- |
|
108 |
"zone_geo" => 1,
|
- |
|
109 |
"lieudit" => 1,
|
- |
|
110 |
"station" => 1,
|
- |
|
111 |
"milieu" => 1,
|
- |
|
112 |
"mots_cles_texte" => "mots_cles_texte",
|
- |
|
113 |
"commentaire" => 1,
|
- |
|
114 |
"ce_utilisateur" => "auteur.id",
|
- |
|
115 |
"nom_utilisateur" => "auteur.nom",
|
- |
|
116 |
"prenom_utilisateur" => "auteur.prenom",),
|
- |
|
117 |
'images' => array( // v_del_image
|
Line 120... |
Line 118... |
120 |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
|
118 |
'id_image' => 1,
|
121 |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
|
119 |
// l'alias suivant est particulier: in-fine il doit s'appeler mots_cles_texte
|
122 |
'i_mots_cles_texte' => 1
|
120 |
// mais nous afin d'éviter un conflit d'alias nous le renommons plus tard (reformateImagesDoubleIndex)
|
263 |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
|
260 |
* nécessaire ? tableau sprintf(key (tri) => value (ordre), key => value ...).
|
264 |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
|
261 |
* Cependant il est impensable de joindre sur un AVG() des valeurs des votes pour
|
265 |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
|
262 |
* *chaque* couple (id_image, protocole) de la base afin de trouver les images
|
266 |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
|
263 |
* les "mieux notées", ou bien les images ayant le "plus de tags" (COUNT())
|
267 |
*/
|
264 |
*/
|
268 |
static function sqlOrderBy($p, $db, &$req) {
|
265 |
static function sqlOrderBy($p, $db, &$req) {
|
269 |
// parmi self::$tri_possible
|
266 |
// parmi self::$tri_possible
|
270 |
if($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
|
267 |
if ($p['tri'] == 'votes') { // LEFT JOIN sur "dis" ci-dessous
|
271 |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
268 |
$req['orderby'] = 'dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
272 |
return;
|
269 |
return;
|
273 |
}
|
270 |
}
|
274 |
|
271 |
|
275 |
if($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous
|
272 |
if ($p['tri'] == 'points') { // LEFT JOIN sur "dis" ci-dessous
|
276 |
$req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
273 |
$req['orderby'] = 'dis.nb_points ' . $p['ordre'] . ', dis.moyenne ' . $p['ordre'] . ', dis.nb_votes ' . $p['ordre'];
|
277 |
return;
|
274 |
return;
|
278 |
}
|
275 |
}
|
279 |
|
276 |
|
280 |
if($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
|
277 |
if ($p['tri'] == 'tags') { // LEFT JOIN sur "dis" ci-dessous
|
281 |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
|
278 |
$req['orderby'] = 'dis.nb_tags ' . $p['ordre'];
|
282 |
return;
|
279 |
return;
|
283 |
}
|
280 |
}
|
284 |
|
281 |
|
285 |
if($p['tri'] == 'date_observation') {
|
282 |
if ($p['tri'] == 'date_observation') {
|
286 |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
283 |
$req['orderby'] = 'date_observation ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
287 |
return;
|
284 |
return;
|
288 |
}
|
285 |
}
|
Line 289... |
Line 286... |
289 |
|
286 |
|
290 |
// tri == 'date_transmission'
|
287 |
// tri == 'date_transmission'
|
291 |
// avant cel:r1860, date_transmission pouvait être NULL
|
288 |
// avant cel:r1860, date_transmission pouvait être NULL
|
292 |
// or nous voulons de la cohérence (notamment pour phpunit)
|
289 |
// or nous voulons de la cohérence (notamment pour phpunit)
|
293 |
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
290 |
$req['orderby'] = 'date_transmission ' . $p['ordre'] . ', id_observation ' . $p['ordre'];
|
294 |
}
|
291 |
}
|
295 |
|
292 |
|
296 |
/*
|
293 |
/*
|
297 |
* in $p: un tableau de paramètres, dont:
|
294 |
* in $p: un tableau de paramètres, dont:
|
298 |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
|
295 |
* - 'masque.tag_cel': *tableau* de mots-clefs à chercher parmi cel_image.mots_clefs_texte
|
299 |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
|
296 |
* - 'masque.tag_pictoflora': *tableau* de mots-clefs à chercher parmi del_image_tag.tag_normalise
|
300 |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
|
297 |
* - 'tag_explode_semantic': défini si les éléments sont tous recherchés ou NON
|
301 |
*
|
298 |
*
|
302 |
* in/ou: $req: un tableau de structure de requête MySQL
|
299 |
* in/ou: $req: un tableau de structure de requête MySQL
|
303 |
*
|
300 |
*
|
304 |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
|
301 |
* Attention, le fait que nous cherchions masque.tag_cel OU/ET masque.tag_cel
|
305 |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
|
302 |
* ne dépend pas de nous, mais du niveau supérieur de construction de la requête:
|
306 |
* Soit directement $this->consulter() si des masque.tag* sont passés
|
303 |
* Soit directement $this->consulter() si des masque.tag* sont passés
|
307 |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
|
304 |
* (split sur ",", "AND" entre chaque condition, "OR" pour chaque valeur de tag)
|
308 |
* Soit via sqlAddMasqueConstraint():
|
305 |
* Soit via sqlAddMasqueConstraint():
|
309 |
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
|
306 |
* (pas de split, "OR" entre chaque condition) [ comportement historique ]
|
310 |
* équivalent à:
|
307 |
* équivalent à:
|
311 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
308 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
312 |
*
|
- |
|
313 |
*/
|
309 |
*
|
314 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
310 |
*/
|
315 |
// TODO implement dans DelTk ?
|
311 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
316 |
if(!empty($p['masque.milieu'])) {
|
312 |
// TODO implement dans DelTk ?
|
317 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
313 |
if (!empty($p['masque.milieu'])) {
|
318 |
}
|
314 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
319 |
|
315 |
}
|
320 |
|
316 |
|
321 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
317 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
322 |
celui-ci indique sur quels votes porte l'AVG.
|
318 |
celui-ci indique sur quels votes porte l'AVG.
|
323 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
319 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
324 |
/* TODO: perf problème:
|
320 |
/* TODO: perf problème:
|
325 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
321 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
326 |
- dissociant le comptage de la récup d'id + javascript async
|
322 |
- dissociant le comptage de la récup d'id + javascript async
|
327 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
323 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
328 |
(paramètre booléen "with-total" par exemple)
|
324 |
(paramètre booléen "with-total" par exemple)
|
329 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
325 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
330 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
326 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
331 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
327 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
332 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
328 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
333 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
329 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
334 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
330 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
335 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
331 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
336 |
if($p['tri'] == 'votes' || $p['tri'] == 'points') {
|
332 |
if ($p['tri'] == 'votes' || $p['tri'] == 'points') {
|
337 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
333 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
338 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
334 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
339 |
if($p['ordre'] == 'desc') {
|
335 |
if($p['ordre'] == 'desc') {
|
340 |
// pas de group by nécessaire pour cette jointure
|
336 |
// pas de group by nécessaire pour cette jointure
|
341 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
337 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
342 |
$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'.
|
338 |
$req['join']['dis'] = sprintf('INNER JOIN del_image_stat dis'.
|
343 |
' ON vdi.id_image = dis.ce_image'.
|
339 |
' ON vdi.id_image = dis.ce_image'.
|
344 |
' AND dis.ce_protocole = %d',
|
340 |
' AND dis.ce_protocole = %d',
|
345 |
$p['protocole']);
|
341 |
$p['protocole']);
|
346 |
} else {
|
342 |
} else {
|
347 |
$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'.
|
343 |
$req['join']['dis'] = sprintf('LEFT JOIN del_image_stat dis'.
|
348 |
' ON vdi.id_image = dis.ce_image'.
|
344 |
' ON vdi.id_image = dis.ce_image'.
|
349 |
' AND dis.ce_protocole = %d',
|
345 |
' AND dis.ce_protocole = %d',
|
350 |
$p['protocole']);
|
346 |
$p['protocole']);
|
351 |
// nécessaire (dup ce_image dans del_image_stat)
|
347 |
// nécessaire (dup ce_image dans del_image_stat)
|
352 |
$req['groupby'][] = 'vdi.id_observation';
|
348 |
$req['groupby'][] = 'vdi.id_observation';
|
353 |
}
|
349 |
}
|
354 |
}
|
350 |
}
|
355 |
|
351 |
|
356 |
if($p['tri'] == 'tags') {
|
352 |
if ($p['tri'] == 'tags') {
|
357 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
353 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
Line -... |
Line 474... |
- |
|
474 |
' ORDER BY %s'.
|
- |
|
475 |
' LIMIT %d, %d -- %s',
|
- |
|
476 |
|
- |
|
477 |
$req['join'] ? implode(' ', array_unique($req['join'])) : '',
|
- |
|
478 |
$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
|
486 |
|
479 |
|
487 |
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
|
480 |
$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
|
488 |
|
481 |
|
489 |
$req['orderby'],
|
482 |
$req['orderby'],
|
490 |
|
483 |
|
491 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
484 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
492 |
}
|
485 |
}
|
493 |
|
486 |
|
494 |
static function chargerImages($db, $idImg) {
|
487 |
static function chargerImages($db, $idImg) {
|
495 |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
488 |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
496 |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
489 |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
497 |
|
490 |
|
498 |
return $db->recupererTous(sprintf('SELECT '.
|
491 |
return $db->recupererTous(sprintf('SELECT '.
|
499 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
492 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
500 |
' %1$s, %2$s FROM v_del_image '.
|
493 |
' %1$s, %2$s FROM v_del_image '.
|
501 |
' WHERE %3$s'.
|
494 |
' WHERE %3$s'.
|
502 |
' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN()
|
495 |
' ORDER BY %4$s'. // important car MySQL ne conserve par l'ordre du IN()
|
503 |
' -- %5$s',
|
496 |
' -- %5$s',
|
504 |
$obs_fields, $image_fields,
|
497 |
$obs_fields, $image_fields,
|
505 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
498 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
506 |
sprintf('FIELD(id_image, %s)', implode(',', $idImg)),
|
499 |
sprintf('FIELD(id_image, %s)', implode(',', $idImg)),
|
507 |
__FILE__ . ':' . __LINE__));
|
500 |
__FILE__ . ':' . __LINE__));
|
508 |
}
|
501 |
}
|
509 |
|
502 |
|
510 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
503 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
511 |
de manière identique à la seule différence que:
|
504 |
de manière identique à la seule différence que:
|
512 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
505 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
513 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
506 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
514 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
507 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
515 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
508 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
516 |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
|
509 |
Pour plus d'information: (ListeObservations|DelTk)::sqlAddMasqueConstraint() */
|
517 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
510 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
518 |
if(!empty($p['masque'])) {
|
511 |
if (!empty($p['masque'])) {
|
519 |
$or_params = array('masque.auteur' => $p['masque'],
|
512 |
$or_params = array('masque.auteur' => $p['masque'],
|
520 |
'masque.departement' => $p['masque'],
|
513 |
'masque.departement' => $p['masque'],
|
521 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
514 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
522 |
'masque.id_zone_geo' => $p['masque'],
|
515 |
'masque.id_zone_geo' => $p['masque'],
|
523 |
|
516 |
|
524 |
/* tous-deux remplacent masque.tag
|
517 |
/* tous-deux remplacent masque.tag
|
525 |
mais sont traité séparément des requestFilterParams() */
|
518 |
mais sont traité séparément des requestFilterParams() */
|
526 |
// 'masque.tag_cel' => $p['masque'],
|
519 |
// 'masque.tag_cel' => $p['masque'],
|
527 |
// 'masque.tag_pictoflora' => $p['masque'],
|
520 |
// 'masque.tag_pictoflora' => $p['masque'],
|
528 |
|
521 |
|
529 |
'masque.ns' => $p['masque'],
|
522 |
'masque.ns' => $p['masque'],
|
530 |
'masque.famille' => $p['masque'],
|
523 |
'masque.famille' => $p['masque'],
|
531 |
'masque.date' => $p['masque'],
|
524 |
'masque.date' => $p['masque'],
|
532 |
'masque.genre' => $p['masque'],
|
525 |
'masque.genre' => $p['masque'],
|
533 |
'masque.milieu' => $p['masque'],
|
526 |
'masque.milieu' => $p['masque'],
|
534 |
'masque.tag_cel' => $p['masque'],
|
527 |
'masque.tag_cel' => $p['masque'],
|
535 |
'masque.tag_pictoflora' => $p['masque'],
|
528 |
'masque.tag_pictoflora' => $p['masque'],
|
536 |
|
529 |
|
537 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
530 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
538 |
'tri' => $p['tri'],
|
531 |
'tri' => $p['tri'],
|
539 |
'ordre' => $p['ordre']);
|
532 |
'ordre' => $p['ordre']);
|
540 |
if (array_key_exists('protocole', $p)) {
|
533 |
if (array_key_exists('protocole', $p)) {
|
541 |
$or_params['protocole'] = $p['protocole'];
|
534 |
$or_params['protocole'] = $p['protocole'];
|
542 |
}
|
535 |
}
|
543 |
|
536 |
|
544 |
/* Cependant les champs spécifiques ont priorité sur le masque général.
|
537 |
/* Cependant les champs spécifiques ont priorité sur le masque général.
|
545 |
Pour cette raison nous supprimons la génération de SQL du masque général sur les
|
538 |
Pour cette raison nous supprimons la génération de SQL du masque général sur les
|
546 |
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
|
539 |
champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
|
547 |
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
|
540 |
if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
|
548 |
if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
|
541 |
if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
|
549 |
if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
|
542 |
if(isset($p['masque.commune'])) unset($or_params['masque.commune']);
|
Line 550... |
Line 543... |
550 |
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
|
543 |
if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
|
551 |
if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
|
544 |
if(isset($p['masque.ns'])) unset($or_params['masque.ns']);
|
552 |
if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
|
545 |
if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
|
553 |
if(isset($p['masque.date'])) unset($or_params['masque.date']);
|
546 |
if(isset($p['masque.date'])) unset($or_params['masque.date']);
|
554 |
if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
|
547 |
if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
|
555 |
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
|
548 |
if(isset($p['masque.milieu'])) unset($or_params['masque.milieu']);
|
556 |
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
|
549 |
if(isset($p['masque.tag_cel'])) unset($or_params['masque.tag_cel']);
|
557 |
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
|
550 |
if(isset($p['masque.tag_pictoflora'])) unset($or_params['masque.tag_pictoflora']);
|
558 |
|
551 |
|
- |
|
552 |
$or_masque = array_merge(
|
559 |
$or_masque = array_merge(
|
553 |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
560 |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
554 |
self::requestFilterParams($or_params)
|
561 |
self::requestFilterParams($or_params)
|
555 |
);
|
562 |
);
|
556 |
|
563 |
|
557 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
564 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
558 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
565 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
559 |
if (isset($or_params['masque.tag_cel'])) {
|
566 |
if(isset($or_params['masque.tag_cel']))
|
560 |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
567 |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
561 |
}
|
568 |
if(isset($or_params['masque.tag_pictoflora']))
|
562 |
if (isset($or_params['masque.tag_pictoflora'])) {
|
569 |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
563 |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
570 |
|
564 |
}
|
571 |
|
565 |
|
572 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
566 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
573 |
$or_req = array('join' => array(), 'where' => array());
|
567 |
$or_req = array('join' => array(), 'where' => array());
|
574 |
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
|
568 |
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
|
575 |
|
569 |
|
576 |
self::sqlAddConstraint($or_masque, $db, $or_req);
|
570 |
self::sqlAddConstraint($or_masque, $db, $or_req);
|
577 |
|
571 |
|
578 |
if($or_req['where']) {
|
572 |
if ($or_req['where']) {
|
579 |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
|
573 |
$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
|
580 |
// utile au cas ou des jointures seraient rajoutées
|
574 |
// utile au cas ou des jointures seraient rajoutées
|
581 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
575 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
582 |
}
|
576 |
}
|
583 |
}
|
577 |
}
|
584 |
}
|
578 |
}
|
585 |
|
579 |
|
586 |
|
580 |
|
587 |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
|
581 |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
|
588 |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
|
582 |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
|
589 |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
|
583 |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
|
590 |
// XXX: cf Observation.php::consulter(), nous pourriouns ici
|
584 |
// XXX: cf Observation.php::consulter(), nous pourriouns ici
|
591 |
// conserver les valeurs vides (pour les phptests notamment, ou non)
|
585 |
// conserver les valeurs vides (pour les phptests notamment, ou non)
|
592 |
// $obs = array_map('array_filter', $obs);
|
586 |
// $obs = array_map('array_filter', $obs);
|
593 |
$obs_merged = $obs_keyed_by_id_image = array();
|
587 |
$obs_merged = $obs_keyed_by_id_image = array();
|
594 |
foreach($obs as $o) {
|
588 |
foreach ($obs as $o) {
|
595 |
// ceci nous complique la tâche pour le reste du processing...
|
589 |
// ceci nous complique la tâche pour le reste du processing...
|
596 |
$id = $o['jsonindex'];
|
590 |
$id = $o['jsonindex'];
|
597 |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
|
591 |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
|
598 |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
|
592 |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
|
599 |
// mais tout deux partage leur référence à "protocole"
|
593 |
// mais tout deux partage leur référence à "protocole"
|
600 |
$image = array(
|
- |
|
601 |
'id_image' => $o['id_image'],
|
- |
|
602 |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
|
- |
|
603 |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
|
594 |
$image = array(
|
604 |
);
|
- |
|
605 |
|
595 |
'id_image' => $o['id_image'],
|
606 |
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
|
- |
|
- |
|
596 |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
|
Line -... |
Line 597... |
- |
|
597 |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
|
- |
|
598 |
);
|
Line -... |
Line 599... |
- |
|
599 |
|
- |
|
600 |
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
|
Line 607... |
Line 601... |
607 |
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
|
601 |
if (!isset($obs_merged[$id])) {
|
608 |
$obs_merged[$id]['observation'] = $o;
|
602 |
$obs_merged[$id] = $image;
|
609 |
$obs_merged[$id]['protocoles_votes'] = array();
|
603 |
}
|
610 |
|
604 |
$obs_merged[$id]['observation'] = $o;
|
611 |
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
|
605 |
$obs_merged[$id]['protocoles_votes'] = array();
|
612 |
}
|
606 |
|
613 |
|
607 |
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] =& $obs_merged[$id]['protocoles_votes'];
|
614 |
return array($obs_merged,$obs_keyed_by_id_image);
|
608 |
}
|
615 |
}
|
609 |
|
616 |
|
610 |
return array($obs_merged,$obs_keyed_by_id_image);
|
617 |
|
611 |
}
|
- |
|
612 |
|
618 |
|
613 |
// complete & override DelTk::requestFilterParams() (même usage)
|
- |
|
614 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
619 |
// complete & override DelTk::requestFilterParams() (même usage)
|
615 |
if ($parametres_autorises) { // filtrage de toute clef inconnue
|
620 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
616 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
621 |
if($parametres_autorises) { // filtrage de toute clef inconnue
|
617 |
}
|
622 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
618 |
|
623 |
}
|
619 |
$p = array();
|
624 |
|
620 |
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
|
625 |
$p = array();
|
621 |
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
|
626 |
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
|
622 |
|
627 |
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
|
623 |
// "milieu" inutile pour IdentiPlantes ?
|
628 |
|
624 |
if (isset($params['masque.milieu'])) {
|
629 |
// "milieu" inutile pour IdentiPlantes ?
|
625 |
$p['masque.milieu'] = trim($params['masque.milieu']);
|
630 |
if(isset($params['masque.milieu'])) $p['masque.milieu'] = trim($params['masque.milieu']);
|
626 |
}
|
631 |
|
627 |
|
632 |
// compatibilité
|
628 |
// compatibilité
|
633 |
if(isset($params['masque.tag'])) {
|
- |
|
634 |
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
|
- |
|
635 |
}
|
- |
|
636 |
|
- |
|
Line -... |
Line 629... |
- |
|
629 |
if (isset($params['masque.tag'])) {
|
- |
|
630 |
$params['masque.tag_cel'] = $params['masque.tag_pictoflora'] = $params['masque.tag'];
|
Line 637... |
Line -... |
637 |
if($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {
|
- |
|
638 |
// ces critère de tri des image à privilégier ne s'applique qu'à un protocole donné
|
- |
|
639 |
if(!isset($params['protocole']) || !is_numeric($params['protocole'])) {
|
- |
|
640 |
$p['protocole'] = self::$default_proto;
|
- |
|
641 |
} else {
|
- |
|
642 |
$p['protocole'] = intval($params['protocole']);
|
- |
|
643 |
}
|
- |
|
644 |
}
|
- |
|
645 |
|
- |
|
646 |
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
|
- |
|
647 |
}
|
- |
|
648 |
|
- |
|
649 |
|
- |
|
650 |
|
- |
|
651 |
// met à jour *toutes* les stats de nombre de tags et de moyenne des votes
|
- |
|
652 |
static function _update_statistics($db) {
|
- |
|
653 |
$db->requeter("TRUNCATE TABLE del_image_stat");
|
- |
|
654 |
$db->requeter(<<<EOF
|
- |
|
655 |
INSERT INTO `del_image_stat` (
|
- |
|
656 |
SELECT id_image, divo.ce_protocole, divo.moyenne, divo.nb_votes, dit.ctags
|
- |
|
657 |
FROM `tb_cel`.`cel_images` ci
|
- |
|
658 |
LEFT JOIN
|
- |
|
659 |
( SELECT ce_image, ce_protocole, AVG(valeur) AS moyenne, COUNT(valeur) AS nb_votes FROM del_image_vote
|
- |
|
660 |
GROUP BY ce_image, ce_protocole ) AS divo
|
631 |
}
|
- |
|
632 |
|
661 |
ON ci.id_image = divo.ce_image
|
633 |
if ($p['tri'] == 'votes' || $p['tri'] == 'tags' || $p['tri'] == 'points') {
|