Subversion Repositories eFlore/Applications.del

Rev

Rev 1505 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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