Subversion Repositories eFlore/Projets.eflore-projets

Rev

Rev 731 | Rev 803 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
/**
* Description :
* Classe NomsVernaculaires.php fournit une liste de noms vernaculaires et leur liaison à la bdtfx
* Le but étant de fournir un ensemble minimal d'information comprenant :
* un identifiant (numérique ou alphanumérique sous forme de ChatMot si possible), un nom, une langue et
* une relation avec un taxon de la bdtfx.
* Si l'url finit par /noms-vernaculaires on retourne une liste de noms (seulement les 100 premières par défaut).
* L'url peut contenir des paramètres optionnels passés après le ? : /observations?param1=val1&param2=val2&...
*
* Les paramètres de requête disponibles sont : masque, masque.code, masque.nom, masque.region , recherche,
* distinct, retour.format, navigation.depart et navigation.limite.
*
* Encodage en entrée : utf8
* Encodage en sortie : utf8
* @package framework-v3
* @author Delphine Cauquil <delphine@tela-botanica.org>
* @author Jennifer Dhé <jennifer.dhe@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version 1.0
* @copyright 1999-${year} Tela Botanica (accueil@tela-botanica.org)
*/

// Un caractère de concaténation entre le projet et le service.
// Ce caractère ne doit pas faire partie d'aucun des noms de service ou projet
define('RES_VAL_SEP', '@');

class NomsVernaculaires extends Commun {
        protected $champ_infos = array(
                'taxon' => array('service' => 'taxons', 'ressource' => 'nt:', 'projet' => 'bdtfx', 'nom' => 'nom_sci',
                                                 // utilisés par ajouterChampsOntologieLigneResultat()
                                                 'intitule' => 'taxon.code', // intitulé du champ tel qu'il sera renvoyé en JSON
                                                 'bdd_champ' => 'num_taxon'), // intitulé du champ tel qu'il est présent dans l'enregistrement MySQL
                'conseil_emploi' => array('service' => 'ontologies', 'ressource' => 'numStatut:', 'projet' => 'nvjfl', 'nom' => 'nom',
                                                                  'intitule' => 'conseil_emploi', 'bdd_champ' => 'num_statut'),
                'genre' => array('service' => 'ontologies', 'ressource' => 'genreNombre:', 'projet' => 'nvjfl', 'nom' => 'nom',
                                                 'intitule' => 'genre', 'bdd_champ' => 'num_genre'));

        protected $service = 'noms-vernaculaires';

        /**
         * Permet de stocker la requete formulée : /noms-vernaculaires | /noms-vernaculaires/#id |
         *  /noms-vernaculaires/#id/champ | /noms-vernaculaires/#id/relations
         * Est remplit au cours de l'analyse des ressources (traiterRessources()), par défaut, a la valeur du service.
         * Est utilisée principalement pr déterminer le format du tableau à retourner.        */
        protected $format_reponse = 'noms-vernaculaires';

        /** Variables constituant les parametres de la requete SQL (champ, condition, limit) remplie
         * selon ressources et paramètres */
        protected $requete_champ = array(' * ');
        protected $requete_condition = '';
        protected $limite_requete = array(
                'depart' => 0,
                'limite' => 100
        );
        
        protected $champ_tri = 'code_langue';
        protected $direction_tri = 'asc';
        
        /**
         * Indique les champs supplémentaires à retourner
         *  - conseil_emploi = conseil d'emploi du nom vernaculaire
         *  - genre = genre et nombre du nom
         *  - taxon = nom retenu associé à ce nom
         */
        protected $champs_supp = array();

        /**
         * Precise la contenance plus ou moins précise du tableau à retourner :
         *  - min = les données présentes dans la table
         *  - max = les données de la table + les informations complémentaires (pour les identifiants et les codes)
         *  - oss = la liste des nom_sci (uniquement pour noms et taxons) */
        protected $retour_format = 'max';
        /** Valeur du paramètre de requete recherche :
         *  - stricte : le masque est passé tel quel à l'opérateur LIKE.
         *  - etendue : ajout automatique du signe % à la place des espaces et en fin de masque avec utilisation de LIKE.
         *  - floue : recherche tolérante vis-à-vis d'approximations ou d'erreurs (fautes d'orthographe par exemple) */
        protected $recherche;
        
        /** Permet de stocker le tableau de résultat (non encodé en json) */
        protected $table_retour = array();
        /** Stocke le nombre total de résultats de la requete principale. Est calculée lors de l'assemblage de la requete */
        protected $total_resultat;
        
        private $config;
        
        public function __construct($config) {
                $this->config = is_null($config) ? Config::get('NomsVernaculaires') : $config;
        }

        //+------------------------------------------------------------------------------------------------------+
        // créer une condition en fonction du paramétre
        public function traiterParametres() {
                if (isset($this->parametres) && !empty($this->parametres)) {

                        if (isset($this->parametres['recherche']) && $this->parametres['recherche'] != '') {
                                $this->recherche = $this->parametres['recherche'];
                        }
                        foreach ($this->parametres as $param => $valeur) {
                                switch ($param) {
                                        case 'masque' :
                                                $this->ajouterFiltreMasque('nom_vernaculaire', $valeur);
                                                break;
                                        case 'masque.nt' :
                                                $this->ajouterFiltreMasque('num_taxon', $valeur);
                                                break;
                                        case 'masque.nv' :
                                                $this->ajouterFiltreMasque('nom_vernaculaire', $valeur);
                                                break;
                                        case 'masque.lg' :
                                                $this->ajouterFiltreMasque('code_langue', $valeur);
                                                break;
                                        case 'masque.cce' :
                                                $this->ajouterFiltreMasque('num_statut', $valeur);
                                                break;
                                        case 'retour.format' :
                                                $this->retour_format = $valeur;
                                                break;
                                        case 'navigation.depart' :
                                                $this->limite_requete['depart'] = $valeur;
                                                break;
                                        case 'navigation.limite' :
                                                $this->limite_requete['limite'] = $valeur;
                                                break;
                                        case 'retour.champs' :
                                                $this->champs_supp = explode(',',$valeur);
                                        break;
                                        case 'recherche' :
                                                break;
                                        case 'version.projet' :
                                                break;
                                        default :
                                                $p = 'Erreur dans les paramètres de recherche de votre requête : '.
                                                        '</br> Le paramètre " '.$param.' " n\'existe pas.';
                                                        $this->renvoyerErreur(RestServeur::HTTP_CODE_MAUVAISE_REQUETE, $p);
                                }
                        }
                }
        }

        public function ajouterFiltreMasque($nom_champ, $valeur) {
                if ($nom_champ == 'num_taxon') { // si il s'agit d'un chiffre
                        $this->requete_condition[] = $nom_champ.' = '.$this->getBdd()->proteger($valeur);
                } else {
                        if ($this->recherche == 'floue') {
                                $this->requete_condition[] = '(SOUNDEX('.$nom_champ.') = SOUNDEX(\''.$valeur.'\')'
                                        .' OR SOUNDEX(REVERSE('.$nom_champ.')) = SOUNDEX(REVERSE(\''.$valeur.'\'))) ';
                        } else {
                                if ($this->recherche == 'etendue') {
                                        $valeur = '%'.str_replace(' ','% ', $valeur);
                                        $valeur .= '%';
                                }
                                $this->requete_condition[] = $nom_champ.' LIKE '.$this->getBdd()->proteger($valeur);
                        }
                }
        }

        //+------------------------------------------------------------------------------------------------------+
        // en fonction de la présence des ressources modifie requete_champ et requete_condition
        public function traiterRessources() {
                if (isset($this->ressources) && !empty($this->ressources)) {
                        if (isset($this->ressources[0]) && !empty($this->ressources[0])) {
                                $this->traiterRessourceId(); // ajoute condition id=#valeur
                                if (isset($this->ressources[1]) && !empty($this->ressources[1])) {
                                        $this->traiterRessourceChamp(); //modifie requete_champ ou requete_condition
                                }
                        }
                } else { //rajoute distinct pour ne pas avoir plusieurs fois le même nom
                        $this->requete_champ = array('distinct(id)', 'nom_vernaculaire ');
                }
        }

        //requete : /noms-vernaculaires/#id (ex : /noms-vernaculaires/7)
        public function traiterRessourceId() {
                if (is_numeric($this->ressources[0])) {
                        $this->requete_condition[] = ' id = '.$this->getBdd()->proteger($this->ressources[0]);
                        $this->format_reponse .= '/id';
                } elseif ($this->ressources[0] == 'attributions') {
                        $this->format_reponse .= '/attributions';
                } else {
                        $r = 'Erreur dans les ressources de votre requête : </br> La ressource " '.$this->ressources[0].
                                ' " n\'existe pas.';
                        $this->renvoyerErreur(RestServeur::HTTP_CODE_MAUVAISE_REQUETE, $r);
                }
        }


        public function traiterRessourceChamp() {
                $this->format_reponse .= '/champ';
                $this->analyserChamp();
        }

        public function analyserChamp() {
                $this->requete_champ = array();
                $this->recupererTableConfig('champs_possibles');// s'il y a plusieurs champs correspondant au champ demandé ils sont séparé par des |
                $champs = explode(' ', $this->ressources[1]);
                foreach ($champs as $champ) {
                        preg_match('/^([^.]+)(\.([^.]+))?$/', $champ, $match);
                        if (isset($this->champs_possibles[$match[1]])) {
                                $this->requete_champ[] = str_replace('|', ', ', $this->champs_possibles[$match[1]]);
                        } elseif (isset($this->champs_possibles[$match[0]])) {
                                $this->requete_champ[] = str_replace('|', ', ', $this->champs_possibles[$match[0]]);
                        } else {
                                $champs_possibles = implode('</li><li>', array_keys($this->champs_possibles));
                                $c = 'Erreur dans votre requête : </br> Le champ "'.$champ_possibles.'" n\'existe pas. '.
                                        'Les champs disponibles sont : <li>'.$champs_possibles.'</li> et leurs déclinaisons (ex. ".code").';
                                $this->renvoyerErreur(RestServeur::HTTP_CODE_MAUVAISE_REQUETE, $c);
                        }
                }
        }

        //+------------------------------------------------------------------------------------------------------+
        public function assemblerLaRequete() {
                $requete = ' SELECT '.$this->formerRequeteChamp().
                                        ', CASE num_statut WHEN "" THEN 1 ELSE 0 END AS is_null '.
                                        ' FROM '.$this->table
                                        .$this->formerRequeteCondition().
                                        ' ORDER BY is_null ASC, num_statut ASC '
                                        .$this->formerRequeteLimite();
                return $requete;
        }

        public  function formerRequeteChamp() {
                if (in_array('*', $this->requete_champ)) {
                        $champ = ' * ';
                } else {
                        $champ = implode(', ', $this->requete_champ);
                }
                return $champ;
        }

        public  function formerRequeteCondition() {
                $condition = '';
                if ($this->requete_condition != null) {
                        $condition = ' WHERE '.implode(' AND ', $this->requete_condition);
                }
                return $condition;
        }

        //ajout d'une limite seulement pour les listes (pas plus de 100 resultats retournés pr les requetes
        // suivantes : /noms-vernaculaires et /noms-vernaculaires/#id/relations)
        public function formerRequeteLimite() {
                if (in_array($this->format_reponse , array($this->service.'/id', $this->service.'/id/champs'))) {
                        $this->requete_limite = '';
                } elseif (($depart = $this->limite_requete['depart']) > ($this->total_resultat = $this->recupererTotalResultat())) {
                        $this->limite_requete['depart'] =
                                (($this->total_resultat - $this->limite_requete['limite']) < 0) ? 0 : ($this->total_resultat - $this->limite_requete['limite']);
                        $this->requete_limite = ' LIMIT '.$this->limite_requete['depart'].', '.$this->limite_requete['limite'];
                } else {
                        $this->requete_limite = ' LIMIT '.$this->limite_requete['depart'].', '.$this->limite_requete['limite'];
                }
                return $this->requete_limite;
        }

        //on récupère le nombre total de résultats de la requete (ex : le nombre d'id contenu dans la liste /noms-vernaculaires)
        public function recupererTotalResultat() {
                $distinct = ($this->format_reponse == 'noms-vernaculaires/attributions') ? 'id' : 'distinct(id)';
                $requete = 'SELECT count('.$distinct.') as nombre FROM '
                        .$this->table
                        .$this->formerRequeteCondition();
                $res = $this->getBdd()->recuperer($requete);

                if ($res) {
                        $total = $res['nombre'];
                } else {
                        $t = 'Fonction recupererTotalResultat() : <br/>Données introuvables dans la base '.$requete;
                        $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $t);
                }
                return $total;
        }

        //+------------------------------------------------------------------------------------------------------+
        // determine en fct du service appelé (/noms-vernaculaires | /noms-vernaculaires/#id | /noms-vernaculaires/#id/champ |
        // /noms-vernaculaires/#id/relations) le format du tableau à retourner.
        public function retournerResultatFormate($resultat) {
                $this->recupererTableConfig('correspondance_champs');
                switch ($this->format_reponse) {
                        case 'noms-vernaculaires'                               : 
                                $reponse = ($this->retour_format == 'oss') ? $this->formaterEnOss($resultat) : $this->formaterNomsVernaculaires($resultat);                     break;
                        case 'noms-vernaculaires/attributions'  : $reponse = $this->formaterNomsVernaculairesAttributions($resultat);   break;
                        case 'noms-vernaculaires/id'                    : $reponse = $this->formaterNomsVernaculairesId($resultat);                     break;
                        case 'noms-vernaculaires/id/champ'              : $reponse = $this->formaterNomsVernaculairesIdChamp($resultat);        break;
                        default                                                                 :                                                                                                                                       break;
                }
                return $reponse;
        }
        
        public function ajouterJsonEnTeteNV() {
                $table_retour_json['masque'] = $this->recupererMasque();
                $table_retour_json['depart'] = $this->limite_requete['depart'];
                $table_retour_json['limite'] = $this->limite_requete['limite'];
                $table_retour_json['total']  = $this->total_resultat;
                $url = $this->formulerUrl($this->total_resultat, '/noms-vernaculaires');
                if (isset($url['precedent']) && $url['precedent'] != '') {
                        $table_retour_json['href.precedent'] = $url['precedent'];
                }
                if (isset($url['suivant']) && $url['suivant']   != '') {
                        $table_retour_json['href.suivant']   = $url['suivant'];
                }
                return $table_retour_json;
        }
        
        public function ajouterJsonResultatNV($resultat) {
                foreach ($resultat as $tab) {
                        foreach ($tab as $key => $valeur) {
                                if ($valeur != '') {
                                        switch ($key) {
                                                case 'id'                               : $num = $valeur;                                                               break;
                                                case 'nom_vernaculaire' : $this->table_retour['nom'] = $valeur;                 break;
                                                default                                 :                                                                                               break;
                                        }
                                }
                        }
                    if ($this->retour_format == 'max') $this->table_retour['href'] = $this->ajouterHref('noms-vernaculaires', $num);
                        $resultat_json[$num] = $this->table_retour;
                        $this->table_retour = array();
                }
                return  $resultat_json;
        }

        
        public function formaterNomsVernaculaires($resultat) {
                $table_retour_json['entete'] = $this->ajouterJsonEnTeteNV();
                $resultat = $this->hierarchiserResultat($resultat);
                $table_retour_json['resultat'] = $this->ajouterJsonResultatNV($resultat);
                return $table_retour_json;
        }
        
        public function hierarchiserResultat($resultat) {
                //tri recherche floue
                if (isset($this->parametres['masque.nv'])) {
                        $resultat = $this->trierRechercheFloue($this->parametres['masque.nv'], $resultat, 'nom_vernaculaire');
                }
                if (isset($this->parametres['masque'])) {
                        $resultat = $this->trierRechercheFloue($this->parametres['masque'], $resultat, 'nom_vernaculaire');
                }
                return $resultat;
        }
        
        public function recupererMasque() {
                $tab_masque = array();
                foreach ($this->parametres as $param=>$valeur) {
                        if (strstr($param, 'masque') != false) {
                                $tab_masque[] = $param.'='.$valeur;
                        }
                }
                $masque = implode('&', $tab_masque);
                return $masque;
        }
        
        public function formaterEnOss($resultat) {
                $table_nom = array();
                $oss = '';
                foreach ($resultat as $tab) {
                        if (isset($tab['nom_vernaculaire']) ) {
                                if (!in_array($tab['nom_vernaculaire'], $table_nom)) {
                                        $table_nom[] = $tab['nom_vernaculaire'];
                                        $oss [] = $tab['nom_vernaculaire'];
                                }
                        }
                }
                if (isset($this->masque)) $masque = implode('&', $this->masque);
                else $masque = 'Pas de masque';
                $table_retour_oss = array($masque, $oss);
                return $table_retour_oss;
        }
        
        public function formaterNomsVernaculairesAttributions($resultat) {
                //on remplie la table $table_retour_json['entete']
                $table_retour_json['entete']['masque'] = $this->recupererMasque();
                $table_retour_json['entete']['depart'] = $this->limite_requete['depart'];
                $table_retour_json['entete']['limite'] = $this->limite_requete['limite'];
                $table_retour_json['entete']['total']  = $this->total_resultat;
                $url = $this->formulerUrl($this->total_resultat, '/noms-vernaculaires/attributions');
                if (!empty($url['precedent'])) {
                        $table_retour_json['entete']['href.precedent'] = $url['precedent'];
                }
                if (!empty($url['suivant'])) {
                        $table_retour_json['entete']['href.suivant']   = $url['suivant'];
                }
                foreach ($resultat as &$tab) {                  
                        $nnv = $tab['num_nom_vernaculaire'];
                        $resultat_json[$nnv]['id'] = $tab['id'];
                        $resultat_json[$nnv]['nom_vernaculaire'] = $tab['nom_vernaculaire'];
                        $resultat_json[$nnv]['code_langue'] = $tab['code_langue'];
                        $resultat_json[$nnv]['taxon.code'] = 'bdtfx.nt:'.$tab['num_taxon'];
                        if ($this->retour_format == 'max') {
                                $this->taxons[] = $tab['num_taxon']; // utilisé pour chercher les noms latins plus bas
                                if($this->champs_supp) {
                                        //$resultat_json[$nnv] = $this->ajouterChampsOntologieLigneResultat($tab);
                                        // simple initialisation par copie de la référence de l'original
                                        $resultat_json[$nnv] = &$tab;
                                }
                                else {
                                        $resultat_json[$nnv]['num_taxon'] = $tab['num_taxon'];
                                        $resultat_json[$nnv]['nom_retenu.code'] = $tab['num_taxon'];
                                        $resultat_json[$nnv]['taxon'] = $tab['num_taxon'];
                                        $resultat_json[$nnv]['href'] = $this->ajouterHref('noms-vernaculaires', $tab['id']);
                                }
                        }
                }

                // dans ce cas (particulier?) nous n'avons pour l'heure initialisé qu'une référence
                // vers le tableau de valeurs original
                if ($this->retour_format == 'max' && $this->champs_supp) {
                        // récupérons désormais les ontologies
                        $this->ajouterChampsOntologieLigneTousResultats($resultat_json);
                }

                if ($this->retour_format == 'max') {
                        // On est obligé de faire un deuxième boucle pour demander tous les taxons présents en une 
                        // fois et les attribuer aux noms car c'est beaucoup plus rapide
                        $noms_sci = $this->recupererNomTaxons();
                        foreach ($resultat_json as $num_nom => &$tab) {
                                $tab = $this->ajouterTaxonsAttributionsLigneResultat($tab, $noms_sci);
                                if($tab == null) {
                                        unset($resultat_json[$num_nom]);
                                }
                        }
                }
                
                $table_retour_json['resultat'] = $resultat_json;
                return $table_retour_json;
        }
        
        /**
         * Ajoute les champs d'ontologie supplémentaires si necéssaire
         * en faisant appels aux web services associés
         * @param array in/out $resultats: tous les résultats
         */
        public function ajouterChampsOntologieLigneTousResultats(&$resultats) {
                foreach($this->champ_infos as $cle => $champs_supplementaires) {
                        if(!in_array($cle, $this->champs_supp)) continue;

                        // TODO: n'activer QUE lorsque le webservice ontologie Nvjfl supportera le multi-critère
                        // cf: services/modules/0.1/commun/Ontologies.php
                        if($cle == 'conseil_emploi') {
                                // la factorisation des toutes les valeurs recherchées (pour tous les
                                // résultats, peut [potentiellement] être effectuée ci-dessous)
                                $ontologieParamPending = static::NvjflOntologieIndex($resultats, $champs_supplementaires);
                                $this->NvjflOntologieExpand($ontologieParamPending);
                                self::NvjflOntologieCombine($resultats, $champs_supplementaires);
                                continue;
                        }

                        // extrait, depuis un élément de $champ_infos:
                        // $service, $ressource, $projet, $nom, $intitule, $bdd_champ
                        extract($champs_supplementaires);

                        foreach ($resultats as &$tab) {
                                $valeur_recherche = $tab[$bdd_champ];
                                if(!trim($valeur_recherche)) continue;

                                $url = $this->ajouterHrefAutreProjet($service, $ressource, $valeur_recherche, $projet);
                                $tab[$intitule] = $this->chercherSignificationCode($url, $nom);
                        }
                }
        }

        /* Récupère les valeurs recherchées pour une liste de résultats, (plus ou moins)
           spécifiquement au service d'Ontologies de NVJFL.
           Aggrège les valeurs dans le tableau retourné.
           Une référence vers l'index du tableau (NULL pour l'instant) est laissée dans
           un élément du résultat. */
        static function NvjflOntologieIndex(&$resultats, $champs_infos) {
                $ontologieParamPending = Array();
                extract($champs_infos);
                foreach($resultats as &$resultat) {
                        $valeur_recherche = $resultat[$bdd_champ];
                        if(!trim($valeur_recherche)) continue;
                        $ontologieParamPending[$ressource . $valeur_recherche] = NULL;
                        // placeholder pour le résultat
                        $resultat['_result_ontologies' . RES_VAL_SEP . 'nvjfl'] =
                                &$ontologieParamPending[$ressource . $valeur_recherche];
                }
                return $ontologieParamPending;
        }

        // TODO: switch to static si il peut en être de même pour ajouterHrefAutreProjet()
        /* À partir d'un aggrégat des critère de requêtes d'ontologies, spécifiques à NVJFL,
           créé une URL multi-critère.
           Celle-ci, dans ce cas précis, n'est que la concaténation, par des virgules,
           des couples <ressource - valeur recherchée>.
           L'URL est appelée et la valeur correspondante, dans $criteres_requete, remplacée.

           Note: dans le cadre du tryptique index/expand/combine pour lequel cette fonction existe,
           la valeur est référencée par une élément d'une ou plusieurs lignes de $resultat correspondantes.
           Celle(s)-ci sera[ont] donc changée(s) dans la foulée. */
        public function NvjflOntologieExpand(&$criteres_requete) {
                // équivalent spécifique de ajouterHrefAutreProjet()
                $valeurs_requises = implode(',', array_keys($criteres_requete));
                $url = Config::get('url_service_base').'nvjfl/ontologies/'.$valeurs_requises;
                $val = $this->consulterHref($url);
                // modifie par conséquent $resultats[X]['_result_ontologies' . RES_VAL_SEP . 'nvjfl']
                // dont la référence pointe toujours sur $v
                foreach($val as $k => $v) $criteres_requete[$k] = $val->$k;
        }

        /* Fonction finale du tryptique: réordonne les valeurs obtenues auprès du web-service
           NVJFL en adéquation avec les champs attendus en sortie. Pour ce faire, $champs_infos
           est nécessaire. D'autre part, l'index spécifique '_result_ontologies' . RES_VAL_SEP . 'nvjfl'
           est supprimé après avoir été correctement copié. */
        static function NvjflOntologieCombine(&$resultats, $champs_infos) {
                extract($champs_infos);
                foreach($resultats as &$resultat) {
                        if(!isset($resultat['_result_ontologies' . RES_VAL_SEP . 'nvjfl'])) continue;
                        // equivalent de chercher signification
                        $resultat[$intitule] = $resultat['_result_ontologies' . RES_VAL_SEP . 'nvjfl']->$nom;
                        unset($resultat['_result_ontologies' . RES_VAL_SEP . 'nvjfl']);
                }
        }

        /**
         * Ajoute les champs d'ontologie supplémentaires si necéssaire
         * en faisant appels aux web services associés
         * @param array $ligne_resultat
         * 
         * @return array la ligne modifiée
         */
        public function ajouterChampsOntologieLigneResultat($ligne_resultat) {
                foreach($this->champ_infos as $cle => $champs_supplementaires) {
                        if(!in_array($cle, $this->champs_supp)) continue;
                        // extrait, depuis un élément de $champ_infos:
                        // $service, $ressource, $projet, $nom, $intitule, $bdd_champ
                        extract($champs_supplementaires);
                        $valeur_recherche = $ligne_resultat[$bdd_champ];
                        if(!trim($valeur_recherche)) continue;
                        $url = $this->ajouterHrefAutreProjet($service, $ressource, $valeur_recherche, $projet);
                        $ligne_resultat[$intitule] = $this->chercherSignificationCode($url, $nom);
                }
                return $ligne_resultat;
        }
        
        /**
         * Fonction qui ajoute les attributions à une ligne de résultats
         *
         * @param array $ligne_tableau_resultat
         * @param array $nom_sci
         */
        public function ajouterTaxonsAttributionsLigneResultat(&$ligne_tableau_resultat, &$noms_sci) {
                if (isset($noms_sci[$ligne_tableau_resultat['num_taxon']])) {
                        $ligne_tableau_resultat['nom_retenu.code'] = $noms_sci[$ligne_tableau_resultat['num_taxon']]['id'];
                        $ligne_tableau_resultat['taxon'] = $noms_sci[$ligne_tableau_resultat['num_taxon']]['nom_sci'];
                } else {
                        $ligne_tableau_resultat = null;
                }
                return $ligne_tableau_resultat;
        }
        
        private function trierLigneTableau($a, $b) {
                $retour = 0;
                
                if ($a[$this->champ_tri] == $b[$this->champ_tri]) {
                        $retour = 0;
                }
                                
                if($this->champ_tri == 'code_langue') {
                        if ($a[$this->champ_tri] == 'fra' && $b[$this->champ_tri] != 'fra') {
                                $retour = ($this->direction_tri == 'asc') ? -1 : 1;
                        } else if ($a[$this->champ_tri] != 'fra' && $b[$this->champ_tri] == 'fra') {
                                $retour = ($this->direction_tri == 'asc') ? 1 : -1;
                        } else {
                                $retour = $this->comparerChaineSelonDirectionTri($a[$this->champ_tri], $b[$this->champ_tri]);
                        }
                } else {
                        $retour = $this->comparerChaineSelonDirectionTri($a[$this->champ_tri], $b[$this->champ_tri]);
                }
                return $retour;
        }
        
        private function comparerChaineSelonDirectionTri($a, $b) {
                if($this->direction_tri == 'asc') {
                        return ($a < $b) ? -1 : 1;
                } else {
                        return ($a > $b) ? -1 : 1;
                }
        }
        
        // formatage de la reponse /id ss la forme
        // id, nom_vernaculaire, attributions
        // langue
        // num_nom (correspond à un taxon bdtfx)
        public function formaterNomsVernaculairesId($resultat) {
                foreach ($resultat as $taxon) { // pour chaque attribution à un taxon bdtfx
                        // on crée les variables qui serviront de clés et on les enléves du tableau
                        $num_nom = $taxon['num_nom_vernaculaire']; // unique pour un trinôme id, langue, taxon
                        unset($taxon['num_nom_vernaculaire']);
                        $langue = $taxon['code_langue'];
                        unset($taxon['code_langue']);

                        foreach ($this->correspondance_champs as $key => $correspondance) { // ordonne les infos pour affichage
                                if (isset($taxon[$key]) && $taxon[$key] != "") {
                                        $this->afficherDonnees($correspondance, $taxon[$key], $langue, $num_nom);
                                }
                        }
                        foreach ($taxon as $key => $valeur) { // rajoute les champs non prévus dans l'api
                                if (!isset($this->correspondance_champs[$key]) && $valeur != "") {
                                        $this->afficherDonnees($key, $valeur, $langue, $num_nom);
                                }
                        }
                        if ($this->retour_format == 'max') $this->chargerBiblio($num_nom, $langue);
                }
                if ($this->retour_format == 'max') $this->afficherTaxons(); // va chercher les noms de tous les taxons
                unset($this->table_retour['href']);
                return $this->table_retour;
        }

        public function afficherDonnees($champ, $valeur, $langue = '', $num_nom = '') {
                if ($champ == 'id' || $champ == 'nom_vernaculaire') {
                        $this->table_retour[$champ] = $valeur;
                } elseif (preg_match('/^(.*)\.code$/', $champ, $match)) {
                                switch ($match[1]) {
                                        case 'taxon'    : if ($this->retour_format == 'max') {$this->taxons[$num_nom] = $valeur;}
                                                $this->afficherPointCode($match[1], $langue, $num_nom, $valeur);        break;
                                        case 'langue'   : //$this->afficherPointCode($match[1], 'iso-639-3', 'langues', $valeur);
                                                break;
                                        case 'genre'    : $this->afficherPointCode($match[1], $langue, $num_nom, $valeur);      break;
                                        case 'conseil_emploi'   : $this->afficherPointCode($match[1], $langue, $num_nom, $valeur);      break;
                                        default : break;
                                }

                } elseif ($langue != '') {
                        $this->table_retour['attributions'][$langue][$num_nom][$champ] = $valeur;
                } else {
                        $this->table_retour[$champ] = $valeur;
                }
        }

        public function afficherPointCode($nomChamp, $langue, $num_nom, $valeur) {
                if (isset($this->champ_infos[$nomChamp])) {
                        extract($this->champ_infos[$nomChamp]);
                }

                if ($this->retour_format == 'max') {
                        $url = $this->ajouterHrefAutreProjet($service, $ressource, $valeur, $projet);
                        if ($service == 'taxons') {
                                $code_valeur = '';
                                $this->table_retour['attributions'][$langue][$num_nom]['nom_retenu.code'] = $code_valeur;
                        } else {
                                $code_valeur = $this->chercherSignificationCode($url, $nom);
                        }
                        if ($projet != '') $projet .= '.';
                        $this->table_retour['attributions'][$langue][$num_nom][$nomChamp] = $code_valeur;
                        $this->table_retour['attributions'][$langue][$num_nom][$nomChamp.'.code'] = $projet.$ressource.$valeur;
                        $this->table_retour['attributions'][$langue][$num_nom][$nomChamp.'.href'] = $url;
                } else {
                        if ($projet != '') $projet .= '.';
                        $this->table_retour['attributions'][$langue][$num_nom][$nomChamp.'.code'] = $projet.$ressource.$valeur;
                }
        }

        public function chercherSignificationCode($url, $nom) {
                if (isset($this->signification_code[$url])) {
                        $valeur = $this->signification_code[$url];
                } else {
                        $res = $this->consulterHref($url);
                        $valeur = $res->$nom;
                        $this->signification_code[$url] = $valeur;
                }
                return $valeur;
        }

        public function afficherTaxons() {
                $resultat = $this->recupererNomTaxons();
                foreach ($this->table_retour['attributions'] as $code_langue=>$langue) {
                        foreach ($langue as $num_nom=>$taxon) {
                                $num_tax = ltrim($taxon['taxon.code'], 'bdtfx.nt:');
                                if (isset($resultat[$num_tax])) {
                                        $this->table_retour['attributions'][$code_langue][$num_nom]['nom_retenu.code'] = $resultat[$num_tax]['id'];
                                        $this->table_retour['attributions'][$code_langue][$num_nom]['taxon'] = $resultat[$num_tax]['nom_sci'];
                                }
                        }
                }
        }
        
        public function recupererNomTaxons() {
                $taxons = array_unique($this->taxons);
                $url = Config::get('url_service_base').'bdtfx/taxons?navigation.limite=500&ns.structure=au&masque.nt='.implode(',', $taxons);
                $res = $this->consulterHref($url);
                foreach ($res->resultat as $id=>$taxon) {
                        $resultat[$taxon->num_taxonomique]['id'] = 'bdtfx.nn:'.$id;
                        $resultat[$taxon->num_taxonomique]['nom_sci'] = $taxon->nom_sci_complet;
                }
                return $resultat;
        }

        public function formaterNomsVernaculairesIdChamp($resultat) {
                $this->table_retour['id'] = $this->ressources[0];
                $champs = explode(' ', $this->ressources[1]);
                if (in_array('attributions', $champs) != false) {
                        $this->formaterNomsVernaculairesId($resultat);
                        unset($this->table_retour['nom_vernaculaire']);
                } else {
                        $champ_attributions = array('num_taxon', 'zone_usage', 'num_statut', 'num_genre', 'notes');
                        foreach ($resultat as $taxon) {
                                foreach ($taxon as $key=>$valeur) {
                                        if ($key == 'code_langue' && in_array('langue', $champs) != false) {
                                                $this->table_retour['attributions']['langue'][] = $valeur;
                                        } elseif (in_array($key, $champ_attributions) != false) {
                                                $this->afficherPoint($this->correspondance_champs[$key] , $valeur, $taxon['code_langue'], $taxon['num_nom_vernaculaire']);
                                        } elseif (in_array($key, $champs) != false) {
                                                $this->table_retour[$key] = $valeur;
                                        }
                                }
                                if (in_array('biblio', $champs) != false) $this->chargerBiblio($taxon['num_nom_vernaculaire'], $taxon['code_langue']);
                        }
                        if (in_array('biblio', $champs) != false && array_search('biblio.num_ref', $this->table_retour) != false) $this->table_retour['biblio'] = null;
                }
                return $this->table_retour;
        }

        public function afficherPoint($champ, $valeur, $langue, $num_nom) {
                preg_match('/^(.*)\.code$/', $champ, $match);
                $champ = $match[1];
                if (isset($this->champ_infos[$champ])) {
                        extract($this->champ_infos[$champ]);
                        $url = $this->ajouterHrefAutreProjet($service, $ressource, $valeur, $projet);
                        $projet .= '.';
                }

                $champs = explode(' ', $this->ressources[1]);
                if (in_array($champ.'.*', $champs) !== false && isset($projet)) {
                        $this->table_retour['attributions'][$langue][$num_nom][$champ.'.code'] = $projet.$ressource.$valeur;
                        $this->table_retour['attributions'][$langue][$num_nom][$champ.'.href'] = $url;
                }
                if (in_array($champ.'.code', $champs) !== false && isset($projet)) {
                        $this->table_retour['attributions'][$langue][$num_nom][$champ.'.code'] = $projet.$ressource.$valeur;
                }
                if (in_array($champ.'.href', $champs) !== false && isset($projet)) {
                        $this->table_retour['attributions'][$langue][$num_nom][$champ.'.href'] = $url;
                }
                if (in_array($champ, $champs) !== false) {
                        if (isset($url)) {
                                $this->table_retour['attributions'][$langue][$num_nom][$champ] = $this->chercherSignificationCode($url, $nom);
                        } else {
                                $this->table_retour['attributions'][$langue][$champ] = $valeur;
                        }
                }
        }

        public function afficherLangue($nomChamp, $projet, $service, $valeur, $ressource = '', $nom = 'nom') {
                if ($this->retour_format == 'max') {
                                $this->table_retour['attributions'][$nomChamp] = $nom;
                                $this->table_retour['attributions'][$nomChamp.'.code'] = $projet.$ressource.$valeur;
                                $this->table_retour['attributions'][$nomChamp.'.href'] = $url;
                } else {
                        $this->table_retour['attributions'][$nomChamp.'.code'] = $projet.$ressource.$valeur;
                }
        }

        public function chargerBiblio($num_nom, $langue) {
                list($table, $version) = explode('_v',$this->table);
                $requete = "SELECT b.*, lb.notes FROM nvjfl_lien_biblio_v$version lb, nvjfl_biblio_v$version b ".
                                        "WHERE b.num_ref = lb.num_ref AND lb.num_nom = '$num_nom' ;";
                $resultat = $this->getBdd()->recupererTous($requete);

                 if ($resultat == '') { //cas ou la requete comporte des erreurs
                        $r = 'La requête SQL formée comporte une erreur !!';
                        $this->renvoyerErreur(RestServeur::HTTP_CODE_RESSOURCE_INTROUVABLE, $r);
                        Debug::printr($requete);
                 } elseif ($resultat) {
                        foreach ($resultat as $res) {
                                foreach ($res as $cle => $valeur) {
                                        if ($valeur !== "") {
                                                $this->table_retour['attributions'][$langue][$num_nom]['biblio.'.$cle] = $valeur;
                                        }
                            }
                        }
                }
        }

}
?>