Subversion Repositories eFlore/Applications.del

Rev

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

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