Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 859 | Rev 862 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 859 Rev 861
Line 23... Line 23...
23
// *et* la valeur maximale autorisée pour navigation.limite.
23
// *et* la valeur maximale autorisée pour navigation.limite.
24
define('_COSTE_TEXTE_MAX_RESULT_LIMIT', 500);
24
define('_COSTE_TEXTE_MAX_RESULT_LIMIT', 500);
Line 25... Line 25...
25
 
25
 
26
// simplifie et accélère la sanitization de l'input:
26
// simplifie et accélère la sanitization de l'input:
27
// SELECT MAX(num_nom) FROM coste_v2_00;
27
// SELECT MAX(num_nom) FROM coste_v2_00;
-
 
28
define('_COSTE_TEXTE_MAX_COSTE_NN', 7015 + 1000);
-
 
29
// SELECT MAX(num_nom) FROM bdtfx_v1_02;
Line 28... Line 30...
28
define('_COSTE_TEXTE_MAX_NUM_NOM', 7015 + 1000);
30
define('_COSTE_TEXTE_MAX_BDTFX_NN', 103386 + 10000);
29
 
31
 
30
/*restore_error_handler();
32
/*restore_error_handler();
31
  error_reporting(E_ALL);*/
33
  error_reporting(E_ALL);*/
Line 43... Line 45...
43
								   'navigation.depart' => 0, 'navigation.limite' => 50);
45
								   'navigation.depart' => 0, 'navigation.limite' => 50);
Line 44... Line 46...
44
 
46
 
45
	// les champs de base de coste_v2_00
47
	// les champs de base de coste_v2_00
46
	// mysql -N tb_eflore<<<"SHOW FIELDS FROM coste_v2_00"|egrep -v 'page_'|awk '{print $1}'|xargs -i -n1 printf "'%s' => 'c.%s',\n" {} {}
48
	// mysql -N tb_eflore<<<"SHOW FIELDS FROM coste_v2_00"|egrep -v 'page_'|awk '{print $1}'|xargs -i -n1 printf "'%s' => 'c.%s',\n" {} {}
-
 
49
	static $allow_champs = array(
-
 
50
		'coste:nn' => 'c.num_nom',
-
 
51
		'bdtfx:nn' => 'IF(c.flore_bdtfx_nn = "",NULL,c.flore_bdtfx_nn)',
47
	static $allow_champs = array(
52
		'bdtfx:nt' => 'c.flore_bdtfx_nt',
48
		'num_nom' => 'c.num_nom',
53
		'num_nom' => 'c.num_nom',
49
		'num_nom_retenu' => 'c.num_nom_retenu',
54
		'num_nom_retenu' => 'c.num_nom_retenu',
50
		'num_tax_sup' => 'c.num_tax_sup',
55
		'num_tax_sup' => 'c.num_tax_sup',
51
		'rang' => 'c.rang',
56
		'rang' => 'c.rang',
Line 91... Line 96...
91
		'famille' => 'b.famille', // cf sqlAddJoins()
96
		'famille' => 'b.famille', // cf sqlAddJoins()
92
		'*' => 'XXX' // spécial
97
		'*' => 'XXX' // spécial
93
	);
98
	);
Line 94... Line 99...
94
 
99
 
-
 
100
	// les champs suivants disparaissent de la liste utilisée pour former la requête SQL
95
	// les champs suivants disparaissent de la liste utilisée pour former la requête SQL
101
	// (ils sont exclue lorsque '*' est utilisée, ce sont généralement des synonymes)
-
 
102
	static $special_champs = array('nom_sci_html', 'nom_sci', '*', 'flore_bdtfx_nn', 'flore_bdtfx_nt', 'num_nom');
-
 
103
 
-
 
104
	// ces champs sont toujours dans les résultats (cf sqlSelectFields()
-
 
105
	static $champs_obligatoires = array('coste:nn', 'bdtfx:nn');
-
 
106
 
96
	static $special_champs = array('nom_sci_html', 'nom_sci', '*');
107
 
97
	// le pattern utilisé pour la recherche dite "floue"
108
	// le pattern utilisé pour la recherche dite "floue"
Line 98... Line 109...
98
	static $soundex_scheme = '(%1$s LIKE %2$s OR SOUNDEX(%1$s) = SOUNDEX(%2$s) OR SOUNDEX(REVERSE(%1$s)) = SOUNDEX(REVERSE(%2$s)))';
109
	static $soundex_scheme = '(%1$s LIKE %2$s OR SOUNDEX(%1$s) = SOUNDEX(%2$s) OR SOUNDEX(REVERSE(%1$s)) = SOUNDEX(REVERSE(%2$s)))';
99
 
110
 
Line 143... Line 154...
143
			// nombre de matches (sans LIMIT) utilisé pour l'en-tête
154
			// nombre de matches (sans LIMIT) utilisé pour l'en-tête
144
			$matches = $db->recuperer('SELECT FOUND_ROWS() AS total');
155
			$matches = $db->recuperer('SELECT FOUND_ROWS() AS total');
145
			$matches = intval($matches['total']);
156
			$matches = intval($matches['total']);
146
		}
157
		}
Line 147... Line 158...
147
 
158
 
148
		// reformate les résultats pour les indexer par num_nom
159
		// reformate les résultats pour les indexer par coste:nn
149
		$res2 = array();
160
		$res2 = array();
150
		foreach($res as $v) {
161
		foreach($res as $v) {
151
			$res2[$v['num_nom']] = $v;
162
			$res2[$v['coste:nn']] = $v;
Line 152... Line 163...
152
		}
163
		}
153
 
164
 
154
		// l'appelant s'occupera du json_encode()
165
		// l'appelant s'occupera du json_encode()
Line 161... Line 172...
161
					 'resultats' => $res2);
172
					 'resultats' => $res2);
Line 162... Line 173...
162
 
173
 
Line 163... Line 174...
163
	}
174
	}
164
 
175
 
165
 
176
 
166
	// la fonction central: récupère les infos à partir de paramètres
177
	// la fonction centrale: récupère les infos à partir de paramètres
167
	// et une optionnel contrainte de num_nom
178
	// et une optionnelle contrainte sur coste:nn ou bdtfx:nn
168
	static function getCosteInfo(array $params, $db) {
179
	static function getCosteInfo(array $params, $db) {
Line 174... Line 185...
174
		$champs_valides_non_formattes = NULL;
185
		$champs_valides_non_formattes = NULL;
175
		$champs_valides = self::sqlSelectFields($params, $champs_valides_non_formattes);
186
		$champs_valides = self::sqlSelectFields($params, $champs_valides_non_formattes);
Line 176... Line 187...
176
 
187
 
177
		// joins:
188
		// joins:
178
		$other_join = self::sqlAddJoins($params, $champs_valides_non_formattes);
-
 
179
 
189
		$other_join = self::sqlAddJoins($params, $champs_valides_non_formattes);
180
		$req = sprintf(<<<EOF
190
		$req = sprintf(<<<EOF
181
SELECT SQL_CALC_FOUND_ROWS c.num_nom, %s
191
SELECT SQL_CALC_FOUND_ROWS  %s
182
FROM tb_eflore.coste_v2_00 c
192
FROM tb_eflore.coste_v2_00 c
183
LEFT JOIN tela_prod_wikini.florecoste_pages dsc ON c.page_wiki_dsc = dsc.tag AND dsc.latest = 'Y'
193
LEFT JOIN tela_prod_wikini.florecoste_pages dsc ON c.page_wiki_dsc = dsc.tag AND dsc.latest = 'Y'
184
LEFT JOIN tela_prod_wikini.florecoste_pages cle ON c.page_wiki_cle = cle.tag AND cle.latest = 'Y'
194
LEFT JOIN tela_prod_wikini.florecoste_pages cle ON c.page_wiki_cle = cle.tag AND cle.latest = 'Y'
185
%s
195
%s
Line 253... Line 263...
253
 
263
 
254
		if(array_key_exists('masque.tome', $params)) {
264
		if(array_key_exists('masque.tome', $params)) {
255
			$stack[] = 'c.tome = ' . intval($params['masque.tome']);
265
			$stack[] = 'c.tome = ' . intval($params['masque.tome']);
Line 256... Line 266...
256
		}
266
		}
257
 
267
 
-
 
268
		// ajout de la contrainte sur coste:nn ou bdtfx:nn si un composant d'URL supplémentaire
258
		// ajout de la contrainte sur num_nom si un composant d'URL supplémentaire
269
		// comportant un #id existe, cf self::requestFilterIds()
-
 
270
		$o_stack = array();
259
		// comportant un #id existe, cf self::requestFilterIds()
271
		if(array_key_exists('_ids', $params) && $params['_ids']) {
-
 
272
			if($params['_ids']['coste']) {
-
 
273
				$o_stack[] = sprintf("c.num_nom IN (%s)", implode(',', $params['_ids']['coste']));
-
 
274
			}
-
 
275
			if($params['_ids']['bdtfx']) {
260
		if(array_key_exists('_ids', $params) && $params['_ids']) {
276
				$o_stack[] = sprintf("c.flore_bdtfx_nn IN (%s)", implode(',', $params['_ids']['bdtfx']));
-
 
277
			}
Line 261... Line 278...
261
			$stack[] = sprintf("c.num_nom IN (%s)", implode(',', $params['_ids']));
278
		}
262
		}
279
		if($o_stack) $stack[] = '(' . implode(' OR ', $o_stack) . ')';
Line 269... Line 286...
269
	// à la différence que celle-ci n'est pas reformatée et s'avère donc
286
	// à la différence que celle-ci n'est pas reformatée et s'avère donc
270
	// utilisable plus aisément dans sqlAddJoins() qui peut en avoir besoin
287
	// utilisable plus aisément dans sqlAddJoins() qui peut en avoir besoin
271
	static function sqlSelectFields($params, &$unmerged) {
288
	static function sqlSelectFields($params, &$unmerged) {
272
		$champs = $params['retour.champs'];
289
		$champs = $params['retour.champs'];
273
		// champs coste_v2_00
290
		// champs coste_v2_00
274
		$c = array_intersect_key(self::$allow_champs, array_flip(explode(',', $champs)));
291
		$c = self::addSQLToFieldSynonym(explode(',', $champs));
275
		if(isset($c['*'])) {
292
		if(isset($c['*'])) {
276
			$t = array_diff_key(self::$allow_champs, array_flip(self::$special_champs));
293
			$t = array_diff_key(self::$allow_champs, array_flip(self::$special_champs));
277
		}
294
		}
278
		else {
295
		else {
279
			// just loop below
296
			// just loop below
Line 289... Line 306...
289
		}
306
		}
Line 290... Line 307...
290
 
307
 
291
		if(array_key_exists('titre', $t))
308
		if(array_key_exists('titre', $t))
Line -... Line 309...
-
 
309
			$t['titre'] = $params['txt.format'] == 'txt' ? 'c.nom_sci' : 'c.nom_sci_html';
-
 
310
 
292
			$t['titre'] = $params['txt.format'] == 'txt' ? 'c.nom_sci' : 'c.nom_sci_html';
311
		// champs obligatoires:
Line 293... Line 312...
293
 
312
		$t = array_merge($t, self::addSQLToFieldSynonym(self::$champs_obligatoires));
294
		$unmerged = $t;
313
		$unmerged = $t;
295
 
314
 
-
 
315
		// XXX: PHP-5.3
-
 
316
		$ret = array();
-
 
317
		foreach($t as $k => $v) {
296
		// XXX: PHP-5.3
318
			if(strpos($k, ':') !== FALSE) {
-
 
319
				$ret[] = "$v AS \"$k\"";
297
		$ret = array();
320
			} else {
298
		foreach($t as $k => $v) {
321
				$ret[] = "$v AS $k";
299
			$ret[] = "$v AS $k";
322
			}
Line 300... Line 323...
300
		}
323
		}
301
		return implode(',',$ret);
324
		return implode(',',$ret);
302
	}
325
	}
303
 
326
 
304
	static function sqlAddJoins($params, $champs) {
327
	static function sqlAddJoins($params, $champs) {
305
		$j = '';
328
		$j = '';
306
		// ces tests doivent correspondre aux champs générés par sqlSelectFields()
329
		// ces tests doivent correspondre aux champs générés par sqlSelectFields()
307
		// ou contraintes générées par sqlAddConstraint()
330
		// ou contraintes générées par sqlAddConstraint()
Line 308... Line 331...
308
		if(array_key_exists('masque.famille', $params) ||
331
		if(array_key_exists('masque.famille', $params) ||
309
		   array_key_exists('famille', $champs)) {
332
		   array_key_exists('famille', $champs)) {
Line -... Line 333...
-
 
333
			$j .= 'LEFT JOIN tb_eflore.bdtfx_v1_02 b ON c.flore_bdtfx_nn = b.num_nom';
-
 
334
		}
-
 
335
 
-
 
336
		return $j;
-
 
337
	}
-
 
338
 
-
 
339
	// d'un tableau de type array("coste:nn", "type_epithete")
-
 
340
	// retourne
310
			$j .= 'LEFT JOIN tb_eflore.bdtfx_v1_02 b ON c.num_nom = b.num_nom';
341
	// un tableau de type array("coste:nn" => "c.num_nom", "type_epithete" => "c.type_epithete")
311
		}
342
	// basé sur self::$allow_champs
312
 
343
	static function addSQLToFieldSynonym(Array $syno) {
313
		return $j;
344
		return array_intersect_key(self::$allow_champs, array_flip($syno));
314
	}
345
	}
Line 353... Line 384...
353
																		   'max_range' => _COSTE_TEXTE_MAX_RESULT_LIMIT)));
384
																		   'max_range' => _COSTE_TEXTE_MAX_RESULT_LIMIT)));
354
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
385
		$p['navigation.depart'] = filter_var(@$params['navigation.depart'],
355
												  FILTER_VALIDATE_INT,
386
												  FILTER_VALIDATE_INT,
356
												  array('options' => array('default' => NULL,
387
												  array('options' => array('default' => NULL,
357
																		   'min_range' => 0,
388
																		   'min_range' => 0,
358
																		   'max_range' => _COSTE_TEXTE_MAX_NUM_NOM)));
389
																		   'max_range' => _COSTE_TEXTE_MAX_COSTE_NN)));
Line 359... Line 390...
359
 
390
 
360
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
391
		// on filtre les NULL, FALSE et '', mais pas les 0, d'où le callback()
361
		// TODO: PHP-5.3
392
		// TODO: PHP-5.3
362
		$p = array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
393
		$p = array_filter($p, create_function('$a','return !in_array($a, array("",false,null),true);'));
Line 366... Line 397...
366
	}
397
	}
Line 367... Line 398...
367
 
398
 
368
	static function requestFilterIds($uri) {
399
	static function requestFilterIds($uri) {
Line 369... Line 400...
369
		if(count($uri) != 1) return NULL;
400
		if(count($uri) != 1) return NULL;
-
 
401
 
-
 
402
		// getNN* renvoient le num_nom passé comme segment d'URI:
370
 
403
		// - soit un id selon coste (num_nom dans coste_v2_00)
-
 
404
		// - soit un id selon bdtfx (num_nom dans bdtfx_v1_02)
371
		// getNN renvoie le num_nom passé comme segment d'URI
405
		// ou bien l'extrait du pattern bdtfx.nn:(#id)
Line 372... Line 406...
372
		// ou bien l'extrait du pattern bdtfx.nn:(#id)
406
		$ids_coste = array_filter(array_map(array(__CLASS__, 'getNNCoste'), explode(',', $uri[0])));
373
		$ids = array_filter(array_map(array(__CLASS__, 'getNN'), explode(',', $uri[0])));
407
		$ids_bdtfx = array_filter(array_map(array(__CLASS__, 'getNNBdtfx'), explode(',', $uri[0])));
374
 
408
 
375
		// en cas d'échec (tous les id sont invalides), bail-out
409
		// en cas d'échec (tous les id sont invalides), bail-out
376
		if(!$ids) {
410
		if(!$ids_bdtfx && !$ids_coste) {
Line -... Line 411...
-
 
411
			// http_response_code(500);
-
 
412
			throw new Exception('not supported', 500);
377
			// http_response_code(500);
413
		}
-
 
414
 
378
			throw new Exception('not supported', 500);
415
		return array(
Line 379... Line 416...
379
		}
416
			'coste' => array_slice($ids_coste, 0, intval(_COSTE_TEXTE_MAX_RESULT_LIMIT/2) ),
380
 
417
			'bdtfx' => array_slice($ids_bdtfx, 0, intval(_COSTE_TEXTE_MAX_RESULT_LIMIT/2) )
381
		return array_slice($ids, 0, _COSTE_TEXTE_MAX_RESULT_LIMIT);
418
		);
382
	}
419
	}
383
 
420
 
384
	static function aide() {
421
	static function aide() {
385
		header("Content-Type: text/plain; charset=utf-8");
-
 
-
 
422
		header("Content-Type: text/plain; charset=utf-8");
386
		return sprintf("
423
		return sprintf("
387
Service coste/textes:
424
Service coste/textes:
388
Retourne des informations (choisies) à propos d'un taxon donné (à partir de son numéro nomenclatural
425
Retourne des informations (choisies) à propos d'un taxon donné (à partir de son numéro nomenclatural
389
Retourne des informations (choisies) à propos de taxons recherchés (à partir de divers critères)
426
Retourne des informations (choisies) à propos de taxons recherchés (à partir de divers critères)
-
 
427
Les résultats sont indexés. La clef par défaut est le num_nom d'après coste (attribut \"coste:nn\")
390
 
428
Usage:
391
Usage:
429
			coste/textes/<liste-num_nom>?<params>
392
			coste/textes/<liste-num_nom>?<params>
430
* <liste-num_nom> étant une liste de numéros nomenclaturaux de taxons séparés par des virgules au format:
393
* <liste-num_nom> étant une liste de numéros nomenclaturaux de taxons bdtfx séparés par des virgules
431
 - <#id>: un numéro nomenclatural dans la base coste
394
  au format <#id> ou <bdtfx.nn:#id>
432
 - <bdtfx.nn:#id>: un numéro nomenclatural dans la base bdtfx
Line 402... Line 440...
402
					   implode(',', self::$allow_params),
440
					   implode(',', self::$allow_params),
403
					   self::$default_params['retour.champs']
441
					   self::$default_params['retour.champs']
404
		);
442
		);
405
	}
443
	}
Line 406... Line 444...
406
 
444
 
407
	static function getNN($refnn) {
445
	static function getNNCoste($refnn) {
408
		if(is_numeric($refnn)) {
446
		if(is_numeric($refnn)) {
409
			$t = intval($refnn);
447
			$t = intval($refnn);
410
			if($t >= 1 && $t < _COSTE_TEXTE_MAX_NUM_NOM) return $t;
-
 
411
			return FALSE;
448
			if($t >= 1 && $t < _COSTE_TEXTE_MAX_COSTE_NN) return $t;
-
 
449
		}
-
 
450
		return FALSE;
-
 
451
	}
-
 
452
 
412
		}
453
	static function getNNBdtfx($refnn) {
413
		if(strpos($refnn, 'bdtfx.nn:') !== 0) return FALSE;
454
		if(strpos($refnn, 'bdtfx.nn:') !== 0) return FALSE;
-
 
455
		$t = intval(str_replace('bdtfx.nn:', '', $refnn));
-
 
456
		if($t >= 1 && $t < _COSTE_TEXTE_MAX_BDTFX_NN) return $t;
414
		return intval(str_replace('bdtfx.nn:', '', $refnn));
457
		return FALSE;
415
	}
458
	}