* @author Jean-Pascal MILCENT * @license GPL v3 * @license CECILL v2 * @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']}"; } } }