Subversion Repositories eFlore/Applications.coel

Rev

Rev 1868 | Blame | Compare with Previous | Last modification | View Log | RSS feed

<?php
/**
 * Service fournissant des informations sur les collections et les structures répondant aux critères de recherche
 * fournis en paramètre.
 *
 * @author Mathias Chouet
 * @author Raphaël Droz <raphael@tela-botanica.org>
 * @author Jean-Pascal MILCENT <jpm@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>
 * @copyright 2009, 2013 Tela-Botanica
 */
class CoelRecherche extends Coel {
        protected $fromClause = array();
        protected $whereClause = array();
        protected $joinClause = array();

        /**
         * Méthode principale appelée avec une requête de type GET.
         * Elle sert d'aiguilleur pour appeller la méthode correspondant au type de recherche passé en paramêtre.
         */
        public function getElement($param = array()) {
                // Initialisation des variables
                $info = array();
                // Nous recherchons le type de requête demandé
                $type = $param[0];
                $methode = 'getElement'.$type;

                if (method_exists($this, $methode)) {
                        array_shift($param);
                        $info = $this->$methode($param);
                } else {
                        $this->messages[] = "Le type de recherche demandé '$type' n'est pas disponible.";
                }
                
                // Envoie sur la sortie standard
                if($this->formatRetour == 'text/plain') {
                        $this->envoyer($info, 'text/plain', 'utf-8', false);
                        exit;
                }
                $this->envoyer($info);
        }

        /** @deprecated retro-compatibilité */
        public function getElementParDefaut($param) {
                return $this->getElementCollections($param);
        }
        /** @deprecated retro-compatibilité */
        public function getElementNombre($param) {
                return $this->getElementNombreCollections($param);
        }

        /**
         * Retourne les collections correspondant aux critères de recherche
         */
        public function getElementCollections($param) {
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlCollections($param, $_GET);

                // Construction des clauses
                self::construireFromEtWhereCollections($p);

                // Construction de la requête
                $champs = "cs_id_structure, cs_ville, cs_nom, cs_code_postal, cs_latitude, cs_longitude, cc_id_collection, cc_nom";
                $this->orderby = is_null($this->orderby) ? 'cs_ville ASC, cs_nom ASC, cc_nom ASC' : $this->orderby;
                $requete = $this->assemblerRequete($champs);

                $info =$this->lancerRequete($requete);
                return $info;
        }

        /**
         * Retourne le nombre de collections correspondant aux critères de recherche
         */
        public function getElementNombreCollections($param) {
                
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlCollections($param, $_GET);

                // Construction des clauses
                self::construireFromEtWhereCollections($p);
                
                // Construction de la requête
                // Il est important de compter le nombre d'association structure-collection différentes pour obtenir le bon nombre
                // @WTF si on compte(*) on a le nombre d'associations structure-collection
                // si on compte(cs_id_structure) on a le nombre d'associations structure-collection dont la structure n'est pas null
                // en aucun cas on n'a le nombre d'association structure-collection DIFFERENTES, à moins de faire un distinct, ce qui n'est pas le cas ici
                // @AMHA on devrait compter(*) car la recherche de collections renvoie même celles qui ont une structure null
                $champs = 'COUNT(*) AS nbre, cc_id_collection ';
                $this->orderby = is_null($this->orderby) ? 'cs_ville ASC, cs_nom ASC, cc_nom ASC' : $this->orderby;
                $requete = $this->assemblerRequete($champs);

                $info = $this->lancerRequeteNombre($requete);

                return $info;
        }

        /**
         * Retourne les personnes correspondant aux critères de recherche
         */
        public function getElementPersonnes($param) {
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlPersonnes($param, $_GET);

                // Construction des clauses
                self::construireFromEtWherePersonnes($p);
                $champs = 'cp_id_personne, cp_fmt_nom_complet, cp_prenom, cp_nom, cp_truk_nom_autre, cp_adresse_01, cp_ville, cp_naissance_date, cp_deces_date';
                $this->orderby = is_null($this->orderby) ? 'cp_nom ASC, cp_prenom ASC' : $this->orderby;
                $requete = $this->assemblerRequete($champs);
                
                $info = $this->lancerRequete($requete);
                return $info;
        }

        /**
         * Retourne le nombre de personnes correspondant aux critères de recherche
         */
        public function getElementNombrePersonnes($param) {
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlPersonnes($param, $_GET);

                // Construction des clauses
                self::construireFromEtWherePersonnes($p);
                $this->orderby = is_null($this->orderby) ? 'cp_nom ASC, cp_prenom ASC' : $this->orderby;
                $champs = 'count(*) as nbre';
                // Construction de la requête
                $requete = $this->assemblerRequete($champs);
                $info = $this->lancerRequeteNombre($requete);
                return $info;
        }
        
        /**
         * Retourne les publications correspondant aux critères de recherche
         */
        public function getElementPublications($param) {
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlPublications($param, $_GET);
        
                // Construction des clauses
                self::construireFromEtWherePublications($p);
                $champs = '*';
                $this->orderby = is_null($this->orderby) ? 'cpu_fmt_auteur ASC' : $this->orderby;
                $requete = $this->assemblerRequete($champs);
        
                $info = $this->lancerRequete($requete);
                return $info;
        }
        
        /**
         * Retourne le nombre de personnes correspondant aux critères de recherche
         */
        public function getElementNombrePublications($param) {
                // Pré traitement des paramêtres
                $p = $this->pretraiterParametresUrlPublications($param, $_GET);
        
                // Construction des clauses
                self::construireFromEtWherePublications($p);
                $this->orderby = is_null($this->orderby) ? 'cpu_fmt_auteur ASC' : $this->orderby;
                $champs = 'count(*) as nbre';
                // Construction de la requête
                $requete = $this->assemblerRequete($champs);
                $info = $this->lancerRequeteNombre($requete);
                return $info;
        }
        
        private function assemblerRequete($champs) {
                // Construction de la requête
                $requete = sprintf(
                                'SELECT %s %s'
                                . ' FROM %s %s'
                                . ' WHERE %s ORDER BY %s %s -- %s:%d',
        
                                $this->distinct ? 'DISTINCT' : '',
                                $champs,
                                implode(',', $this->fromClause),
                                implode(' ', $this->joinClause),
                                $this->whereClause ? implode(" AND ", $this->whereClause) : TRUE,
                                $this->orderby,
                                $this->limit != -1 ? "LIMIT {$this->start}, {$this->limit}" : "",
                                __FILE__,
                                __LINE__
                );
                //echo "REQUETE: " . $requete;
                //exit;
                return $requete;
        }
        
        private function lancerRequete($requete) {
                $info = array();
                // Récupération des résultats
                try {
                        $donnees = $this->bdd->query($requete)->fetchAll(PDO::FETCH_ASSOC);
                        if ($donnees === false) {
                                $this->messages[] = "La requête n'a retourné aucun résultat.";
                        } else {
                                $info = $donnees;
                        }
                } catch (PDOException $e) {
                        $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage());
                }
                return $info;
        }
        
        private function lancerRequeteNombre($requete) {
                $info = array();
                // Récupération des résultats
                try {
                        $donnees = $this->bdd->query($requete)->fetch(PDO::FETCH_ASSOC);
                        if ($donnees === false) {
                                $this->messages[] = "La requête n'a retourné aucun résultat.";
                        } else {
                                $info = $donnees['nbre'];
                        }
                } catch (PDOException $e) {
                        $this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage());
                }
                return $info;
        }

        // Voici les paramètres qu'il faut passer au service Collections, tous et dans l'ordre (soit une valeur soit '*')
        private function pretraiterParametresUrlCollections($param, $qs) {
                $params_passes = array(
                        'mots' => 'str', 
                        'sci' => 'bool', 
                        'bot' => 'int', 
                        'lieu-stockage' => 'str',
                        'zg' => 'str', 
                        'p' => 'str', 
                        'pr' => 'int',
                        'str-d' => 'frdepliste',
                        'veg' => 'int'
                );
                return $this->pretraiterParametresUrl($param, $qs, $params_passes);
        }

        // Voici les paramètres qu'il faut passer au service Personnes, tous et dans l'ordre (soit une valeur soit '*')
        private function pretraiterParametresUrlPersonnes($param, $qs) {
                $params_passes = array(
                        'nom-famille' => 'str', 
                        'adresse' => 'str', 
                        'date-vivant' => 'int'
                );
                return $this->pretraiterParametresUrl($param, $qs, $params_passes);
        }
        
        // Voici les paramètres qu'il faut passer au service Personnes, tous et dans l'ordre (soit une valeur soit '*')
        private function pretraiterParametresUrlPublications($param, $qs) {
                $params_passes = array(
                                'libre' => 'str',
                                'titre' => 'str',
                                'auteur' => 'str',
                                'mot-cle' => 'str',
                                'ouvrage' => 'str',
                                'date' => 'str'
                );
                return $this->pretraiterParametresUrl($param, $qs, $params_passes);
        }

        private function pretraiterParametresUrl($param, $qs, $params_passes) {
                $p = $this->traiterParametresUrl(array_keys($params_passes), $param, false);
                $this->debug[] = $param;
                foreach ($params_passes as $param_passe => $type) {
                        if (isset($p[$param_passe])) {
                                // Suppression des éventuels espaces en début et fin de chaine
                                $valeur = trim($p[$param_passe]);
                                
                                // Type de paramêtre chaine
                                if ($type == 'str') {
                                        // Suppression des slash
                                        $valeur = stripslashes($valeur);
                                        
                                        // Utilisation d'une recherche de chaîne exacte
                                        if (preg_match('/^"(.*)"$/', $valeur, $match)) {
                                                $valeur = '%'.$match[1].'%';
                                        } else {
                                                // Recherche de mots non liés
                                                $mots = explode(' ', $valeur);
                                                $valeur = '%'.implode ('%', $mots).'%';
                                        }
                                        // Mise en place des quotes pour l'intérogation dans la bdd
                                        $valeur = $this->bdd->quote($valeur);
                                }
                                // Type de paramêtre booléen
                                if ($type == 'bool') {
                                        if (preg_match('/^[0]$/', $valeur)) {
                                                $valeur = false;
                                        } else if (preg_match('/^[1]$/', $valeur)) {
                                                $valeur = true;
                                        } else {
                                                $this->messages[] = "Le paramêtre '$param_passe' attend une valeur de type 0 ou 1 et non '$valeur'.";
                                                $valeur = null;
                                        }
                                        
                                }
                                // Type de paramêtre entier
                                if ($type == 'int') {
                                        if (!preg_match('/^(?:[0-9]+,\s*)*[0-9]+$/', $valeur)) {
                                                $this->messages[] = "Le paramêtre '$param_passe' attend une ou plusieurs valeurs de type entiers ".
                                                        "séparés par des virgules et non '$valeur'.";
                                                $valeur = null;
                                        }
                                }

                                if ($type == 'frdepliste') {
                                        $valeur = array_filter(explode(',', $valeur), create_function('$val', 'return preg_match("/^(\d+|2A|2B)$/i", $val);'));
                                }

                                $p[$param_passe] = $valeur;
                        }
                }

        if(isset($qs['pays'])) {
            $p['pays'] = array_filter(explode(',', $qs['pays']), create_function('$val', 'return preg_match("/^[A-Z][A-Z]$/", $val);'));
            if(!$p['pays']) unset($p['pays']);
        }

                return $p;
        }

        // construit les clauses FROM et WHERE pour la recherche de collections (#CaptainObvious)
        private function construireFromEtWhereCollections($p) {
                $this->fromClause = array('coel_collection');
                $this->joinClause = array('LEFT JOIN coel_structure ON (cc_ce_structure = cs_id_structure)');
                $this->whereClause = array();

                // Gestion du from en fonction des paramêtres
                if (isset($p['str-d'])) {// ATTENTION : Remplace $this->fromClause, doit être situé en première position!
                        $this->fromClause = array('coel_structure');
                        $this->joinClause = array('LEFT JOIN coel_collection ON (cs_id_structure = cc_ce_structure)');
                }
                
                // Construire from et where en fonction des paramêtres 
                if (isset($p['mots'])) {
                        $this->whereClause[] = '(' . implode(' OR ', array(
                                "cc_nom LIKE {$p['mots']}",
                                "cc_truk_nom_alternatif LIKE {$p['mots']}",
                                "cc_truk_code LIKE {$p['mots']}",
                                "cc_description LIKE {$p['mots']}",
                                "cc_description_specialiste LIKE {$p['mots']}",
                                "cc_historique LIKE {$p['mots']}",
                                "cs_nom LIKE {$p['mots']}",
                                "cs_truk_nom_alternatif LIKE {$p['mots']}",
                                "cs_description LIKE {$p['mots']}",
                                "cs_adresse_01 LIKE {$p['mots']}",
                                "cs_ville LIKE {$p['mots']}",
                                "cs_truk_identifiant_alternatif LIKE {$p['mots']}",
                                "cs_condition_acces LIKE {$p['mots']}",
                                "cs_condition_usage LIKE {$p['mots']}",
                                "cs_truk_telephone LIKE {$p['mots']}",
                                "cs_courriel LIKE {$p['mots']}",
                                "cs_truk_url LIKE {$p['mots']}")) . ')';
                }

                if (isset($p['sci'])) {
                        if ($p['sci'] === true) {
                                $this->whereClause[] = 'csv_mark_visite_avec_motif = 1';
                        } else if ($p['sci'] === false) {
                                $this->whereClause[] = 'csv_mark_acces_ss_motif = 1';
                        }
                }
                if (isset($p['bot'])) {
                        $this->whereClause[] = "ccb_ce_truk_type IN ({$p['bot']})";
                }
                if (isset($p['lieu-stockage'])) {
      $this->joinClause[] = 'LEFT JOIN coel_meta_liste_valeur cmlv ON cmlv.cmlv_id_valeur = cs_ce_truk_pays';
                        $this->whereClause[] = '(' . implode(' OR ', array(
                                "cs_adresse_01 LIKE {$p['lieu-stockage']}",
                                "cs_code_postal LIKE {$p['lieu-stockage']}",
                                "cs_ville LIKE {$p['lieu-stockage']}",
                                "cs_ce_truk_pays LIKE {$p['lieu-stockage']}",
                                "cmlv.cmlv_nom LIKE {$p['lieu-stockage']}",
                        )) . ')';
                }
                if (isset($p['zg'])) {
                        $this->whereClause[] = "cc_truk_couverture_lieu LIKE {$p['zg']}";
                }
                if (isset($p['p'])) {
                        $this->whereClause[] = "cp_fmt_nom_complet LIKE {$p['p']}";
                }
                if (isset($p['pr'])) {
                        $this->whereClause[] = "ccap_id_role IN ({$p['pr']})";
                }

        // par défaut, spécifier un département restreint à la France
        // TODO: INNER JOIN
                if (isset($p['str-d'])) {
            $this->joinClause[] = 'LEFT JOIN coel_meta_liste_valeur cv ON cv.cmlv_id_valeur = cs_ce_truk_pays';
                        $this->whereClause[] = "cv.cmlv_abreviation IN ('FR', 'RE', 'YT', 'GP', 'MQ', 'GF', 'NC')";
                        $this->whereClause[] = sprintf("cs_code_postal REGEXP '^(%s).*'", implode('|', $p['str-d']));
                }

                if (isset($p['pays'])) {
            if(array_search('FR', $p['pays']) !== FALSE) $p['pays'] = array_merge($p['pays'], array('RE','YT','GP','MQ','GF','NC'));
            $this->joinClause[] = 'LEFT JOIN coel_meta_liste_valeur cv ON cv.cmlv_id_valeur = cs_ce_truk_pays';
                        $this->whereClause[] = sprintf('cv.cmlv_abreviation IN ("%s")', implode('","', $p['pays']));
                }

                if (isset($p['veg'])) {
                        $veg = explode(',', $p['veg']);
                        $veg_nbre = count($veg);
                        
                        if ($veg_nbre == 1) {
                                $this->whereClause[] = "ccb_truk_nature LIKE '%{$p['veg']}%'";
                        } else {
                                $recherche = array();
                                foreach ($veg as $id) {
                                        $recherche[] = "ccb_truk_nature LIKE '%$id%'";
                                }
                                $this->whereClause[] = '('.implode(' OR ', $recherche).') ';
                        }
                }
                

                if (isset($p['sci'])) {
                        $this->joinClause[] = 'LEFT JOIN coel_structure_valorisation ON (cs_id_structure = csv_id_structure)';
                }
                if (isset($p['bot']) || isset($p['veg'])) {
                        $this->joinClause[] = 'LEFT JOIN coel_collection_botanique ON (cc_id_collection = ccb_id_collection)';
                }
                if (isset($p['p']) || isset($p['pr'])) {
                        $this->joinClause[] = 'LEFT JOIN coel_collection_a_personne ON (cc_id_collection = ccap_id_collection)';
                }
                if (isset($p['p'])) {
                        $this->joinClause[] = 'LEFT JOIN coel_personne ON (ccap_id_personne = cp_id_personne)';
                }

        $this->joinClause = array_unique($this->joinClause);
        }

        // construit les clauses FROM et WHERE pour la recherche de personnes (#CaptainObvious)
        private function construireFromEtWherePersonnes($p) {
                $this->fromClause = array('coel_personne');
                $this->joinClause = array();
                $this->whereClause = array();

                //"cp_truk_recolte LIKE {$p['adresse']}", // Inclure le lieu de récolte ?

                if (isset($p['nom-famille'])) {
                        $this->whereClause[] = "cp_nom LIKE {$p['nom-famille']}";
                }
                if (isset($p['adresse'])) {
      $this->joinClause[] = 'LEFT JOIN coel_meta_liste_valeur cmlv ON cmlv.cmlv_id_valeur = cp_ce_truk_pays';
                        $this->whereClause[] = '(' . implode(' OR ', array(
                                "cp_adresse_01 LIKE {$p['adresse']}",
                                "cp_code_postal LIKE {$p['adresse']}",
                                "cp_ville LIKE {$p['adresse']}",
                                "cp_ce_truk_pays LIKE {$p['adresse']}",
                                "cp_naissance_lieu LIKE {$p['adresse']}",
                                "cp_deces_lieu LIKE {$p['adresse']}",
                                "cmlv.cmlv_nom LIKE {$p['adresse']}",
                        )) . ')';
                }
                if (isset($p['date-vivant'])) {
                        $this->whereClause[] = "cp_naissance_date <= {$p['date-vivant']}";
                        $this->whereClause[] = "cp_deces_date >= {$p['date-vivant']}";
                        $this->whereClause[] = "cp_naissance_date IS NOT NULL";
                        $this->whereClause[] = "cp_deces_date IS NOT NULL";
                        //$this->whereClause[] = "(cp_deces_date IS NULL OR cp_deces_date >= {$p['date-vivant']})";
                }

                // pour inclure éventuellement les adresses de la structure
                /*if (isset($p['adresse'])) {
                        $this->joinClause[] = 'JOIN coel_structure_a_personne ON (cp_id_personne = csap_id_personne)';
                        $this->joinClause[] = 'JOIN coel_structure ON (cs_id_structure = csap_id_structure)';
                }*/
        $this->joinClause = array_unique($this->joinClause);
        }
        
        // construit les clauses FROM et WHERE pour la recherche de publications (#CaptainObvious)
        private function construireFromEtWherePublications($p) {
                $this->fromClause = array('coel_publication');
                $this->joinClause = array();
                $this->whereClause = array();

                if (isset($p['libre'])) {
                        $this->whereClause[] = "cpu_fmt_nom_complet LIKE {$p['libre']}";
                }
                if (isset($p['titre'])) {
                        $this->whereClause[] = "cpu_titre LIKE {$p['titre']}";
                }
                if (isset($p['auteur'])) {
                        $this->whereClause[] = "cpu_fmt_auteur LIKE {$p['auteur']}";
                }
                if (isset($p['mot-cle'])) {
                        $this->whereClause[] = "cpu_resume_mot_cle LIKE {$p['mot-cle']}";
                }
                if (isset($p['ouvrage'])) {
                        $this->whereClause[] = "cpu_collection LIKE {$p['ouvrage']}";
                }
                if (isset($p['date'])) {
                        $this->whereClause[] = "cpu_date_parution LIKE {$p['date']}";
                }
        
        }
}