Subversion Repositories eFlore/Applications.del

Rev

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

Rev 1794 Rev 1798
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->bdd = $this->conteneur->getBdd();
60
		$this->bdd = $this->conteneur->getBdd();
61
    }
61
    }
62
 
62
 
63
	static function reformateObservation($obs, $url_pattern = '') {
63
	static function reformateObservation($obs, $url_pattern = '') {
64
		$obs = array_map('array_filter', $obs);
64
		$obs = array_map('array_filter', $obs);
65
		$obs_merged = array();
65
		$obs_merged = array();
66
		foreach ($obs as $o) {
66
		foreach ($obs as $o) {
67
			$id = $o['id_observation'];
67
			$id = $o['id_observation'];
68
 
68
 
69
			// 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
70
			// 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
71
			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";
72
			if (!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
72
			if (!isset($o['auteur.nom'])) $o['auteur.nom'] = '[inconnu]';
73
 
73
 
74
			$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')));
75
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
75
			$image['binaire.href'] = sprintf($url_pattern, $image['id_image']);
76
			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']);
77
			if (!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
77
			if (!isset($obs_merged['"' . $id . '"'])) $obs_merged['"' . $id . '"'] = $o;
78
			$obs_merged['"' . $id . '"']['images'][] = $image;
78
			$obs_merged['"' . $id . '"']['images'][] = $image;
79
		}
79
		}
80
		return $obs_merged;
80
		return $obs_merged;
81
    }
81
    }
82
 
82
 
83
	/**
83
	/**
84
	 * Méthode principale de la classe.
84
	 * Méthode principale de la classe.
85
	 * 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
86
	 * pour l'afficher.
86
	 * pour l'afficher.
87
	 * @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)
88
	 * @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
89
	 **/
89
	 **/
90
	public function consulter($ressources, $parametres) {
90
	public function consulter($ressources, $parametres) {
91
		// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
91
		// SELECT, à terme, pourrait affecter getInfos(), mais en aucune manière getIdObs()
92
		$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());
93
 
93
 
94
		// 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
95
		// toutes les images, mais nous voulons $limite observations uniques.
95
		// toutes les images, mais nous voulons $limite observations uniques.
96
		$req['groupby'][] = 'vdi.id_observation';
96
		$req['groupby'][] = 'vdi.id_observation';
97
 
97
 
98
		$db = $this->bdd;
98
		$db = $this->bdd;
99
 
99
 
100
		// filtrage de l'INPUT
100
		// filtrage de l'INPUT
101
		$params = DelTk::requestFilterParams($parametres, DelTk::$parametres_autorises, $this->conteneur);
101
		$params = DelTk::requestFilterParams($parametres, DelTk::$parametres_autorises, $this->conteneur);
102
 
102
 
103
		$params['masque.tag'] = DelTk::buildTagsAST(@$parametres['masque.tag'], 'OR', ',');
103
		$params['masque.tag'] = DelTk::buildTagsAST(@$parametres['masque.tag'], 'OR', ',');
104
 
104
 
105
		// ... et paramètres par défaut
105
		// ... et paramètres par défaut
106
		$params = array_merge(DelTk::$default_params, $params);
106
		$params = array_merge(DelTk::$default_params, $params);
107
 
107
 
108
		// création des contraintes (masques)
108
		// création des contraintes (masques)
109
		DelTk::sqlAddConstraint($params, $db, $req);
109
		DelTk::sqlAddConstraint($params, $db, $req);
110
		self::sqlAddConstraint($params, $db, $req, $this->conteneur);
110
		self::sqlAddConstraint($params, $db, $req, $this->conteneur);
111
		self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
111
		self::sqlAddMasqueConstraint($params, $db, $req, $this->conteneur);
112
 
112
 
113
		// 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)
114
		$idobs_tab = self::getIdObs($params, $req, $db);
114
		$idobs_tab = self::getIdObs($params, $req, $db);
115
		// 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
116
		$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));
117
 
117
 
118
		if ($idobs) {
118
		if ($idobs) {
119
			$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']);
120
 
120
 
121
			// 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)
122
			// 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
123
			// car tout provient de v_del_image
123
			// car tout provient de v_del_image
124
			$obs_unfmt = self::getInfos($idobs, $db);
124
			$obs_unfmt = self::getInfos($idobs, $db);
125
 
125
 
126
			// 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
127
			$observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
127
			$observations = self::reformateObservation($obs_unfmt, $this->conteneur->getParametre('url_images'));
128
 
128
 
129
			// 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)
130
			// modifie $observations
130
			// modifie $observations
131
			$this->configurer();
131
			$this->configurer();
132
			$this->chargerDeterminations($observations);
132
			$this->chargerDeterminations($observations);
133
 
133
 
134
			// 5) restauration de l'ordre souhaité initialement
134
			// 5) restauration de l'ordre souhaité initialement
135
			$observations = self::sortArrayByArray($observations, $idobs);
135
			$observations = self::sortArrayByArray($observations, $idobs);
136
		} else {
136
		} else {
137
			$observations = array();
137
			$observations = array();
138
			$total = 0;
138
			$total = 0;
139
		}
139
		}
140
 
140
 
141
		// 6) JSON output
141
		// 6) JSON output
142
		$resultat = new ResultatService();
142
		$resultat = new ResultatService();
143
		$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')),
144
			'resultats' => $observations);
144
			'resultats' => $observations);
145
 
145
 
146
		return $resultat;
146
		return $resultat;
147
	}
147
	}
148
 
148
 
149
	static function sortArrayByArray($array, $orderArray) {
149
	static function sortArrayByArray($array, $orderArray) {
150
		$ordered = array();
150
		$ordered = array();
151
		foreach ($orderArray as $key) {
151
		foreach ($orderArray as $key) {
152
			if (array_key_exists('"' . $key . '"', $array)) {
152
			if (array_key_exists('"' . $key . '"', $array)) {
153
				$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
153
				$ordered['"' . $key . '"'] = $array['"' . $key . '"'];
154
				unset($array['"' . $key . '"']);
154
				unset($array['"' . $key . '"']);
155
			}
155
			}
156
		}
156
		}
157
		return $ordered + $array;
157
		return $ordered + $array;
158
	}
158
	}
159
 
159
 
160
	// SQL helpers
160
	// SQL helpers
161
	/*
161
	/*
162
	 * 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
163
	 * 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
164
	 *
164
	 *
165
	 * @param p: $params (filtrés sauf escape-string)
165
	 * @param p: $params (filtrés sauf escape-string)
166
	 * @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
167
	 * @param db: l'instance de db
167
	 * @param db: l'instance de db
168
	 */
168
	 */
169
	static function getIdObs($p, $req, $db) {
169
	static function getIdObs($p, $req, $db) {
170
		$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
170
		$req_s = sprintf('SELECT SQL_CALC_FOUND_ROWS id_observation' .
171
			' FROM v_del_image vdi'.
171
			' FROM v_del_image vdi'.
172
			' %s' . // LEFT JOIN if any
172
			' %s' . // LEFT JOIN if any
173
			' WHERE %s'. // where-clause ou TRUE
173
			' WHERE %s'. // where-clause ou TRUE
174
			' %s'. // group-by
174
			' %s'. // group-by
175
			' %s'. // having (si commentaires)
175
			' %s'. // having (si commentaires)
176
			' ORDER BY %s %s %s'.
176
			' ORDER BY %s %s %s'.
177
			' LIMIT %d, %d -- %s',
177
			' LIMIT %d, %d -- %s',
178
 
178
 
179
			$req['join'] ? implode(' ', $req['join']) : '',
179
			$req['join'] ? implode(' ', $req['join']) : '',
180
			$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
180
			$req['where'] ? implode(' AND ', $req['where']) : 'TRUE',
181
 
181
 
182
			$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
182
			$req['groupby'] ? ('GROUP BY ' . implode(', ', array_unique($req['groupby']))) : '',
183
			$req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
183
			$req['having'] ? ('HAVING ' . implode(' AND ', $req['having'])) : '',
184
 
184
 
185
			$p['tri'], strtoupper($p['ordre']),
185
			$p['tri'], strtoupper($p['ordre']),
186
			// date_transmission peut-être NULL et nous voulons de la consistence
186
			// date_transmission peut-être NULL et nous voulons de la consistence
187
			// (sauf après r1860 de Cel)
187
			// (sauf après r1860 de Cel)
188
			$p['tri'] == 'date_transmission' ? ', id_observation' : '',
188
			$p['tri'] == 'date_transmission' ? ', id_observation' : '',
189
 
189
 
190
			$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
190
			$p['navigation.depart'], $p['navigation.limite'], __FILE__ . ':' . __LINE__);
191
 
191
 
192
		$res = $db->recupererTous($req_s);
192
		$res = $db->recupererTous($req_s);
193
		$err = mysql_error();
193
		$err = mysql_error();
194
		if (!$res && $err) {
194
		if (!$res && $err) {
195
			// http_response_code(400);
195
			// http_response_code(400);
196
			// if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
196
			// if(defined('DEBUG') && DEBUG) header("X-Debug: $req_s");
197
			throw new Exception('not found', 400);
197
			throw new Exception('not found', 400);
198
		}
198
		}
199
		// ordre préservé, à partir d'ici c'est important.
199
		// ordre préservé, à partir d'ici c'est important.
200
		return $res;
200
		return $res;
201
	}
201
	}
202
 
202
 
203
	/**
203
	/**
204
	 * Champs récupérés:
204
	 * Champs récupérés:
205
	 * 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
206
	 * (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})
207
	 * Pour del_commentaires: nous voulons *
207
	 * Pour del_commentaires: nous voulons *
208
	 * Reste ensuite à formatter.
208
	 * Reste ensuite à formatter.
209
	 * 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
210
	 * n'en dépend pour l'heure. (inutilisation de $req['select'])
210
	 * n'en dépend pour l'heure. (inutilisation de $req['select'])
211
	 */
211
	 */
212
	static function getInfos($idobs, $db) {
212
	static function getInfos($idobs, $db) {
213
		/*$select_fields = implode(',', array_merge(
213
		/*$select_fields = implode(',', array_merge(
214
		 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']),
215
		 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']),
216
		 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'])));*/
217
		$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']);
218
		$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
218
		$req_s = sprintf('SELECT %s FROM v_del_image vdi'.
219
			// ' 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'.
220
			' WHERE id_observation IN (%s)',
220
			' WHERE id_observation IN (%s)',
221
			implode(',', $select_fields),
221
			implode(',', $select_fields),
222
			implode(',', $idobs));
222
			implode(',', $idobs));
223
		return $db->recupererTous($req_s);
223
		return $db->recupererTous($req_s);
224
	}
224
	}
225
 
225
 
226
	/**
226
	/**
227
	 * Complément à DelTk::sqlAddConstraint()
227
	 * Complément à DelTk::sqlAddConstraint()
228
	 *
228
	 *
229
	 * @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)
230
	 * @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
231
	 * @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"
232
	 *								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)
233
	 */
233
	 */
234
	static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
234
	static function sqlAddConstraint($p, $db, &$req, Conteneur $c = NULL) {
235
		if (!empty($p['masque.tag'])) {
235
		if (!empty($p['masque.tag'])) {
236
			// 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
237
			// i_mots_cles_texte provient de la VIEW v_del_image
237
			// i_mots_cles_texte provient de la VIEW v_del_image
238
			if (isset($p['masque.tag']['AND'])) {
238
			if (isset($p['masque.tag']['AND'])) {
239
				/* 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()),
240
				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
241
				" 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.
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 */
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 */
243
				$subwhere = array();
243
				$subwhere = array();
244
				foreach ($p['masque.tag']['AND'] as $tag) {
244
				foreach ($p['masque.tag']['AND'] as $tag) {
245
					if (trim($tag) == ',') continue;
245
					if (trim($tag) == ',') continue;
246
 
246
 
247
					$subwhere[] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
247
					$subwhere[] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
248
						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')),
249
						$db->proteger(strtolower($tag)));
249
						$db->proteger(strtolower($tag)));
250
				}
250
				}
251
				$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
251
				$req['where'][] = '(' . implode(' AND ', $subwhere) . ')';
252
			} else {
252
			} else {
253
				$req['where'][] = sprintf('LOWER(CONCAT(%s)) REGEXP %s',
253
				$req['where'][] = 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(implode('|', $p['masque.tag']['OR']))));
255
					$db->proteger(strtolower(implode('|', $p['masque.tag']['OR']))));
256
			}
256
			}
257
		}
257
		}
258
 
258
 
259
		if (!empty($p['masque.type'])) {
259
		if (!empty($p['masque.type'])) {
260
			self::addTypeConstraints($p['masque.type'], $db, $req, $c);
260
			self::addTypeConstraints($p['masque.type'], $db, $req, $c);
261
		}
261
		}
262
	}
262
	}
263
 
263
 
264
	/** 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.
265
	 * 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
266
	 * 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']
267
	 * imploded par des " OR ".
267
	 * imploded par des " OR ".
268
	 */
268
	 */
269
	static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
269
	static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
270
		if (!empty($p['masque'])) {
270
		if (!empty($p['masque'])) {
271
			$or_params = array('masque.auteur' => $p['masque'],
271
			$or_params = array('masque.auteur' => $p['masque'],
272
				'masque.departement' => $p['masque'],
272
				'masque.departement' => $p['masque'],
273
				'masque.id_zone_geo' => $p['masque'],
273
				'masque.id_zone_geo' => $p['masque'],
274
				'masque.tag' => $p['masque'],
274
				'masque.tag' => $p['masque'],
275
				'masque.ns' => $p['masque'],
275
				'masque.ns' => $p['masque'],
276
				'masque.famille' => $p['masque'],
276
				'masque.famille' => $p['masque'],
277
				'masque.date' => $p['masque'],
277
				'masque.date' => $p['masque'],
278
				'masque.genre' => $p['masque'],
278
				'masque.genre' => $p['masque'],
279
				/* milieu: TODO ? */ );
279
				/* milieu: TODO ? */ );
280
			/* 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.
281
			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
282
			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. */
283
			if (isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
283
			if (isset($p['masque.auteur'])) unset($or_params['masque.auteur']);
284
			if (isset($p['masque.departement'])) unset($or_params['masque.departement']);
284
			if (isset($p['masque.departement'])) unset($or_params['masque.departement']);
285
			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']);
286
			if (isset($p['masque.tag'])) unset($or_params['masque.tag']);
286
			if (isset($p['masque.tag'])) unset($or_params['masque.tag']);
287
			if (isset($p['masque.famille'])) unset($or_params['masque.famille']);
287
			if (isset($p['masque.famille'])) unset($or_params['masque.famille']);
288
			if (isset($p['masque.date'])) unset($or_params['masque.date']);
288
			if (isset($p['masque.date'])) unset($or_params['masque.date']);
289
			if (isset($p['masque.genre'])) unset($or_params['masque.genre']);
289
			if (isset($p['masque.genre'])) unset($or_params['masque.genre']);
290
 
290
 
291
 
291
 
292
			$or_masque = DelTk::requestFilterParams($or_params, array_keys($or_params), $c);
292
			$or_masque = DelTk::requestFilterParams($or_params, array_keys($or_params), $c);
293
			if (isset($or_params['masque.tag'])) {
293
			if (isset($or_params['masque.tag'])) {
294
				$or_masque['masque.tag'] = DelTk::buildTagsAST($p['masque'], 'OR', ' ');
294
				$or_masque['masque.tag'] = DelTk::buildTagsAST($p['masque'], 'OR', ' ');
295
			}
295
			}
296
 
296
 
297
			// $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());
298
			$or_req = array('join' => array(), 'where' => array());
298
			$or_req = array('join' => array(), 'where' => array());
299
			DelTk::sqlAddConstraint($or_masque, $db, $or_req);
299
			DelTk::sqlAddConstraint($or_masque, $db, $or_req);
300
			self::sqlAddConstraint($or_masque, $db, $or_req);
300
			self::sqlAddConstraint($or_masque, $db, $or_req);
301
 
301
 
302
			if ($or_req['where']) {
302
			if ($or_req['where']) {
303
				$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
303
				$req['where'][] = '(' . implode(' OR ', $or_req['where']) . ')';
304
				// utile au cas ou des jointures seraient rajoutées
304
				// utile au cas ou des jointures seraient rajoutées
305
				$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
305
				$req['join'] = array_unique(array_merge($req['join'], $or_req['join']));
306
			}
306
			}
307
		}
307
		}
308
	}
308
	}
309
 
309
 
310
	private function configurer() {
310
	private function configurer() {
311
		$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
311
		$this->mappingVotes = $this->conteneur->getParametre('mapping_votes');
312
		$this->mappingCommentaire = $this->conteneur->getParametre('mapping_commentaire');
312
		$this->mappingCommentaire = $this->conteneur->getParametreTableau('commentaires.mapping');
313
	}
313
	}
314
 
314
 
315
	/**
315
	/**
316
	 * @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.
317
	 */
317
	 */
318
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
318
	static function addTypeConstraints($val, $db, &$req, Conteneur $c) {
319
		if (array_key_exists('adeterminer', $val)) {
319
		if (array_key_exists('adeterminer', $val)) {
320
			// 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
321
			// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
321
			// *ou* qui ont la "certitude" à ("aDeterminer" *ou* "douteux")
322
			$req['where'][] = '(' . implode(' OR ', array(
322
			$req['where'][] = '(' . implode(' OR ', array(
323
				'vdi.certitude = "aDeterminer"',
323
				'vdi.certitude = "aDeterminer"',
324
				'vdi.certitude = "douteux"',
324
				'vdi.certitude = "douteux"',
325
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
325
				'vdi.mots_cles_texte LIKE "%aDeterminer%"',
326
				'vdi.nom_sel_nn IS NULL',
326
				'vdi.nom_sel_nn IS NULL',
327
				'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 !!
328
				)) . ')';
328
				)) . ')';
329
		}
329
		}
330
		if (array_key_exists('validees', $val)) {
330
		if (array_key_exists('validees', $val)) {
331
			//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
332
			$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';
333
		}
333
		}
334
 
334
 
335
		// solution n°1: impraticable
335
		// solution n°1: impraticable
336
		if (false && array_key_exists('endiscussion', $val)) {
336
		if (false && array_key_exists('endiscussion', $val)) {
337
			//Si on veut les observations en discussion,
337
			//Si on veut les observations en discussion,
338
			// 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
339
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
339
			$req['select'][] = 'COUNT(dc.id_commentaire) AS comm_count';
340
			$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';
341
			$req['groupby'][] = 'vdi.id_observation';
341
			$req['groupby'][] = 'vdi.id_observation';
342
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
342
			$req['having'][] = "COUNT(id_commentaire) > " . $c->getParametre('nb_commentaires_discussion');
343
		}
343
		}
344
 
344
 
345
		if (array_key_exists('endiscussion', $val)) {
345
		if (array_key_exists('endiscussion', $val)) {
346
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
346
			$req['where'][] = '(SELECT COUNT(id_commentaire) FROM del_commentaire AS dc'.
347
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
347
				' WHERE ce_observation = id_observation) > ' . intval($c->getParametre('nb_commentaires_discussion'));
348
		}
348
		}
349
	}
349
	}
350
 
350
 
351
	/**
351
	/**
352
	 * 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
353
	 * @param array $observations la liste des observations à mettre à jour
353
	 * @param array $observations la liste des observations à mettre à jour
354
	 */
354
	 */
355
	private function chargerDeterminations(&$observations) {
355
	private function chargerDeterminations(&$observations) {
356
		$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));
357
		$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',
358
			implode(',',$idObs),
358
			implode(',',$idObs),
359
			__FILE__ . ':' . __LINE__);
359
			__FILE__ . ':' . __LINE__);
360
		$propositions = $this->bdd->recupererTous($r);
360
		$propositions = $this->bdd->recupererTous($r);
361
		if (!$propositions) return;
361
		if (!$propositions) return;
362
		foreach ($propositions as $proposition) {
362
		foreach ($propositions as $proposition) {
363
			$idObs = $proposition['ce_observation'];
363
			$idObs = $proposition['ce_observation'];
364
			$idComment = $proposition['id_commentaire'];
364
			$idComment = $proposition['id_commentaire'];
365
			$comment = $this->formaterDetermination($idComment, $proposition);
365
			$comment = $this->formaterDetermination($idComment, $proposition);
366
			if ($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
366
			if ($comment) $observations['"' . $idObs . '"']['commentaires'][$idComment] = $comment;
367
 
367
 
368
		}
368
		}
369
	}
369
	}
370
 
370
 
371
	private function formaterDetermination($commentId, $proposition) {
371
	private function formaterDetermination($commentId, $proposition) {
372
		if (!$proposition) return NULL;
372
		if (!$proposition) return NULL;
373
 
373
 
374
		$proposition_formatee = array('nb_commentaires' => '0');
374
		$proposition_formatee = array('nb_commentaires' => '0');
375
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
375
		foreach ($this->mappingCommentaire as $nomOriginal => $nomFinal) {
376
			if (isset($proposition[$nomOriginal])) {
376
			if (isset($proposition[$nomOriginal])) {
377
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
377
				$proposition_formatee[$nomFinal] = $proposition[$nomOriginal];
378
			}
378
			}
379
		}
379
		}
380
 
380
 
381
		// Charger les votes sur les déterminations
381
		// Charger les votes sur les déterminations
382
		$resultatsVotes = $this->bdd->recupererTous(
382
		$resultatsVotes = $this->bdd->recupererTous(
383
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
383
			sprintf('SELECT * FROM del_commentaire_vote WHERE ce_proposition = %d', $commentId));
384
 
384
 
385
		foreach ($resultatsVotes as $vote) {
385
		foreach ($resultatsVotes as $vote) {
386
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
386
			$proposition_formatee['votes'][$vote['id_vote']] = $this->formaterVote($vote);
387
		}
387
		}
388
 
388
 
389
		// chargerNombreCommentaire()
389
		// chargerNombreCommentaire()
390
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
390
		// Charger le nombre de commentaires (sans détermination) associé à l'observation
391
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
391
		$listeCommentaires = $this->bdd->recupererTous(sprintf(
392
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
392
			'SELECT ce_commentaire_parent, ce_proposition, COUNT( id_commentaire ) AS nb '.
393
			'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',
394
			$commentId, __FILE__ . ':' . __LINE__));
394
			$commentId, __FILE__ . ':' . __LINE__));
395
		foreach ($listeCommentaires as $ligneProposition) {
395
		foreach ($listeCommentaires as $ligneProposition) {
396
			// 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
397
			if ($ligneProposition['ce_commentaire_parent']) {
397
			if ($ligneProposition['ce_commentaire_parent']) {
398
				// TODO/debug: id_commentaire_parent != $commentId ??
398
				// TODO/debug: id_commentaire_parent != $commentId ??
399
				// 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, ...
400
				if ($ligneProposition['ce_commentaire_parent'] != $commentId) {
400
				if ($ligneProposition['ce_commentaire_parent'] != $commentId) {
401
					// restore_error_handler();
401
					// restore_error_handler();
402
					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",
403
						$ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
403
						$ligneProposition['nb'], $commentId, $ligneProposition['ce_commentaire_parent'], __FILE__));
404
				}
404
				}
405
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
405
				$proposition_formatee['nb_commentaires'] = $ligneProposition['nb'];
406
			} else {
406
			} else {
407
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
407
				$proposition_formatee['observation']['nb_commentaires'] = $ligneProposition['nb'];
408
			}
408
			}
409
		}
409
		}
410
 
410
 
411
		return $proposition_formatee;
411
		return $proposition_formatee;
412
	}
412
	}
413
 
413
 
414
	/**
414
	/**
415
	 * 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
416
	 * @param $votes array()
416
	 * @param $votes array()
417
	 */
417
	 */
418
	private function formaterVote($vote) {
418
	private function formaterVote($vote) {
419
		$retour = array();
419
		$retour = array();
420
		foreach ($vote as $param=>$valeur) {
420
		foreach ($vote as $param=>$valeur) {
421
			$retour[$this->mappingVotes[$param]] = $valeur;
421
			$retour[$this->mappingVotes[$param]] = $valeur;
422
		}
422
		}
423
		return $retour;
423
		return $retour;
424
	}
424
	}
425
}
425
}