Subversion Repositories eFlore/Applications.del

Rev

Rev 1528 | Rev 1666 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1528 Rev 1618
1
<?php
1
<?php
2
/**
2
/**
3
 * Le web service observations récupère toutes les observations et, pour chacune d'elle, les
3
 * Le web service observations récupère toutes les observations et, pour chacune d'elle, les
4
 * images qui lui sont associées.
4
 * images qui lui sont associées.
5
 * Basée sur la classe antérieure dans ListeObservations.php de
5
 * Basée sur la classe antérieure dans ListeObservations.php de
6
 * Grégoire Duché et Aurélien Peronnet
6
 * Grégoire Duché et Aurélien Peronnet
7
 * (formaterVote, formaterDeterminations, chargerNombreCommentaire, chargerVotes, chargerDeterminations)
7
 * (formaterVote, formaterDeterminations, chargerNombreCommentaire, chargerVotes, chargerDeterminations)
8
 *
8
 *
9
 * @category	php 5.2
9
 * @category	php 5.2
10
 * @package		del
10
 * @package		del
11
 * @author		Raphaël Droz <raphael@tela-botanica.org>
11
 * @author		Raphaël Droz <raphael@tela-botanica.org>
12
 * @copyright	Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
12
 * @copyright	Copyright (c) 2013 Tela Botanica (accueil@tela-botanica.org)
13
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
13
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
14
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
14
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
15
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
15
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
16
 *
16
 *
17
 * TODO:
17
 * TODO:
18
 * PDO::prepare()
18
 * PDO::prepare()
19
 * Sphinx pour auteur, genre, ns, commune, tag et masque-général
19
 * Sphinx pour auteur, genre, ns, commune, tag et masque-général
20
 */
20
 */
21
 
21
 
22
require_once(dirname(__FILE__) . '/../DelTk.php');
22
require_once(dirname(__FILE__) . '/../DelTk.php');
23
/*
23
/*
24
  restore_error_handler();
24
  restore_error_handler();
25
  restore_exception_handler();
25
  restore_exception_handler();
26
  error_reporting(E_ALL);
26
  error_reporting(E_ALL);
27
*/
27
*/
28
 
28
 
29
class ListeObservations {
29
class ListeObservations {
30
 
30
 
31
    private $conteneur;
31
    private $conteneur;
32
    private $gestionBdd;
32
    private $gestionBdd;
33
    private $bdd;
33
    private $bdd;
34
    private $parametres = array();
34
    private $parametres = array();
35
    private $ressources = array();
35
    private $ressources = array();
36
 
36
 
37
    static $tris_possibles = array('date_observation');
37
    static $tris_possibles = array('date_observation');
38
    // paramètres autorisés
38
    // paramètres autorisés
39
 
39
 
40
    static $sql_fields_liaisons = array(
40
    static $sql_fields_liaisons = array(
41
	'dob' => array('id_observation', 'nom_sel AS `determination.ns`', 'nt AS `determination.nt`',
41
	'dob' => array('id_observation', 'nom_sel AS `determination.ns`', 'nt AS `determination.nt`',
42
		       'nom_sel_nn AS `determination.nn`', 'famille AS `determination.famille`',
42
		       'nom_sel_nn AS `determination.nn`', 'famille AS `determination.famille`',
43
		       'nom_referentiel AS `determination.referentiel`',
43
		       'nom_referentiel AS `determination.referentiel`',
44
		       'ce_zone_geo AS id_zone_geo', 'zone_geo', 'lieudit',
44
		       'ce_zone_geo AS id_zone_geo', 'zone_geo', 'lieudit',
45
		       'station', 'milieu', 'date_observation', 'mots_cles_texte', 'date_transmission',
45
		       'station', 'milieu', 'date_observation', 'mots_cles_texte', 'date_transmission',
46
		       'ce_utilisateur AS `auteur.id`', 'prenom_utilisateur AS `auteur.prenom`',
46
		       'ce_utilisateur AS `auteur.id`', 'prenom_utilisateur AS `auteur.prenom`',
47
		       'nom_utilisateur AS `auteur.nom`', 'courriel_utilisateur AS observateur',
47
		       'nom_utilisateur AS `auteur.nom`', 'courriel_utilisateur AS observateur',
48
		       'commentaire'),
48
		       'commentaire'),
49
	'di' => array('id_image', 'date_prise_de_vue AS `date`', 'hauteur',/* 'largeur','nom_original' // apparemment inutilisés */),
49
	'di' => array('id_image', 'date_prise_de_vue AS `date`', 'hauteur',/* 'largeur','nom_original' // apparemment inutilisés */),
50
	'du' => array('prenom', 'nom', 'courriel'),
50
	'du' => array('prenom', 'nom', 'courriel'),
51
	'dc' => array('commentaire')
51
	'dc' => array('commentaire')
52
    );
52
    );
53
 
53
 
54
 
54
 
55
    public function __construct(Conteneur $conteneur = null) {
55
    public function __construct(Conteneur $conteneur = null) {
56
	$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
56
		$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
57
	$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
57
		$this->conteneur->chargerConfiguration('config_departements_bruts.ini');
58
	$this->conteneur->chargerConfiguration('config_observations.ini');
58
		$this->conteneur->chargerConfiguration('config_observations.ini');
59
	$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
59
		$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
60
	$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
60
		$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
61
	$this->navigation = $conteneur->getNavigation();
61
		$this->navigation = $conteneur->getNavigation();
62
	$this->masque = $conteneur->getMasque();
62
		$this->masque = $conteneur->getMasque();
63
	$this->gestionBdd = $conteneur->getGestionBdd();
63
		$this->gestionBdd = $conteneur->getGestionBdd();
64
	$this->bdd = $this->gestionBdd->getBdd();
64
		$this->bdd = $this->gestionBdd->getBdd();
65
    }
65
    }
66
 
66
 
67
    static function reformateObservation($obs, $url_pattern = '') {
67
    static function reformateObservation($obs, $url_pattern = '') {
68
	$obs = array_map('array_filter', $obs);
68
		$obs = array_map('array_filter', $obs);
69
	$obs_merged = array();
69
		$obs_merged = array();
70
	foreach($obs as $o) {
70
		foreach($obs as $o) {
71
	    $id = $o['id_observation'];
71
		    $id = $o['id_observation'];
72
 
72
	
73
	    // car auteur.id peut être un email, un hash, ou un annuaire_tela.U_ID
73
		    // car auteur.id peut être un email, un hash, ou un annuaire_tela.U_ID
74
	    // mais dans les deux premiers cas SELECT courriel AS observateur fait déjà l'affaire
74
		    // mais dans les deux premiers cas SELECT courriel AS observateur fait déjà l'affaire
75
	    if(!isset($o['auteur.id']) || !is_numeric($o['auteur.id'])) $o['auteur.id'] = "0";
75
		    if(!isset($o['auteur.id']) || !is_numeric($o['auteur.id'])) $o['auteur.id'] = "0";
76
	    if(!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
76
		    if(!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
77
 
77
	
78
	    $image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
78
		    $image = array_intersect_key($o, array_flip(array('id_image', 'date', 'hauteur' , 'largeur', 'nom_original')));
79
	    $image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
79
		    $image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
80
	    unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
80
		    unset($o['id_image'], $o['date'], $o['hauteur'], $o['largeur'], $o['nom_original']);
81
	    if(!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
81
		    if(!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
82
	    $obs_merged['"' . $id . '"']['images'][] = $image;
82
		    $obs_merged['"' . $id . '"']['images'][] = $image;
83
	}
83
		}
84
	return $obs_merged;
84
		return $obs_merged;
85
    }
85
    }
86
 
86
 
87
    /**
87
    /**
88
     * Méthode principale de la classe.
88
     * Méthode principale de la classe.
89
     * Lance la récupération des images dans la base et les place dans un objet ResultatService
89
     * Lance la récupération des images dans la base et les place dans un objet ResultatService
90
     * pour l'afficher.
90
     * pour l'afficher.
91
     * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
91
     * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
92
     * @param array $parametres les paramètres situés après le ? dans l'url
92
     * @param array $parametres les paramètres situés après le ? dans l'url
93
     **/	
93
     **/	
94
    public function consulter($ressources, $parametres) {
94
    public function consulter($ressources, $parametres) {
95
	// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
95
		// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
96
	$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
96
		$req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
97
 
97
	
98
	// toujours nécessaire puisque nous tapons sur v_del_image qui INNER JOIN cel_images, or nous voulons certes
98
		// toujours nécessaire puisque nous tapons sur v_del_image qui INNER JOIN cel_images, or nous voulons certes
99
	// toutes les images, mais nous voulons $limite observations uniques.
99
		// toutes les images, mais nous voulons $limite observations uniques.
100
	$req['groupby'][] = 'vdi.id_observation';
100
		$req['groupby'][] = 'vdi.id_observation';
101
 
101
	
102
	$db = $this->bdd;
102
		$db = $this->bdd;
103
 
103
	
104
	// filtrage de l'INPUT
104
		// filtrage de l'INPUT
105
	$params = DelTk::requestFilterParams($parametres, DelTk::$parametres_autorises, $this->conteneur);
105
		$params = DelTk::requestFilterParams($parametres, DelTk::$parametres_autorises, $this->conteneur);
106
 
106
	
107
	$params['masque.tag'] = DelTk::buildTagsAST(@$parametres['masque.tag'], 'OR', ',');
107
		$params['masque.tag'] = DelTk::buildTagsAST(@$parametres['masque.tag'], 'OR', ',');
108
 
108
	
109
	// ... et paramètres par défaut
109
		// ... et paramètres par défaut
110
	$params = array_merge(DelTk::$default_params, $params);
110
		$params = array_merge(DelTk::$default_params, $params);
111
 
111
	
112
	// création des contraintes (masques)
112
		// création des contraintes (masques)
113
	DelTk::sqlAddConstraint($params, $db, $req);
113
		DelTk::sqlAddConstraint($params, $db, $req);
114
	self::sqlAddConstraint($params, $db, $req, $this->conteneur);
114
		self::sqlAddConstraint($params, $db, $req, $this->conteneur);
115
	self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
115
		self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
116
 
116
	
117
	// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
117
		// 1) grunt-work: *la* requête de récupération des id valides (+ SQL_CALC_FOUND_ROWS)
118
	$idobs_tab = self::getIdObs($params, $req, $db);
118
		$idobs_tab = self::getIdObs($params, $req, $db);
119
	// idobs est une liste (toujours ordonnée) des id d'observations recherchées
119
		// idobs est une liste (toujours ordonnée) des id d'observations recherchées
120
	$idobs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'), $idobs_tab));
120
		$idobs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'), $idobs_tab));
121
 
121
	
122
	if($idobs) {
122
		if($idobs) {
123
	    $total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
123
		    $total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
124
 
124
	
125
	    // 2) récupération des données nécessaires pour ces observations (obs + images)
125
		    // 2) récupération des données nécessaires pour ces observations (obs + images)
126
	    // ici les champs récupérés sont issus de self::$sql_fields_liaisons mais sans préfixes
126
		    // ici les champs récupérés sont issus de self::$sql_fields_liaisons mais sans préfixes
127
	    // car tout provient de v_del_image
127
		    // car tout provient de v_del_image
128
	    $obs_unfmt = self::getInfos($idobs, $db);
128
		    $obs_unfmt = self::getInfos($idobs, $db);
129
 
129
	
130
	    // 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
130
		    // 3) suppression, merge des données en tableau assez représentatif du futur JSON en output
131
	    $observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
131
		    $observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
132
 
132
	
133
	    // 4) récupération des données nécessaires pour ces observations (commentaires + votes)
133
		    // 4) récupération des données nécessaires pour ces observations (commentaires + votes)
134
	    // modifie $observations
134
		    // modifie $observations
135
	    $this->configurer();
135
		    $this->configurer();
136
	    $this->chargerDeterminations($observations);
136
		    $this->chargerDeterminations($observations);
137
 
137
	
138
	    // 5) restauration de l'ordre souhaité initialement
138
		    // 5) restauration de l'ordre souhaité initialement
139
	    $observations = self::sortArrayByArray($observations, $idobs);
139
		    $observations = self::sortArrayByArray($observations, $idobs);
140
	} else {
140
		} else {
141
	    $observations = array();
141
		    $observations = array();
142
	    $total = 0;
142
		    $total = 0;
143
	}
143
		}
144
 
144
	
145
	// 6) JSON output
145
		// 6) JSON output
146
	$resultat = new ResultatService();
146
		$resultat = new ResultatService();
147
	$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params, Config::get('url_service')),
147
		$resultat->corps = array('entete' => DelTk::makeJSONHeader($total, $params, Config::get('url_service')),
148
				 'resultats' => $observations);
148
					 'resultats' => $observations);
149
		
149
			
150
	return $resultat;
150
		return $resultat;
151
    }
151
    }
152
 
152
 
153
    static function sortArrayByArray($array, $orderArray) {
153
    static function sortArrayByArray($array, $orderArray) {
154
	$ordered = array();
154
		$ordered = array();
155
	foreach($orderArray as $key) {
155
		foreach($orderArray as $key) {
156
	    if(array_key_exists('"' . $key . '"', $array)) {
156
		    if(array_key_exists('"' . $key . '"', $array)) {
157
		$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
157
			$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
158
		unset($array['"' . $key . '"']);
158
			unset($array['"' . $key . '"']);
159
	    }
159
		    }
160
	}
160
		}
161
	return $ordered + $array;
161
		return $ordered + $array;
162
    }
162
    }
163
 
163
 
164
    // SQL helpers
164
    // SQL helpers
165
    /*
165
    /*
166
     * Retourne une liste ordonnée d'id d'observation correspondant aux critères
166
     * Retourne une liste ordonnée d'id d'observation correspondant aux critères
167
     * passés dans p et aux clauses where/join présentes dans le tableau $req
167
     * passés dans p et aux clauses where/join présentes dans le tableau $req
168
     *
168
     *
169
     * @param p: $params (filtrés sauf escape-string)
169
     * @param p: $params (filtrés sauf escape-string)
170
     * @param req: le tableau représentant les composants de la requete SQL
170
     * @param req: le tableau représentant les composants de la requete SQL
171
     * @param db: l'instance de db
171
     * @param db: l'instance de db
172
     */
172
     */
173
    static function getIdObs($p, $req, $db) {
173
    static function getIdObs($p, $req, $db) {
174
	$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
174
	$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
175
			 ' FROM v_del_image vdi'.
175
			 ' FROM v_del_image vdi'.
176
			 ' %s' . // LEFT JOIN if any
176
			 ' %s' . // LEFT JOIN if any
177
			 ' WHERE %s'. // where-clause ou TRUE
177
			 ' WHERE %s'. // where-clause ou TRUE
178
			 ' %s'. // group-by
178
			 ' %s'. // group-by
179
			 ' %s'. // having (si commentaires)
179
			 ' %s'. // having (si commentaires)
180
			 ' ORDER BY %s %s %s'.
180
			 ' ORDER BY %s %s %s'.
181
			 ' LIMIT %d, %d -- %s',
181
			 ' LIMIT %d, %d -- %s',
182
						 
182
						 
183
			 $req['join'] ? implode(' ', $req['join']) : '',
183
			 $req['join'] ? implode(' ', $req['join']) : '',
184
			 $req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
184
			 $req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
185
 
185
 
186
			 $req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
186
			 $req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
187
			 $req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
187
			 $req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
188
 
188
 
189
			 $p['tri'], strtoupper($p['ordre']),
189
			 $p['tri'], strtoupper($p['ordre']),
190
			 // date_transmission peut-être NULL et nous voulons de la consistence
190
			 // date_transmission peut-être NULL et nous voulons de la consistence
191
			 // (sauf après r1860 de Cel)
191
			 // (sauf après r1860 de Cel)
192
			 $p['tri'] == 'date_transmission' ? ', id_observation' : '',
192
			 $p['tri'] == 'date_transmission' ? ', id_observation' : '',
193
 
193
 
194
			 $p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
194
			 $p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
195
 
195
 
196
	$res = $db->recupererTous($req_s);
196
	$res = $db->recupererTous($req_s);
197
	$err = mysql_error();
197
	$err = mysql_error();
198
	if(!$res && $err) {
198
	if(!$res && $err) {
199
	    // http_response_code(400);
199
	    // http_response_code(400);
200
	    // if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
200
	    // if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
201
	    throw new Exception('not found', 400);
201
	    throw new Exception('not found', 400);
202
	}
202
	}
203
	// ordre préservé, à partir d'ici c'est important.
203
	// ordre préservé, à partir d'ici c'est important.
204
	return $res;
204
	return $res;
205
    }
205
    }
206
 
206
 
207
    /*
207
    /*
208
      Champs récupérés:
208
      Champs récupérés:
209
      Pour del_images, la vue retourne déjà ce que nous recherchons de cel_obs et cel_images
209
      Pour del_images, la vue retourne déjà ce que nous recherchons de cel_obs et cel_images
210
      (cel_obs.* et cel_[obs_]images.{id_observation, id_image, date_prise_de_vue AS date, hauteur, largeur})
210
      (cel_obs.* et cel_[obs_]images.{id_observation, id_image, date_prise_de_vue AS date, hauteur, largeur})
211
      Pour del_commentaires: nous voulons *
211
      Pour del_commentaires: nous voulons *
212
      Reste ensuite à formatter.
212
      Reste ensuite à formatter.
213
      Note: le préfixe de table utilisé ici (vdi) n'impacte *aucune* autre partie du code car rien
213
      Note: le préfixe de table utilisé ici (vdi) n'impacte *aucune* autre partie du code car rien
214
      n'en dépend pour l'heure. (inutilisation de $req['select'])
214
      n'en dépend pour l'heure. (inutilisation de $req['select'])
215
    */
215
    */
216
    static function getInfos($idobs, $db) {
216
    static function getInfos($idobs, $db) {
217
	/*$select_fields = implode(',', array_merge(
217
	/*$select_fields = implode(',', array_merge(
218
	  array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['dob']),
218
	  array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['dob']),
219
	  array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['di']),
219
	  array_map(create_function('$a', 'return "vdi.".$a;'), self::$sql_fields_liaisons['di']),
220
	  array_map(create_function('$a', 'return "du.".$a;'), self::$sql_fields_liaisons['du'])));*/
220
	  array_map(create_function('$a', 'return "du.".$a;'), self::$sql_fields_liaisons['du'])));*/
221
	$select_fields = array_merge(self::$sql_fields_liaisons['dob'],
221
	$select_fields = array_merge(self::$sql_fields_liaisons['dob'],
222
				     self::$sql_fields_liaisons['di']);
222
				     self::$sql_fields_liaisons['di']);
223
	$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
223
	$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
224
			 // ' LEFT JOIN del_commentaire AS dc ON di.id_observation = dc.ce_observation AND dc.nom_sel IS NOT NULL'.
224
			 // ' LEFT JOIN del_commentaire AS dc ON di.id_observation = dc.ce_observation AND dc.nom_sel IS NOT NULL'.
225
			 ' WHERE id_observation IN (%s)',
225
			 ' WHERE id_observation IN (%s)',
226
			 implode(',', $select_fields),
226
			 implode(',', $select_fields),
227
			 implode(',', $idobs));
227
			 implode(',', $idobs));
228
	return $db->recupererTous($req_s);
228
	return $db->recupererTous($req_s);
229
    }
229
    }
230
 
230
 
231
 
231
 
232
    /**
232
    /**
233
     * Complément à DelTk::sqlAddConstraint()
233
     * Complément à DelTk::sqlAddConstraint()
234
     *
234
     *
235
     * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
235
     * @param $p les paramètres (notamment de masque) passés par l'URL et déjà traités/filtrés (sauf quotes)
236
     * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
236
     * @param $req le tableau, passé par référence représentant les composants de la requête à bâtir
237
     * @param $c conteneur, utilisé soit pour l'appel récursif à requestFilterParams() en cas de param "masque"
237
     * @param $c conteneur, utilisé soit pour l'appel récursif à requestFilterParams() en cas de param "masque"
238
     *								soit pour la définition du type (qui utilise la variable nb_commentaires_discussion)
238
     *								soit pour la définition du type (qui utilise la variable nb_commentaires_discussion)
239
     */
239
     */
240
    static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
240
    static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
241
	if(!empty($p['masque.tag'])) {
241
	if(!empty($p['masque.tag'])) {
242
	    // TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
242
	    // TODO: remove LOWER() lorsqu'on est sur que les tags sont uniformés en minuscule
243
	    // i_mots_cles_texte provient de la VIEW v_del_image
243
	    // i_mots_cles_texte provient de la VIEW v_del_image
244
	    if(isset($p['masque.tag']['AND'])) {
244
	    if(isset($p['masque.tag']['AND'])) {
245
		/* Lorsque nous interprêtons la chaîne provenant du masque général (cf: buildTagsAST($p['masque'], 'OR', ' ') dans sqlAddMasqueConstraint()),
245
		/* Lorsque nous interprêtons la chaîne provenant du masque général (cf: buildTagsAST($p['masque'], 'OR', ' ') dans sqlAddMasqueConstraint()),
246
		   nous sommes splittés par espace. Cependant, assurons que si une virgule à été saisie, nous n'aurons pas le motif
246
		   nous sommes splittés par espace. Cependant, assurons que si une virgule à été saisie, nous n'aurons pas le motif
247
		   " AND CONCAT(mots_cles_texte, i_mots_cles_texte) REGEXP ',' " dans notre requête.
247
		   " AND CONCAT(mots_cles_texte, i_mots_cles_texte) REGEXP ',' " dans notre requête.
248
		   XXX: Au 12/11/2013, une recherche sur tag depuis le masque général implique un OU, donc le problème ne se pose pas ici */
248
		   XXX: Au 12/11/2013, une recherche sur tag depuis le masque général implique un OU, donc le problème ne se pose pas ici */
249
		$subwhere = array();
249
		$subwhere = array();
250
		foreach($p['masque.tag']['AND'] as $tag) {
250
		foreach($p['masque.tag']['AND'] as $tag) {
251
		    if(trim($tag) == ',') continue;
251
		    if(trim($tag) == ',') continue;
252
 
252
 
253
		    $subwhere[] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
253
		    $subwhere[] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
254
					  DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
254
					  DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
255
					  $db->proteger(strtolower($tag)));
255
					  $db->proteger(strtolower($tag)));
256
		}
256
		}
257
		$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
257
		$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
258
	    }
258
	    }
259
	    else {
259
	    else {
260
		$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
260
		$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
261
					  DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
261
					  DelTk::sqlAddIfNullPourConcat(array('vdi.mots_cles_texte', 'vdi.i_mots_cles_texte')),
262
					  $db->proteger(strtolower(implode('|', $p['masque.tag']['OR']))));
262
					  $db->proteger(strtolower(implode('|', $p['masque.tag']['OR']))));
263
	    }
263
	    }
264
	}
264
	}
265
 
265
 
266
	if(!empty($p['masque.type'])) {
266
	if(!empty($p['masque.type'])) {
267
	    self::addTypeConstraints($p['masque.type'], $db, $req, $c);
267
	    self::addTypeConstraints($p['masque.type'], $db, $req, $c);
268
	}
268
	}
269
    }
269
    }
270
 
270
 
271
    /* Le masque fait une recherche générique parmi de nombreux champs ci-dessus.
271
    /* Le masque fait une recherche générique parmi de nombreux champs ci-dessus.
272
       Nous initialisons donc ces paramètres (excepté masque biensur), et nous rappelons
272
       Nous initialisons donc ces paramètres (excepté masque biensur), et nous rappelons
273
       récursivement. À la seule différence que nous n'utiliserons que $or_req['where']
273
       récursivement. À la seule différence que nous n'utiliserons que $or_req['where']
274
       imploded par des " OR ". */
274
       imploded par des " OR ". */
275
    static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
275
    static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
276
	if(!empty($p['masque'])) {
276
	if(!empty($p['masque'])) {
277
	    $or_params = array('masque.auteur' => $p['masque'],
277
	    $or_params = array('masque.auteur' => $p['masque'],
278
			       'masque.departement' => $p['masque'],
278
			       'masque.departement' => $p['masque'],
279
			       'masque.id_zone_geo' => $p['masque'],
279
			       'masque.id_zone_geo' => $p['masque'],
280
			       'masque.tag' => $p['masque'],
280
			       'masque.tag' => $p['masque'],
281
			       'masque.ns' => $p['masque'],
281
			       'masque.ns' => $p['masque'],
282
			       'masque.famille' => $p['masque'],
282
			       'masque.famille' => $p['masque'],
283
			       'masque.date' => $p['masque'],
283
			       'masque.date' => $p['masque'],
284
			       'masque.genre' => $p['masque'],
284
			       'masque.genre' => $p['masque'],
285
			       /* milieu: TODO ? */ );
285
			       /* milieu: TODO ? */ );
286
	    /* Cependant les champs spécifiques ont priorité sur le masque général.
286
	    /* Cependant les champs spécifiques ont priorité sur le masque général.
287
	       Pour cette raison nous supprimons la génération de SQL du masque général sur les
287
	       Pour cette raison nous supprimons la génération de SQL du masque général sur les
288
	       champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
288
	       champ spécifiques qui feront l'objet d'un traitement avec une valeur propre. */
289
	    if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
289
	    if(isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
290
	    if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
290
	    if(isset($p['masque.departement'])) unset($or_params['masque.departement']);
291
	    if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
291
	    if(isset($p['masque.id_zone_geo'])) unset($or_params['masque.id_zone_geo']);
292
	    if(isset($p['masque.tag'])) unset($or_params['masque.tag']);
292
	    if(isset($p['masque.tag'])) unset($or_params['masque.tag']);
293
	    if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
293
	    if(isset($p['masque.famille'])) unset($or_params['masque.famille']);
294
	    if(isset($p['masque.date'])) unset($or_params['masque.date']);
294
	    if(isset($p['masque.date'])) unset($or_params['masque.date']);
295
	    if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
295
	    if(isset($p['masque.genre'])) unset($or_params['masque.genre']);
296
 
296
 
297
 
297
 
298
	    $or_masque = DelTk::requestFilterParams($or_params, array_keys($or_params), $c);
298
	    $or_masque = DelTk::requestFilterParams($or_params, array_keys($or_params), $c);
299
	    if(isset($or_params['masque.tag'])) {
299
	    if(isset($or_params['masque.tag'])) {
300
		$or_masque['masque.tag'] = DelTk::buildTagsAST($p['masque'], 'OR', ' ');
300
		$or_masque['masque.tag'] = DelTk::buildTagsAST($p['masque'], 'OR', ' ');
301
	    }
301
	    }
302
 
302
 
303
	    // $or_req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
303
	    // $or_req = array('select' => array(), 'join' => array(), 'where' => array(), 'groupby' => array(), 'having' => array());
304
	    $or_req = array('join' => array(), 'where' => array());
304
	    $or_req = array('join' => array(), 'where' => array());
305
	    DelTk::sqlAddConstraint($or_masque, $db, $or_req);
305
	    DelTk::sqlAddConstraint($or_masque, $db, $or_req);
306
	    self::sqlAddConstraint($or_masque, $db, $or_req);
306
	    self::sqlAddConstraint($or_masque, $db, $or_req);
307
 
307
 
308
	    if($or_req['where']) {
308
	    if($or_req['where']) {
309
		$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
309
		$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
310
		// utile au cas ou des jointures seraient rajoutées
310
		// utile au cas ou des jointures seraient rajoutées
311
		$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
311
		$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
312
	    }
312
	    }
313
	}
313
	}
314
    }
314
    }
315
 
315
 
316
 
316
 
317
    private function configurer() {
317
    private function configurer() {
318
	$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
318
	$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
319
	$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
319
	$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
320
    }
320
    }
321
 
321
 
322
 
322
 
323
    /*
323
    /*
324
     * @param $req: la représentation de la requête MySQL complète, à amender.
324
     * @param $req: la représentation de la requête MySQL complète, à amender.
325
     */
325
     */
326
    static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
326
    static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
327
	if(array_key_exists('adeterminer', $val)) {
327
	if(array_key_exists('adeterminer', $val)) {
328
	    // On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
328
	    // On récupère toutes les observations qui on le tag "aDeterminer" *ou* qui n'ont pas de nom d'espèce
329
	    // *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
329
	    // *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
330
	    $req['where'][] = '(' . implode(' OR ', array(
330
	    $req['where'][] = '(' . implode(' OR ', array(
331
		'vdi.certitude = "aDeterminer"',
331
		'vdi.certitude = "aDeterminer"',
332
		'vdi.certitude = "douteux"',
332
		'vdi.certitude = "douteux"',
333
		'vdi.mots_cles_texte LIKE "%aDeterminer%"',
333
		'vdi.mots_cles_texte LIKE "%aDeterminer%"',
334
		'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
334
		'vdi.nom_sel_nn IS NULL', // TODO: ensure pas d'entrée à 0
335
	    )) . ')';
335
	    )) . ')';
336
	}
336
	}
337
	if(array_key_exists('validees', $val)) {
337
	if(array_key_exists('validees', $val)) {
338
	    //On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
338
	    //On récupère toutes les observations ayant un commentaire doté de proposition_retenue = 1
339
	    $req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
339
	    $req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation AND dc.proposition_retenue = 1';
340
	}
340
	}
341
 
341
 
342
	// solution n°1: impraticable
342
	// solution n°1: impraticable
343
	if(false && array_key_exists('endiscussion', $val)) {
343
	if(false && array_key_exists('endiscussion', $val)) {
344
	    //Si on veut les observations en discussion,
344
	    //Si on veut les observations en discussion,
345
	    // on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
345
	    // on va récupérer les ids des observations dont le nombre de commentaire est supérieur à N
346
	    $req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
346
	    $req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
347
	    $req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
347
	    $req['join'][] = 'INNER JOIN del_commentaire AS dc ON vdi.id_observation = dc.ce_observation';
348
	    $req['groupby'][] = 'vdi.id_observation';
348
	    $req['groupby'][] = 'vdi.id_observation';
349
	    $req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
349
	    $req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
350
	}
350
	}
351
 
351
 
352
	if(array_key_exists('endiscussion', $val)) {
352
	if(array_key_exists('endiscussion', $val)) {
353
	    $req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
353
	    $req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
354
		' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
354
		' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
355
	}
355
	}
356
    }
356
    }
357
 
357
 
358
 
358
 
359
    /**
359
    /**
360
     * Récupérer toutes les déterminations et le nombre de commentaire au total
360
     * Récupérer toutes les déterminations et le nombre de commentaire au total
361
     * @param array $observations la liste des observations à mettre à jour
361
     * @param array $observations la liste des observations à mettre à jour
362
     * */
362
     * */
363
    private function chargerDeterminations(&$observations) {
363
    private function chargerDeterminations(&$observations) {
364
	$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
364
	$idObs = array_values(array_map(create_function('$a', 'return $a["id_observation"];'),
365
					$observations));
365
					$observations));
366
	$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
366
	$r = sprintf('SELECT * FROM del_commentaire AS dc WHERE dc.nom_sel IS NOT NULL AND ce_observation IN (%s) -- %s',
367
		     implode(',',$idObs),
367
		     implode(',',$idObs),
368
		     __FILE__ . ':' . __LINE__);
368
		     __FILE__ . ':' . __LINE__);
369
	$propositions = $this->bdd->recupererTous($r);
369
	$propositions = $this->bdd->recupererTous($r);
370
	if(!$propositions) return;
370
	if(!$propositions) return;
371
	foreach ($propositions as $proposition) {
371
	foreach ($propositions as $proposition) {
372
	    $idObs = $proposition['ce_observation'];
372
	    $idObs = $proposition['ce_observation'];
373
	    $idComment = $proposition['id_commentaire'];
373
	    $idComment = $proposition['id_commentaire'];
374
	    $comment = $this->formaterDetermination($idComment, $proposition);
374
	    $comment = $this->formaterDetermination($idComment, $proposition);
375
	    if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
375
	    if($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
376
				
376
				
377
	}
377
	}
378
    }
378
    }
379
 
379
 
380
    private function formaterDetermination($commentId, $proposition) {
380
    private function formaterDetermination($commentId, $proposition) {
381
	if(!$proposition) return NULL;
381
	if(!$proposition) return NULL;
382
 
382
 
383
	$proposition_formatee = array('nb_commentaires' => '0');
383
	$proposition_formatee = array('nb_commentaires' => '0');
384
	foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
384
	foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
385
	    if (isset($proposition[$nomOriginal])) {
385
	    if (isset($proposition[$nomOriginal])) {
386
		$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
386
		$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
387
	    }
387
	    }
388
	}
388
	}
389
 
389
 
390
	// Charger les votes sur les déterminations
390
	// Charger les votes sur les déterminations
391
	$resultatsVotes = $this->bdd->recupererTous(
391
	$resultatsVotes = $this->bdd->recupererTous(
392
	    sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
392
	    sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
393
		
393
		
394
	foreach ($resultatsVotes as $vote) {
394
	foreach ($resultatsVotes as $vote) {
395
	    $proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
395
	    $proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
396
	}
396
	}
397
 
397
 
398
 
398
 
399
	// chargerNombreCommentaire()
399
	// chargerNombreCommentaire()
400
	// Charger le nombre de commentaires (sans détermination) associé à l'observation
400
	// Charger le nombre de commentaires (sans détermination) associé à l'observation
401
	$listeCommentaires = $this->bdd->recupererTous(sprintf(
401
	$listeCommentaires = $this->bdd->recupererTous(sprintf(
402
	    'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
402
	    'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
403
	    'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
403
	    'FROM del_commentaire WHERE ce_proposition = %d GROUP BY ce_proposition -- %s',
404
	    $commentId, __FILE__ . ':' . __LINE__));
404
	    $commentId, __FILE__ . ':' . __LINE__));
405
	foreach ($listeCommentaires as $ligneProposition) {
405
	foreach ($listeCommentaires as $ligneProposition) {
406
	    // ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
406
	    // ce test sert à exclure les proposition de 1er niveau qui sont elles aussi des commentaires
407
	    if($ligneProposition['ce_commentaire_parent']) {
407
	    if($ligneProposition['ce_commentaire_parent']) {
408
		// TODO/debug: id_commentaire_parent != $commentId ??
408
		// TODO/debug: id_commentaire_parent != $commentId ??
409
		// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
409
		// reprendre la "logique" du code... moins de boucles, moins de requêtes, ...
410
		if($ligneProposition['ce_commentaire_parent'] != $commentId) {
410
		if($ligneProposition['ce_commentaire_parent'] != $commentId) {
411
		    // restore_error_handler();
411
		    // restore_error_handler();
412
		    error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
412
		    error_log(sprintf("possible error: nb_commentaires = %s: comment = %d, parent = %d, %s",
413
				      $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
413
				      $ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
414
		}
414
		}
415
		$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
415
		$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
416
	    } else {
416
	    } else {
417
		$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
417
		$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
418
	    }
418
	    }
419
	}
419
	}
420
 
420
 
421
	return $proposition_formatee;
421
	return $proposition_formatee;
422
    }
422
    }
423
 
423
 
424
    /**
424
    /**
425
     *	Formater un vote en fonction du fichier de configuration config_votes.ini
425
     *	Formater un vote en fonction du fichier de configuration config_votes.ini
426
     *	@param $votes array()
426
     *	@param $votes array()
427
     * */
427
     * */
428
    private function formaterVote($vote) {
428
    private function formaterVote($vote) {
429
	$retour = array();
429
	$retour = array();
430
	foreach ($vote as $param=>$valeur) {
430
	foreach ($vote as $param=>$valeur) {
431
	    $retour[$this->mappingVotes[$param]] = $valeur;
431
	    $retour[$this->mappingVotes[$param]] = $valeur;
432
	}
432
	}
433
	return $retour;
433
	return $retour;
434
    }
434
    }
435
}
435
}