Subversion Repositories eFlore/Applications.del

Rev

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

Rev 1435 Rev 1451
1
<?php
1
<?php
2
/**
2
/**
3
 * Le web service observations récupère toutes les information pour une observation:
3
 * Le web service observations récupère toutes les information pour une observation:
4
 * images, votes sur image et protocole, commentaires, votes sur commentaires, ...
4
 * images, votes sur image et protocole, commentaires, votes sur commentaires, ...
5
 *
5
 *
6
 * @category	php 5.2
6
 * @category	php 5.2
7
 * @author		Raphaël Droz <raphael@tela-botanica.org>
7
 * @author		Raphaël Droz <raphael@tela-botanica.org>
8
 * @copyright	Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
8
 * @copyright	Copyright (c) 2013, Tela Botanica (accueil@tela-botanica.org)
9
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
9
 * @license	http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
10
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
10
 * @license	http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
11
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
11
 * @see http://www.tela-botanica.org/wikini/eflore/wakka.php?wiki=ApiIdentiplante01Observations
12
 *
12
 *
13
 * @config-depend: "url_image" (dans configurations/config_observations.ini)
13
 * @config-depend: "url_image" (dans configurations/config_observations.ini)
14
 *				   ex: http://www.tela-botanica.org/appli:cel-img:%09dXL.jpg
14
 *				   ex: http://www.tela-botanica.org/appli:cel-img:%09dXL.jpg
15
 */
15
 */
16
 
16
 
17
// http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
17
// http://localhost/del/services/0.1/observations/#id => une observation donnée et ses images, SANS LES propositions & nombre de commentaire
18
 
18
 
19
require_once(dirname(__FILE__) . '/ListeObservations2.php');
19
require_once(dirname(__FILE__) . '/ListeObservations.php');
20
 
20
 
21
class Observation {
21
class Observation {
22
 
22
 
23
	/* Map les champs MySQL vers les champs utilisés dans le JSON pour les clients pour
23
	/* Map les champs MySQL vers les champs utilisés dans le JSON pour les clients pour
24
	   chacune des différentes tables utilisées pour le chargement de résultats ci-dessous.
24
	   chacune des différentes tables utilisées pour le chargement de résultats ci-dessous.
25
	   - chargerObservation() (v_del_image)
25
	   - chargerObservation() (v_del_image)
26
	   - chargerVotesImage() (del_image_vote, del_image_protocole)
26
	   - chargerVotesImage() (del_image_vote, del_image_protocole)
27
	   - chargerCommentaires() (del_commentaire_vote, del_commentaire).
27
	   - chargerCommentaires() (del_commentaire_vote, del_commentaire).
28
	   Si la valeur vaut 1, aucun alias ne sera défini (nom du champ d'origine), cf consulter() */
28
	   Si la valeur vaut 1, aucun alias ne sera défini (nom du champ d'origine), cf consulter() */
29
	static $mappings = array(
29
	static $mappings = array(
30
		'observations' => array( // v_del_image
30
		'observations' => array( // v_del_image
31
			"id_observation" => 1,
31
			"id_observation" => 1,
32
			"date_observation" => 1,
32
			"date_observation" => 1,
33
			"date_transmission" => 1, 
33
			"date_transmission" => 1, 
34
			"famille" => "determination.famille",
34
			"famille" => "determination.famille",
35
			"nom_sel" => "determination.ns",
35
			"nom_sel" => "determination.ns",
36
			"nom_sel_nn" => "determination.nn",
36
			"nom_sel_nn" => "determination.nn",
37
			"nom_referentiel" => "determination.referentiel",
37
			"nom_referentiel" => "determination.referentiel",
38
			"nt" => "determination.nt",
38
			"nt" => "determination.nt",
39
			"ce_zone_geo" => "id_zone_geo",
39
			"ce_zone_geo" => "id_zone_geo",
40
			"zone_geo" => 1,
40
			"zone_geo" => 1,
41
			"lieudit" => 1,
41
			"lieudit" => 1,
42
			"station" => 1,
42
			"station" => 1,
43
			"milieu" => 1,
43
			"milieu" => 1,
44
			"ce_utilisateur" => "auteur.id",
44
			"ce_utilisateur" => "auteur.id",
45
			"mots_cles_texte" => "mots_cles_texte",
45
			"mots_cles_texte" => "mots_cles_texte",
46
			"commentaire" => 1),
46
			"commentaire" => 1),
47
		/* exclus car issus de la jointure annuaire:
47
		/* exclus car issus de la jointure annuaire:
48
		   "nom" => "auteur.nom", // XXX: jointure annuaire
48
		   "nom" => "auteur.nom", // XXX: jointure annuaire
49
		   "prenom" => "auteur.prenom",  // XXX: jointure annuaire
49
		   "prenom" => "auteur.prenom",  // XXX: jointure annuaire
50
		   "courriel" => "observateur",  // XXX: jointure annuaire */
50
		   "courriel" => "observateur",  // XXX: jointure annuaire */
51
		/* présents dans cel_obs mais exclus:
51
		/* présents dans cel_obs mais exclus:
52
		   ordre, nom_ret, nom_ret_nn, latitude, longitude, altitude, geodatum
52
		   ordre, nom_ret, nom_ret_nn, latitude, longitude, altitude, geodatum
53
		   transmission, date_creation, date_modificationabondance, certitude, phenologie, code_insee_calcule */
53
		   transmission, date_creation, date_modificationabondance, certitude, phenologie, code_insee_calcule */
54
 
54
 
55
		'images' => array( // v_del_image
55
		'images' => array( // v_del_image
56
			"id_image" => 1,
56
			"id_image" => 1,
57
			"hauteur" => 1,
57
			"hauteur" => 1,
58
			// "largeur" => 1, inutile semble-t-il
58
			// "largeur" => 1, inutile semble-t-il
59
			"date_prise_de_vue" => "date"),
59
			"date_prise_de_vue" => "date"),
60
		/* présents dans cel_images mais exclus:
60
		/* présents dans cel_images mais exclus:
61
		   i_commentaire, nom_original, publiable_eflore, i_mots_cles_texte, i_ordre,
61
		   i_commentaire, nom_original, publiable_eflore, i_mots_cles_texte, i_ordre,
62
		   i_ce_utilisateur, i_prenom_utilisateur, i_nom_utilisateur, i_courriel_utilisateur */
62
		   i_ce_utilisateur, i_prenom_utilisateur, i_nom_utilisateur, i_courriel_utilisateur */
63
 
63
 
64
		'protocoles' => array( // del_image_protocole
64
		'protocoles' => array( // del_image_protocole
65
			"ce_protocole" => "protocole.id",
65
			"ce_protocole" => "protocole.id",
66
			"id_protocole" => "protocole.id",
66
			"id_protocole" => "protocole.id",
67
			"intitule" => "protocole.intitule",
67
			"intitule" => "protocole.intitule",
68
			"descriptif" => "protocole.descriptif",
68
			"descriptif" => "protocole.descriptif",
69
			"tag" => "protocole.tag"),
69
			"tag" => "protocole.tag"),
70
 
70
 
71
		/* See desc del_commentaire_vote & desc del_image_vote;
71
		/* See desc del_commentaire_vote & desc del_image_vote;
72
		   Les deux schémas sont similaires, à l'exception de ce_protocole
72
		   Les deux schémas sont similaires, à l'exception de ce_protocole
73
		   spécifique à del_image_vote et ce_proposition => ce_image */
73
		   spécifique à del_image_vote et ce_proposition => ce_image */
74
		'votes' => array( // del_image_vote et del_commentaire_vote
74
		'votes' => array( // del_image_vote et del_commentaire_vote
75
			"id_vote" => "vote.id",
75
			"id_vote" => "vote.id",
76
			"ce_proposition" => "proposition.id",
76
			"ce_proposition" => "proposition.id",
77
			"ce_image" => "image.id",
77
			"ce_image" => "image.id",
78
			"ce_utilisateur" => "auteur.id", // attention, conflit avec commentaire, cf ci-dessous
78
			"ce_utilisateur" => "auteur.id", // attention, conflit avec commentaire, cf ci-dessous
79
			"valeur" => "vote",
79
			"valeur" => "vote",
80
			"date" => 1, // attention, conflit avec commentaire, cf ci-dessous
80
			"date" => 1, // attention, conflit avec commentaire, cf ci-dessous
81
			// absents du JSON, et pourtant présents dans services/configurations/config_mapping_votes.ini
81
			// absents du JSON, et pourtant présents dans services/configurations/config_mapping_votes.ini
82
			// (nécessiterait une propre jointure sur del_utilisateur)
82
			// (nécessiterait une propre jointure sur del_utilisateur)
83
			/* "nom" => "auteur.nom",
83
			/* "nom" => "auteur.nom",
84
			"prenom" => "auteur.prenom",
84
			"prenom" => "auteur.prenom",
85
			"courriel" => "auteur.courriel" */),
85
			"courriel" => "auteur.courriel" */),
86
 
86
 
87
		'commentaires' => array( // del_commentaire
87
		'commentaires' => array( // del_commentaire
88
			"id_commentaire" => 1,
88
			"id_commentaire" => 1,
89
			"ce_observation" => "observation",
89
			"ce_observation" => "observation",
90
			"ce_proposition" => "proposition",
90
			"ce_proposition" => "proposition",
91
			"ce_commentaire_parent" => "id_parent",
91
			"ce_commentaire_parent" => "id_parent",
92
 
92
 
93
			// les deux alias suivants sont particuliers afin d'éviter un conflit d'alias
93
			// les deux alias suivants sont particuliers afin d'éviter un conflit d'alias
94
			// lors des jointures avec del_commentaire_vote ci-dessus
94
			// lors des jointures avec del_commentaire_vote ci-dessus
95
			// (cf cas particulier dans la boucle de chargerCommentaires())
95
			// (cf cas particulier dans la boucle de chargerCommentaires())
96
			"ce_utilisateur" => "__auteur_com",
96
			"ce_utilisateur" => "__auteur_com",
97
			"date" => "__date_com",
97
			"date" => "__date_com",
98
 
98
 
99
			"texte" => 1,
99
			"texte" => 1,
100
			"utilisateur_nom" => "auteur.nom",
100
			"utilisateur_nom" => "auteur.nom",
101
			"utilisateur_prenom" => "auteur.prenom",
101
			"utilisateur_prenom" => "auteur.prenom",
102
			"utilisateur_courriel" => "auteur.courriel",
102
			"utilisateur_courriel" => "auteur.courriel",
103
			"nom_sel" => 1,
103
			"nom_sel" => 1,
104
			"nom_sel_nn" => 1,
104
			"nom_sel_nn" => 1,
105
			"nom_ret_nn" => 1,
105
			"nom_ret_nn" => 1,
106
			"nom_referentiel" => 1,
106
			"nom_referentiel" => 1,
107
			"proposition_initiale" => 1),
107
			"proposition_initiale" => 1),
108
	);
108
	);
109
 
109
 
110
	
110
	
111
	private $conteneur;
111
	private $conteneur;
112
	private $gestionBdd;
112
	private $gestionBdd;
113
	private $bdd;
113
	private $bdd;
114
	
114
	
115
	public function __construct(Conteneur $conteneur = null) {
115
	public function __construct(Conteneur $conteneur = null) {
116
		$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
116
		$this->conteneur = $conteneur == null ? new Conteneur() : $conteneur;
117
		$this->conteneur->chargerConfiguration('config_votes.ini');
117
		$this->conteneur->chargerConfiguration('config_votes.ini');
118
		$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
118
		$this->conteneur->chargerConfiguration('config_mapping_votes.ini');
119
		$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
119
		$this->conteneur->chargerConfiguration('config_mapping_commentaires.ini');
120
		$this->gestionBdd = $conteneur->getGestionBdd();
120
		$this->gestionBdd = $conteneur->getGestionBdd();
121
		$this->bdd = $this->gestionBdd->getBdd();
121
		$this->bdd = $this->gestionBdd->getBdd();
122
	}
122
	}
123
	
123
	
124
	/**
124
	/**
125
	 * Méthode principale de la classe.
125
	 * Méthode principale de la classe.
126
	 * Lance la récupération des images dans la base et les place dans un objet ResultatService 
126
	 * Lance la récupération des images dans la base et les place dans un objet ResultatService 
127
	 * pour l'afficher.
127
	 * pour l'afficher.
128
	 * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
128
	 * @param array $ressources les ressources situées après l'url de base (ex : http://url/ressource1/ressource2)
129
	 * @param array $parametres les paramètres situés après le ? dans l'url
129
	 * @param array $parametres les paramètres situés après le ? dans l'url
130
	 * */
130
	 * */
131
	public function consulter($ressources, $parametres) {
131
	public function consulter($ressources, $parametres) {
132
		if (!$ressources || count($ressources) != 1 ) {
132
		if (!$ressources || count($ressources) != 1 ) {
133
			throw new Exception("Le service observation accepte un unique identifiant d'observation", RestServeur::HTTP_CODE_ERREUR);
133
			throw new Exception("Le service observation accepte un unique identifiant d'observation", RestServeur::HTTP_CODE_ERREUR);
134
		}
134
		}
135
 
135
 
136
		// initialise les mappings:
136
		// initialise les mappings:
137
		// substitue les valeurs à 1 par le nom de la clef (pas d'alias de champ)
137
		// substitue les valeurs à 1 par le nom de la clef (pas d'alias de champ)
138
		array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
138
		array_walk(self::$mappings['observations'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
139
		array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
139
		array_walk(self::$mappings['images'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
140
		array_walk(self::$mappings['protocoles'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
140
		array_walk(self::$mappings['protocoles'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
141
		array_walk(self::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
141
		array_walk(self::$mappings['votes'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
142
		array_walk(self::$mappings['commentaires'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
142
		array_walk(self::$mappings['commentaires'], create_function('&$val, $k', 'if($val==1) $val = $k;'));
143
 
143
 
144
 
144
 
145
		// Gestion des configuration du script
145
		// Gestion des configuration du script
146
		$idobs = $ressources[0];
146
		$idobs = $ressources[0];
147
		$protocole = isset($parametres['protocole']) && is_numeric($parametres['protocole']) ?intval($parametres['protocole']) : NULL;
147
		$protocole = isset($parametres['protocole']) && is_numeric($parametres['protocole']) ?intval($parametres['protocole']) : NULL;
148
 
148
 
149
		// 1) récupération de l'observation (et de ses images (v_del_image est une vue utilisant des INNER JOIN))
149
		// 1) récupération de l'observation (et de ses images (v_del_image est une vue utilisant des INNER JOIN))
150
		$liaisons = self::chargerObservation($this->bdd, $idobs);
150
		$liaisons = self::chargerObservation($this->bdd, $idobs);
151
	
151
	
152
		if(!$liaisons) {
152
		if(!$liaisons) {
153
			header('HTTP/1.0 404 Not Found');
153
			header('HTTP/1.0 404 Not Found');
154
			// don't die (phpunit)
154
			// don't die (phpunit)
155
			throw(new Exception());
155
			throw(new Exception());
156
		}
156
		}
157
 
157
 
158
		// 2) réassocie les images "à plat" à leur observation (merge)
158
		// 2) réassocie les images "à plat" à leur observation (merge)
159
		// TODO: appliquer le formattage dépendant de la configuration en fin de processus
159
		// TODO: appliquer le formattage dépendant de la configuration en fin de processus
160
		$observations = ListeObservations2::reformateObservationSimpleIndex($liaisons, $this->conteneur->getParametre('url_images'));
160
		$observations = ListeObservations::reformateObservationSimpleIndex($liaisons, $this->conteneur->getParametre('url_images'));
161
		// bien que dans notre cas il n'y est qu'une seule observation, issu de plusieurs images
161
		// bien que dans notre cas il n'y est qu'une seule observation, issu de plusieurs images
162
		// dans $liaisons, $observation est un tableau (cf reformateObservation).
162
		// dans $liaisons, $observation est un tableau (cf reformateObservation).
163
		// Considérons la chose comme telle au cas où le webservice doivent demain demander une paire
163
		// Considérons la chose comme telle au cas où le webservice doivent demain demander une paire
164
		// d'observations (... convergence ListeObservations)
164
		// d'observations (... convergence ListeObservations)
165
 
165
 
166
		$observation = array_pop($observations);
166
		$observation = array_pop($observations);
167
 
167
 
168
		// 3) charge les données de votes et protocoles associés aux images
168
		// 3) charge les données de votes et protocoles associés aux images
169
		if($observation['images']) {
169
		if($observation['images']) {
170
			$votes = self::chargerVotesImage($this->bdd, $observation['images'], $protocole);
170
			$votes = self::chargerVotesImage($this->bdd, $observation['images'], $protocole);
171
			// 3") merge/reformate les données retournées
171
			// 3") merge/reformate les données retournées
172
			self::mapVotesToImages($votes,
172
			self::mapVotesToImages($votes,
173
								   $observation['images']);
173
								   $observation['images']);
174
		}
174
		}
175
 
175
 
176
		// 4) charge les commentaires et les votes associés
176
		// 4) charge les commentaires et les votes associés
177
		// modifie/créé $observation['commentaires']
177
		// modifie/créé $observation['commentaires']
178
		self::chargerCommentaires($this->bdd, $observation);
178
		self::chargerCommentaires($this->bdd, $observation);
179
 
179
 
180
 
180
 
181
		// désindexe le tableau (tel qu'apparement attendu par les applis), c'est une exception
181
		// désindexe le tableau (tel qu'apparement attendu par les applis), c'est une exception
182
		// corriger l'appli cliente pour utiliser les index puis supprimer cette ligne
182
		// corriger l'appli cliente pour utiliser les index puis supprimer cette ligne
183
		$observation['images'] = array_values($observation['images']);
183
		$observation['images'] = array_values($observation['images']);
184
		// autre élément de post-processing: le ce_utilisateur de l'observation non-numeric...
184
		// autre élément de post-processing: le ce_utilisateur de l'observation non-numeric...
185
		if(!is_numeric($observation['auteur.id'])) $observation['auteur.id'] = "0";
185
		if(!is_numeric($observation['auteur.id'])) $observation['auteur.id'] = "0";
186
		if(!isset($observation['auteur.nom'])) $observation['auteur.nom'] = '[inconnu]';
186
		if(!isset($observation['auteur.nom'])) $observation['auteur.nom'] = '[inconnu]';
187
	
187
	
188
 
188
 
189
		if(isset($parametres['justthrow'])) return $observation;
189
		if(isset($parametres['justthrow'])) return $observation;
190
 
190
 
191
		// Mettre en forme le résultat et l'envoyer pour affichage
191
		// Mettre en forme le résultat et l'envoyer pour affichage
192
		$resultat = new ResultatService();
192
		$resultat = new ResultatService();
193
		$resultat->corps = $observation;
193
		$resultat->corps = $observation;
194
		return $resultat;
194
		return $resultat;
195
	}
195
	}
196
 
196
 
197
	static function chargerObservation($db, $idobs) {
197
	static function chargerObservation($db, $idobs) {
198
		// prenom_utilisateur, nom_utilisateur, courriel_utilisateur sont exclus du mapping
198
		// prenom_utilisateur, nom_utilisateur, courriel_utilisateur sont exclus du mapping
199
		// car nous utilisons une construction SQL élaborée pour eux (cf IFNULL ci-dessous)
199
		// car nous utilisons une construction SQL élaborée pour eux (cf IFNULL ci-dessous)
200
		$obs_fields = self::sqlFieldsToAlias(self::$mappings['observations'], NULL, 'dob');
200
		$obs_fields = self::sqlFieldsToAlias(self::$mappings['observations'], NULL, 'dob');
201
 
201
 
202
		$image_fields = self::sqlFieldsToAlias(self::$mappings['images'], NULL, 'dob');
202
		$image_fields = self::sqlFieldsToAlias(self::$mappings['images'], NULL, 'dob');
203
 
203
 
204
		// champs de l'annuaire (del_utilisateur): id_utilisateur prenom, nom, courriel
204
		// champs de l'annuaire (del_utilisateur): id_utilisateur prenom, nom, courriel
205
		$annuaire_fields = implode(', ', array("IFNULL(du.prenom, prenom_utilisateur) AS `auteur.prenom`",
205
		$annuaire_fields = implode(', ', array("IFNULL(du.prenom, prenom_utilisateur) AS `auteur.prenom`",
206
											   "IFNULL(du.nom, nom_utilisateur) AS `auteur.nom`",
206
											   "IFNULL(du.nom, nom_utilisateur) AS `auteur.nom`",
207
											   "IFNULL(du.courriel, courriel_utilisateur) AS observateur"));
207
											   "IFNULL(du.courriel, courriel_utilisateur) AS observateur"));
208
		return $db->recupererTous(sprintf(
208
		return $db->recupererTous(sprintf(
209
			'SELECT %s, %s, %s FROM v_del_image as dob'.
209
			'SELECT %s, %s, %s FROM v_del_image as dob'.
210
			' LEFT JOIN del_utilisateur du ON CAST(du.id_utilisateur AS CHAR) = CAST(dob.ce_utilisateur AS CHAR)'.
210
			' LEFT JOIN del_utilisateur du ON CAST(du.id_utilisateur AS CHAR) = CAST(dob.ce_utilisateur AS CHAR)'.
211
			' WHERE dob.id_observation = %d -- %s',
211
			' WHERE dob.id_observation = %d -- %s',
212
			$obs_fields, $image_fields, $annuaire_fields, $idobs, __FILE__ . ':' . __LINE__));
212
			$obs_fields, $image_fields, $annuaire_fields, $idobs, __FILE__ . ':' . __LINE__));
213
	}
213
	}
214
 
214
 
215
	
215
	
216
	// Charger les images et leurs votes associés
216
	// Charger les images et leurs votes associés
217
	static function chargerVotesImage($db, $images, $protocole = NULL) {
217
	static function chargerVotesImage($db, $images, $protocole = NULL) {
218
		if(!$images) return NULL;
218
		if(!$images) return NULL;
219
 
219
 
220
 		$select = array('votes' =>
220
 		$select = array('votes' =>
221
						array('id_vote', 'ce_image', 'ce_protocole', 'ce_utilisateur', 'valeur', 'date', /* del_image_vote */),
221
						array('id_vote', 'ce_image', 'ce_protocole', 'ce_utilisateur', 'valeur', 'date', /* del_image_vote */),
222
						'protocole' => 
222
						'protocole' => 
223
						array('id_protocole', 'intitule', 'descriptif', 'tag' /* del_image_protocole */ ));
223
						array('id_protocole', 'intitule', 'descriptif', 'tag' /* del_image_protocole */ ));
224
		$vote_fields = self::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'v'); // "v": cf alias dans la requête
224
		$vote_fields = self::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'v'); // "v": cf alias dans la requête
225
		$proto_fields = self::sqlFieldsToAlias(self::$mappings['protocoles'], $select['protocole'], 'p');
225
		$proto_fields = self::sqlFieldsToAlias(self::$mappings['protocoles'], $select['protocole'], 'p');
226
		
226
		
227
		$where = array();
227
		$where = array();
228
		$where[] = sprintf('v.ce_image IN (%s)',
228
		$where[] = sprintf('v.ce_image IN (%s)',
229
						   implode(',', array_values(array_map(create_function('$a', 'return $a["id_image"];'), $images))));
229
						   implode(',', array_values(array_map(create_function('$a', 'return $a["id_image"];'), $images))));
230
 
230
 
231
		if ($protocole) {
231
		if ($protocole) {
232
			$where[] = "v.ce_protocole = $protocole";
232
			$where[] = "v.ce_protocole = $protocole";
233
		}
233
		}
234
 
234
 
235
		return $db->recupererTous(sprintf(
235
		return $db->recupererTous(sprintf(
236
			'SELECT %s, %s FROM del_image_vote AS v'.
236
			'SELECT %s, %s FROM del_image_vote AS v'.
237
			' INNER JOIN del_image_protocole p ON v.ce_protocole = p.id_protocole'.
237
			' INNER JOIN del_image_protocole p ON v.ce_protocole = p.id_protocole'.
238
			' WHERE %s -- %s',
238
			' WHERE %s -- %s',
239
			$vote_fields, $proto_fields,
239
			$vote_fields, $proto_fields,
240
			$where ? implode(' AND ', $where) : 1,
240
			$where ? implode(' AND ', $where) : 1,
241
			__FILE__ . ':' . __LINE__));
241
			__FILE__ . ':' . __LINE__));
242
	}
242
	}
243
	
243
	
244
	/**
244
	/**
245
	 * Formater une observation depuis une ligne liaison
245
	 * Formater une observation depuis une ligne liaison
246
	 * @param $liaison liaison issue de la recherche
246
	 * @param $liaison liaison issue de la recherche
247
	 * @return $observation l'observation mise en forme
247
	 * @return $observation l'observation mise en forme
248
	 * Exemple: vote, au sortir de MySQL contient:
248
	 * Exemple: vote, au sortir de MySQL contient:
249
	 * 'xxx' => 'blah', 'descriptif' => 'foo', 'valeur' => 3
249
	 * 'xxx' => 'blah', 'descriptif' => 'foo', 'valeur' => 3
250
	 * et le tableau de mapping contient:
250
	 * et le tableau de mapping contient:
251
	 * descriptif = protocole.descriptif, valeur = vote
251
	 * descriptif = protocole.descriptif, valeur = vote
252
	 * Alors $retour[ contient:
252
	 * Alors $retour[ contient:
253
	 *
253
	 *
254
	 * */
254
	 * */
255
	static function mapVotesToImages($votes, &$images) {
255
	static function mapVotesToImages($votes, &$images) {
256
		if(!$votes) return;
256
		if(!$votes) return;
257
 
257
 
258
		// pour chaque vote
258
		// pour chaque vote
259
		foreach ($votes as $vote) {
259
		foreach ($votes as $vote) {
260
			$imgid = $vote['image.id'];
260
			$imgid = $vote['image.id'];
261
			$protoid = $vote['protocole.id'];
261
			$protoid = $vote['protocole.id'];
262
			
262
			
263
			// un vote sans image associée ? est-ce possible ?
263
			// un vote sans image associée ? est-ce possible ?
264
			// if(!isset($images[$imgid])) continue;
264
			// if(!isset($images[$imgid])) continue;
265
			
265
			
266
			if(!array_key_exists('protocoles_votes', $images[$imgid]) ||
266
			if(!array_key_exists('protocoles_votes', $images[$imgid]) ||
267
			   !array_key_exists($protoid, $images[$imgid]['protocoles_votes'])) {
267
			   !array_key_exists($protoid, $images[$imgid]['protocoles_votes'])) {
268
				// extrait les champs spécifique au protocole (le LEFT JOIN de chargerVotesImage les ramène en doublons
268
				// extrait les champs spécifique au protocole (le LEFT JOIN de chargerVotesImage les ramène en doublons
269
				$protocole = array_intersect_key($vote, array_flip(self::$mappings['protocoles']));
269
				$protocole = array_intersect_key($vote, array_flip(self::$mappings['protocoles']));
270
				$images[$imgid]['protocoles_votes'][$protoid] = $protocole;
270
				$images[$imgid]['protocoles_votes'][$protoid] = $protocole;
271
			}
271
			}
272
 
272
 
273
			$vote = array_intersect_key($vote, array_flip(self::$mappings['votes']));
273
			$vote = array_intersect_key($vote, array_flip(self::$mappings['votes']));
274
			$images[$imgid]['protocoles_votes'][$protoid]['votes'][$vote['vote.id']] = $vote;
274
			$images[$imgid]['protocoles_votes'][$protoid]['votes'][$vote['vote.id']] = $vote;
275
		}
275
		}
276
	}
276
	}
277
 
277
 
278
	// Charger les commentaires et leurs votes associés
278
	// Charger les commentaires et leurs votes associés
279
	static function chargerCommentaires($db, &$observation) {
279
	static function chargerCommentaires($db, &$observation) {
280
		$select = array('votes' =>
280
		$select = array('votes' =>
281
						array('id_vote', 'ce_proposition', 'ce_utilisateur', 'valeur', 'date' /* del_commentaire_vote */),
281
						array('id_vote', 'ce_proposition', 'ce_utilisateur', 'valeur', 'date' /* del_commentaire_vote */),
282
						'commentaires' =>
282
						'commentaires' =>
283
						array('id_commentaire', 'ce_observation', 'ce_proposition', 'ce_commentaire_parent', 'texte',
283
						array('id_commentaire', 'ce_observation', 'ce_proposition', 'ce_commentaire_parent', 'texte',
284
							  'ce_utilisateur', 'utilisateur_prenom', 'utilisateur_nom', 'utilisateur_courriel',
284
							  'ce_utilisateur', 'utilisateur_prenom', 'utilisateur_nom', 'utilisateur_courriel',
285
							  'nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nt', 'famille', 'nom_referentiel', 'date',
285
							  'nom_sel', 'nom_sel_nn', 'nom_ret', 'nom_ret_nn', 'nt', 'famille', 'nom_referentiel', 'date',
286
							  'proposition_initiale'));
286
							  'proposition_initiale'));
287
		$vote_fields = self::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'cv'); // "v": cf alias dans la requête
287
		$vote_fields = self::sqlFieldsToAlias(self::$mappings['votes'], $select['votes'], 'cv'); // "v": cf alias dans la requête
288
		$comment_fields = self::sqlFieldsToAlias(self::$mappings['commentaires'], $select['commentaires'], 'dc');
288
		$comment_fields = self::sqlFieldsToAlias(self::$mappings['commentaires'], $select['commentaires'], 'dc');
289
 
289
 
290
		$commentaires = $db->recupererTous(sprintf(
290
		$commentaires = $db->recupererTous(sprintf(
291
			"SELECT %s, %s FROM del_commentaire as dc".
291
			"SELECT %s, %s FROM del_commentaire as dc".
292
			// LEFT JOIN optionnel, mais explicatif:
292
			// LEFT JOIN optionnel, mais explicatif:
293
			// on ne récupère des infos de vote que pour les commentaires comportant un
293
			// on ne récupère des infos de vote que pour les commentaires comportant un
294
			// nom_sel "valide"
294
			// nom_sel "valide"
295
			" LEFT JOIN del_commentaire_vote cv".
295
			" LEFT JOIN del_commentaire_vote cv".
296
			" ON cv.ce_proposition = dc.id_commentaire AND dc.nom_sel != '' AND dc.nom_sel IS NOT NULL".
296
			" ON cv.ce_proposition = dc.id_commentaire AND dc.nom_sel != '' AND dc.nom_sel IS NOT NULL".
297
			" WHERE ce_observation = %d -- %s",
297
			" WHERE ce_observation = %d -- %s",
298
			$comment_fields, $vote_fields,
298
			$comment_fields, $vote_fields,
299
			$observation['id_observation'],
299
			$observation['id_observation'],
300
			__FILE__ . ':' . __LINE__));
300
			__FILE__ . ':' . __LINE__));
301
 
301
 
302
		if(!$commentaires) return;
302
		if(!$commentaires) return;
303
 
303
 
304
		// les commentaires réunifiées et dont les votes sont mergés
304
		// les commentaires réunifiées et dont les votes sont mergés
305
		$ret = array();
305
		$ret = array();
306
		foreach ($commentaires as $comment) {
306
		foreach ($commentaires as $comment) {
307
			$commentid = $comment['id_commentaire'];
307
			$commentid = $comment['id_commentaire'];
308
			$voteid = $comment['vote.id'];
308
			$voteid = $comment['vote.id'];
309
 
309
 
310
			if(!array_key_exists($commentid, $ret)) {
310
			if(!array_key_exists($commentid, $ret)) {
311
				$comment_extract = array_intersect_key($comment, array_flip(self::$mappings['commentaires']));
311
				$comment_extract = array_intersect_key($comment, array_flip(self::$mappings['commentaires']));
312
				// cas particulier: conflit d'aliases avec del_commentaire_vote
312
				// cas particulier: conflit d'aliases avec del_commentaire_vote
313
				$comment_extract['auteur.id'] = $comment_extract['__auteur_com'];
313
				$comment_extract['auteur.id'] = $comment_extract['__auteur_com'];
314
				$comment_extract['date'] = $comment_extract['__date_com'];
314
				$comment_extract['date'] = $comment_extract['__date_com'];
315
				unset($comment_extract['__auteur_com'], $comment_extract['__date_com']);
315
				unset($comment_extract['__auteur_com'], $comment_extract['__date_com']);
316
 
316
 
317
				// toujours un éléments "votes", quand bien même il n'y en aurait pas
317
				// toujours un éléments "votes", quand bien même il n'y en aurait pas
318
				$comment_extract['votes'] = array();
318
				$comment_extract['votes'] = array();
319
				$ret[$commentid] = $comment_extract;
319
				$ret[$commentid] = $comment_extract;
320
			}
320
			}
321
 
321
 
322
			if(!$comment['nom_sel'] || ! $voteid) continue;
322
			if(!$comment['nom_sel'] || ! $voteid) continue;
323
			$vote = array_intersect_key($comment, array_flip(self::$mappings['votes']));
323
			$vote = array_intersect_key($comment, array_flip(self::$mappings['votes']));
324
			$ret[$commentid]['votes'][$voteid] = $vote;
324
			$ret[$commentid]['votes'][$voteid] = $vote;
325
		}
325
		}
326
		$observation['commentaires'] = $ret;
326
		$observation['commentaires'] = $ret;
327
	}
327
	}
328
 
328
 
329
 
329
 
330
	/* SQL helper
330
	/* SQL helper
331
	   Converti un tableau associatif et un préfix optionnel en une chaîne de champs adéquate
331
	   Converti un tableau associatif et un préfix optionnel en une chaîne de champs adéquate
332
	   à un SELECT MySQL.
332
	   à un SELECT MySQL.
333
	   $select (optionnel) restreint les champs mappés aux valeurs de $select.
333
	   $select (optionnel) restreint les champs mappés aux valeurs de $select.
334
	   Si $select n'est pas fourni, toutes les clefs présentes dans $map seront présentes dans
334
	   Si $select n'est pas fourni, toutes les clefs présentes dans $map seront présentes dans
335
	   le SELECT en sortie */
335
	   le SELECT en sortie */
336
	static function sqlFieldsToAlias($map, $select = NULL, $prefix = NULL) {
336
	static function sqlFieldsToAlias($map, $select = NULL, $prefix = NULL) {
337
		if($select) $arr = array_intersect_key($map, array_flip($select));
337
		if($select) $arr = array_intersect_key($map, array_flip($select));
338
		else $arr = $map;
338
		else $arr = $map;
339
		$keys = array_keys($arr);
339
		$keys = array_keys($arr);
340
 
340
 
341
		if($prefix) array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
341
		if($prefix) array_walk($keys, create_function('&$val, $k, $prefix', '$val = sprintf("%s.`%s`", $prefix, $val);'), $prefix);
342
		else array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
342
		else array_walk($keys, create_function('&$val, $k', '$val = sprintf("`%s`", $val);'));
343
 
343
 
344
		return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
344
		return implode(', ', array_map(create_function('$v, $k', 'return sprintf("%s AS `%s`", $k, $v);'), $arr, $keys));
345
	}
345
	}
346
	
346
	
347
}
347
}