Line 20... |
Line 20... |
20 |
* Par contre, le tri par moyenne des votes, sous-entend "pour un protocole donné".
|
20 |
* Par contre, le tri par moyenne des votes, sous-entend "pour un protocole donné".
|
21 |
* Dès lors le choix d'un protocole doit avoir été fait afin de régler le JOIN et ainsi l'ORDER BY.
|
21 |
* Dès lors le choix d'un protocole doit avoir été fait afin de régler le JOIN et ainsi l'ORDER BY.
|
22 |
* (cf requestFilterParams())
|
22 |
* (cf requestFilterParams())
|
23 |
*
|
23 |
*
|
24 |
* Histoire: auparavant (pré-r142x) un AVG + GROUP BY étaient utilisés pour générer on-the-fly les valeurs
|
24 |
* Histoire: auparavant (pré-r142x) un AVG + GROUP BY étaient utilisés pour générer on-the-fly les valeurs
|
25 |
* utilsées ensuite pour l'ORDER BY. La situation à base de del_image_stat
|
25 |
* utilisées ensuite pour l'ORDER BY. La situation à base de del_image_stat
|
26 |
* est déjà bien meilleur sans être pour autant optimale. cf commentaire de sqlAddConstraint()
|
26 |
* est déjà bien meilleure sans être pour autant optimale. cf commentaire de sqlAddConstraint()
|
27 |
*
|
27 |
*
|
28 |
*
|
28 |
*
|
29 |
* Tags:
|
29 |
* Tags:
|
30 |
* Le comportement habituel dans le masque *général*: les mots sont séparés par des espaces,
|
30 |
* Le comportement habituel dans le masque *général*: les mots sont séparés par des espaces,
|
31 |
* implod()ed par des AND (tous les mots doivent matcher).
|
31 |
* implod()ed par des AND (tous les mots doivent matcher).
|
Line 45... |
Line 45... |
45 |
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
|
45 |
* - subqueries dans le FROM pour les critère WHERE portant directement sur v_del_image
|
46 |
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
|
46 |
* plutôt que dans WHERE (qui nécessite dès lors un FULL-JOIN)
|
47 |
* (http://www.mysqlperformanceblog.com/2007/04/06/using-delayed-join-to-optimize-count-and-limit-queries/)
|
47 |
* (http://www.mysqlperformanceblog.com/2007/04/06/using-delayed-join-to-optimize-count-and-limit-queries/)
|
48 |
* - éviter de dépendre d'une jointure systématique sur `cel_obs`, uniquement pour `(date_)transmission
|
48 |
* - éviter de dépendre d'une jointure systématique sur `cel_obs`, uniquement pour `(date_)transmission
|
49 |
* (cf VIEW del_image)
|
49 |
* (cf VIEW del_image)
|
50 |
* - réorganiser les méthodes statiques parmis Observation, ListeObservations et ListImages2
|
50 |
* - poursuivre la réorganisation des méthodes statiques parmis Observation, ListeObservations et ListImages2
|
51 |
* - *peut-être*: passer requestFilterParams() en méthode de classe
|
51 |
* - *peut-être*: passer requestFilterParams() en méthode de classe
|
52 |
*
|
52 |
*
|
53 |
*
|
53 |
*
|
54 |
* MySQL sux:
|
54 |
* MySQL sux:
|
55 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
|
55 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
|
Line 63... |
Line 63... |
63 |
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
|
63 |
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
|
64 |
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
|
64 |
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
|
65 |
*
|
65 |
*
|
66 |
*/
|
66 |
*/
|
Line -... |
Line 67... |
- |
|
67 |
|
67 |
|
68 |
require_once(dirname(__FILE__) . '/../DelTk.php');
|
68 |
require_once(dirname(__FILE__) . '/../observations/ListeObservations.php');
|
69 |
require_once(dirname(__FILE__) . '/../observations/ListeObservations.php');
|
69 |
require_once(dirname(__FILE__) . '/../observations/Observation.php');
|
70 |
require_once(dirname(__FILE__) . '/../observations/Observation.php');
|
70 |
restore_error_handler();
|
71 |
restore_error_handler();
|
71 |
restore_exception_handler();
|
72 |
restore_exception_handler();
|
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"
|
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');
|
Line 83... |
Line 84... |
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 |
|
Line 86... |
Line 87... |
86 |
// en plus de ceux dans ListeObservations
|
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 |
|
Line 149... |
Line 150... |
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 = ListeObservations::requestFilterParams($parametres,
|
155 |
$params_ip = DelTk::requestFilterParams($parametres,
|
155 |
array_diff(ListeObservations::$parametres_autorises,
|
156 |
array_diff(DelTk::$parametres_autorises,
|
156 |
array('masque.type')),
|
157 |
array('masque.type')),
|
Line 157... |
Line 158... |
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,
|
Line 161... |
Line 162... |
161 |
array_merge(ListeObservations::$parametres_autorises,
|
162 |
array_merge(DelTk::$parametres_autorises,
|
162 |
self::$parametres_autorises));
|
163 |
self::$parametres_autorises));
|
163 |
|
164 |
|
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
|
Line -... |
Line 167... |
- |
|
167 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
166 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
168 |
$params_pf['masque.tag_cel'] = DelTk::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
167 |
$params_pf['masque.tag_cel'] = self::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
169 |
$params_pf['masque.tag_pictoflora'] = DelTk::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
168 |
$params_pf['masque.tag_pictoflora'] = self::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
170 |
|
169 |
|
171 |
$params = array_merge(
|
Line 170... |
Line 172... |
170 |
$params = array_merge(ListeObservations::$default_params, // paramètre par défaut Identiplante
|
172 |
DelTk::$default_params, // paramètre par défaut Identiplante
|
171 |
self::$default_params, // paramètres par défaut PictoFlora
|
173 |
self::$default_params, // paramètres par défaut PictoFlora
|
172 |
$params_ip, // les paramètres passés, traités par Identiplante
|
174 |
$params_ip, // les paramètres passés, traités par Identiplante
|
Line 173... |
Line 175... |
173 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
175 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
- |
|
176 |
|
- |
|
177 |
// XXX: temp tweak
|
174 |
|
178 |
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('url_images'),
|
175 |
// XXX: temp tweak
|
179 |
"%09d", $params['format']));*/
|
176 |
/* $this->conteneur->setParametre('url_images', sprintf($this->conteneur->getParametre('url_images'),
|
180 |
|
177 |
"%09d", $params['format']));*/
|
181 |
// création des contraintes (génériques de DelTk)
|
178 |
|
182 |
DelTk::sqlAddConstraint($params, $db, $req);
|
Line 190... |
Line 194... |
190 |
$idobs_tab = self::getIdImages($params, $req, $db);
|
194 |
$idobs_tab = self::getIdImages($params, $req, $db);
|
Line 191... |
Line 195... |
191 |
|
195 |
|
192 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
196 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
193 |
if(!$idobs_tab) {
|
197 |
if(!$idobs_tab) {
|
194 |
$resultat = new ResultatService();
|
198 |
$resultat = new ResultatService();
|
195 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader(0, $params, Config::get('url_service')),
|
199 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader(0, $params, Config::get('url_service')),
|
196 |
'resultats' => array());
|
200 |
'resultats' => array());
|
197 |
return $resultat;
|
201 |
return $resultat;
|
198 |
/*
|
202 |
/*
|
199 |
header('HTTP/1.0 404 Not Found');
|
203 |
header('HTTP/1.0 404 Not Found');
|
Line 214... |
Line 218... |
214 |
$o = new Observation($this->conteneur);
|
218 |
$o = new Observation($this->conteneur);
|
215 |
foreach($idobs as $i) {
|
219 |
foreach($idobs as $i) {
|
216 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
220 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
217 |
}
|
221 |
}
|
218 |
*/
|
222 |
*/
|
219 |
list($images, $images_keyed_by_id_image) = ListeObservations::reformateImagesDoubleIndex(
|
223 |
list($images, $images_keyed_by_id_image) = self::reformateImagesDoubleIndex(
|
220 |
$liaisons,
|
224 |
$liaisons,
|
221 |
$this->conteneur->getParametre('url_images'),
|
225 |
$this->conteneur->getParametre('url_images'),
|
222 |
$params['format']);
|
226 |
$params['format']);
|
Line 235... |
Line 239... |
235 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
239 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
236 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
240 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
237 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
241 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
238 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
242 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
239 |
$resultat = new ResultatService();
|
243 |
$resultat = new ResultatService();
|
240 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
244 |
$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
241 |
'resultats' => $images);
|
245 |
'resultats' => $images);
|
242 |
return $resultat;
|
246 |
return $resultat;
|
243 |
}
|
247 |
}
|
Line 244... |
Line 248... |
244 |
|
248 |
|
Line 290... |
Line 294... |
290 |
* équivalent à:
|
294 |
* équivalent à:
|
291 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
295 |
* (split sur " ", "OR" entre chaque condition, "AND" pour chaque valeur de tag)
|
292 |
*
|
296 |
*
|
293 |
*/
|
297 |
*/
|
294 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
298 |
static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
295 |
// TODO implement dans ListeObservations ?
|
299 |
// TODO implement dans DelTk ?
|
296 |
if(!empty($p['masque.milieu'])) {
|
300 |
if(!empty($p['masque.milieu'])) {
|
297 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
301 |
$req['where'][] = 'vdi.milieu LIKE '.$db->proteger('%' . $p['masque.milieu'].'%');
|
298 |
}
|
302 |
}
|
Line 438... |
Line 442... |
438 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
442 |
$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__));
|
Line 439... |
Line 443... |
439 |
|
443 |
|
Line 440... |
Line 444... |
440 |
}
|
444 |
}
|
441 |
|
445 |
|
442 |
static function chargerImages($db, $idImg) {
|
446 |
static function chargerImages($db, $idImg) {
|
Line 443... |
Line 447... |
443 |
$obs_fields = Observation::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
447 |
$obs_fields = DelTk::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
444 |
$image_fields = Observation::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
448 |
$image_fields = DelTk::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
445 |
|
449 |
|
446 |
return $db->recupererTous(sprintf('SELECT '.
|
450 |
return $db->recupererTous(sprintf('SELECT '.
|
Line 482... |
Line 486... |
482 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
486 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
483 |
'tri' => $p['tri'],
|
487 |
'tri' => $p['tri'],
|
484 |
'ordre' => $p['ordre']);
|
488 |
'ordre' => $p['ordre']);
|
Line 485... |
Line 489... |
485 |
|
489 |
|
486 |
$or_masque = array_merge(
|
490 |
$or_masque = array_merge(
|
487 |
ListeObservations::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
491 |
DelTk::requestFilterParams($or_params, NULL, $c /* pour masque.departement */),
|
Line 488... |
Line 492... |
488 |
self::requestFilterParams($or_params));
|
492 |
self::requestFilterParams($or_params));
|
489 |
|
493 |
|
490 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
494 |
/* Lorsqu'on utilise le masque général pour chercher des tags, ils sont
|
491 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
495 |
postulés comme séparés par des espaces, et doivent être tous matchés. */
|
Line 492... |
Line 496... |
492 |
$or_masque['masque.tag_cel'] = self::buildTagsAST($p['masque'], 'AND', ' ');
|
496 |
$or_masque['masque.tag_cel'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
493 |
$or_masque['masque.tag_pictoflora'] = self::buildTagsAST($p['masque'], 'AND', ' ');
|
497 |
$or_masque['masque.tag_pictoflora'] = DelTk::buildTagsAST($p['masque'], 'AND', ' ');
|
- |
|
498 |
|
494 |
|
499 |
|
495 |
|
500 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
Line 496... |
Line 501... |
496 |
// pas de select, groupby & co ici: uniquement 'join' et 'where'
|
501 |
$or_req = array('join' => array(), 'where' => array());
|
497 |
$or_req = array('join' => array(), 'where' => array());
|
502 |
DelTk::sqlAddConstraint($or_masque, $db, $or_req);
|
Line 504... |
Line 509... |
504 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
509 |
$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
|
505 |
}
|
510 |
}
|
506 |
}
|
511 |
}
|
507 |
}
|
512 |
}
|
Line -... |
Line 513... |
- |
|
513 |
|
- |
|
514 |
|
- |
|
515 |
// cf Observation::reformateObservationSimpleIndex() et ListeObservations::reformateObservation()
|
- |
|
516 |
// (trop de variétés de formatage, à unifier côté client pour unifier côté backend ...)
|
- |
|
517 |
static function reformateImagesDoubleIndex($obs, $url_pattern = '', $image_format = 'XL') {
|
- |
|
518 |
// XXX: cf Observation.php::consulter(), nous pourriouns ici
|
- |
|
519 |
// conserver les valeurs vides (pour les phptests notamment, ou non)
|
- |
|
520 |
// $obs = array_map('array_filter', $obs);
|
- |
|
521 |
$obs_merged = $obs_keyed_by_id_image = array();
|
- |
|
522 |
foreach($obs as $o) {
|
- |
|
523 |
// ceci nous complique la tâche pour le reste du processing...
|
- |
|
524 |
$id = $o['jsonindex'];
|
- |
|
525 |
// ainsi nous utilisons deux tableaux: le final, indexé par couple d'id(image-obs)
|
- |
|
526 |
// et celui indexé par simple id_image qui est fort utile pour mapVotesToImages()
|
- |
|
527 |
// mais tout deux partage leur référence à "protocole"
|
- |
|
528 |
$image = array(
|
- |
|
529 |
'id_image' => $o['id_image'],
|
- |
|
530 |
'binaire.href' => sprintf($url_pattern, $o['id_image'], $image_format),
|
- |
|
531 |
'mots_cles_texte' => @$o['i_mots_cles_texte'], // @, peut avoir été filtré par array_map() ci-dessus
|
- |
|
532 |
);
|
- |
|
533 |
unset($o['id_image'], $o['i_mots_cles_texte'], $o['jsonindex']);
|
- |
|
534 |
if(!isset($obs_merged[$id])) $obs_merged[$id] = $image;
|
- |
|
535 |
$obs_merged[$id]['observation'] = $o;
|
- |
|
536 |
$obs_merged[$id]['protocoles_votes'] = array();
|
- |
|
537 |
|
- |
|
538 |
$obs_keyed_by_id_image[$image['id_image']]['protocoles_votes'] = &$obs_merged[$id]['protocoles_votes'];
|
- |
|
539 |
}
|
- |
|
540 |
|
- |
|
541 |
return array($obs_merged,$obs_keyed_by_id_image);
|
- |
|
542 |
}
|
- |
|
543 |
|
- |
|
544 |
|
508 |
|
545 |
|
509 |
// complete & override ListeObservations::requestFilterParams() (même usage)
|
546 |
// complete & override DelTk::requestFilterParams() (même usage)
|
510 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
547 |
static function requestFilterParams(Array $params, $parametres_autorises = NULL) {
|
511 |
if($parametres_autorises) { // filtrage de toute clef inconnue
|
548 |
if($parametres_autorises) { // filtrage de toute clef inconnue
|
512 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
549 |
$params = array_intersect_key($params, array_flip($parametres_autorises));
|
Line 513... |
Line 550... |
513 |
}
|
550 |
}
|
514 |
|
551 |
|
515 |
$p = array();
|
552 |
$p = array();
|
Line 516... |
Line 553... |
516 |
$p['tri'] = ListeObservations::unsetIfInvalid($params, 'tri', self::$tri_possible);
|
553 |
$p['tri'] = DelTk::unsetIfInvalid($params, 'tri', self::$tri_possible);
|
517 |
$p['format'] = ListeObservations::unsetIfInvalid($params, 'format', self::$format_image_possible);
|
554 |
$p['format'] = DelTk::unsetIfInvalid($params, 'format', self::$format_image_possible);
|
Line 518... |
Line 555... |
518 |
|
555 |
|
Line 534... |
Line 571... |
534 |
|
571 |
|
535 |
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
|
572 |
return array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
|
Line 536... |
Line -... |
536 |
}
|
- |
|
537 |
|
- |
|
538 |
|
- |
|
539 |
/* Construit un (vulgaire) abstract syntax tree:
|
- |
|
540 |
"AND" => [ "tag1", "tag2" ]
|
- |
|
541 |
Idéalement (avec un parser simple comme proposé par http://hoa-project.net/Literature/Hack/Compiler.html#Langage_PP)
|
- |
|
542 |
nous aurions:
|
- |
|
543 |
"AND" => [ "tag1", "tag2", "OR" => [ "tag3", "tag4" ] ]
|
- |
|
544 |
|
- |
|
545 |
Ici nous devons traiter les cas suivants:
|
- |
|
546 |
tags séparés par des "ET/AND OU/OR", séparés par des espaces ou des virgules.
|
- |
|
547 |
Mais la chaîne peut aussi avoir été issue du "masque général" (la barre de recherche générique).
|
- |
|
548 |
ce qui implique des comportement par défaut différents afin de préserver la compatibilité.
|
- |
|
549 |
|
- |
|
550 |
Théorie:
|
- |
|
551 |
1) tags passés par "champ tag":
|
- |
|
552 |
- support du ET/OU, et explode par virgule.
|
- |
|
553 |
- si pas d'opérande détectée: "OU"
|
- |
|
554 |
|
- |
|
555 |
2) tags passés par "recherche générale":
|
- |
|
556 |
- support du ET/OU, et explode par whitespace.
|
- |
|
557 |
- si pas d'opérande détectée: "ET"
|
- |
|
558 |
|
- |
|
559 |
La présence de $additional_sep s'explique car ET/OU sous-entendent une séparation par des espaces.
|
- |
|
560 |
Mais ce n'est pas toujours pertinent car: 1) la compatibilité suggère de considérer parfois
|
- |
|
561 |
la virgule comme séparateur et 2) les tags *peuvent* contenir des espaces. Par conséquent:
|
- |
|
562 |
* a,b,c => "a" $default_op "b" $default_op "c"
|
- |
|
563 |
* a,b AND c => "a" AND "b" AND "c"
|
- |
|
564 |
* a OR b AND c,d => "a" AND "b" AND "c" AND "d"
|
- |
|
565 |
C'est à dire par ordre décroissant de priorité:
|
- |
|
566 |
1) opérande contenu dans la chaîne
|
- |
|
567 |
2) opérande par défaut
|
- |
|
568 |
3) les séparateurs présents sont substitués par l'opérande déterminée par 1) ou 2)
|
- |
|
569 |
|
- |
|
570 |
// TODO: support des parenthèses, imbrications & co: "(", ")"
|
- |
|
571 |
// http://codehackit.blogspot.fr/2011/08/expression-parser-in-php.html
|
- |
|
572 |
// http://blog.angeloff.name/post/2012/08/05/php-recursive-patterns/
|
- |
|
573 |
|
- |
|
574 |
@param $str: la chaîne à "parser"
|
- |
|
575 |
@param $default_op: "AND" ou "OR"
|
- |
|
576 |
@param $additional_sep: séparateur de mots:
|
- |
|
577 |
*/
|
- |
|
578 |
static function buildTagsAST($str = NULL, $default_op, $additional_sep = ',') {
|
- |
|
579 |
if(!$str) return;
|
- |
|
580 |
$words = preg_split('/ (OR|AND|ET|OU) /', $str, -1, PREG_SPLIT_NO_EMPTY);
|
- |
|
581 |
|
- |
|
582 |
if(preg_match('/\b(ET|AND)\b/', $str)) $op = 'AND';
|
- |
|
583 |
elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
|
- |
|
584 |
else $op = $default_op;
|
- |
|
585 |
|
- |
|
586 |
if($additional_sep) {
|
- |
|
587 |
array_walk($words,
|
- |
|
588 |
create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
|
- |
|
589 |
$additional_sep);
|
- |
|
590 |
}
|
- |
|
591 |
$words = self::array_flatten($words);
|
- |
|
592 |
$words = array_map('trim', $words);
|
- |
|
Line 593... |
Line 573... |
593 |
return array($op => array_filter($words));
|
573 |
}
|
594 |
}
|
574 |
|
595 |
|
575 |
|
596 |
|
576 |
|
Line 614... |
Line 594... |
614 |
}
|
594 |
}
|
Line 615... |
Line 595... |
615 |
|
595 |
|
616 |
static function revOrderBy($orderby) {
|
596 |
static function revOrderBy($orderby) {
|
617 |
return $orderby == 'asc' ? 'desc' : 'asc';
|
597 |
return $orderby == 'asc' ? 'desc' : 'asc';
|
618 |
}
|
- |
|
619 |
|
- |
|
620 |
static function array_flatten($arr) {
|
- |
|
621 |
$arr = array_values($arr);
|
- |
|
622 |
while (list($k,$v)=each($arr)) {
|
- |
|
623 |
if (is_array($v)) {
|
- |
|
624 |
array_splice($arr,$k,1,$v);
|
- |
|
625 |
next($arr);
|
- |
|
626 |
}
|
- |
|
627 |
}
|
- |
|
628 |
return $arr;
|
- |
|
629 |
}
|
598 |
}
|