source: spip-zone/_plugins_/prestashop_api/trunk/public/prestashop.php @ 111384

Last change on this file since 111384 was 111384, checked in by cam.lafit, 8 months ago

Fournir un select conditionnel à la ressource Prestashop et non au type DATA

  • Pour toute ressource le type est DATA, le test est sans intéret
  • Ce qu'on souhaite c'est obtenir une select propre à la ressources

recherchée

File size: 12.4 KB
Line 
1<?php
2
3if (!defined('_ECRIRE_INC_VERSION')) return;
4
5include_spip('inc/prestashop_webservice');
6include_spip('inc/prestashop_webservice_utils');
7include_spip('iterateur/data');
8
9/**
10 * Requeteur pour les boucles (prestashop:products)
11 *
12 * @param $boucles Liste des boucles
13 * @param $boucle  La boucle parcourue
14 * @param $id      L'identifiant de la boucle parcourue
15 *
16 **/
17function requeteur_PRESTASHOP_dist(&$boucles, &$boucle, &$id) {
18        $resource = $boucle->type_requete;
19        if ($g = charger_fonction('prestashop', 'iterateur', true)) {
20                $boucles[$id] = $g($boucle, $resource);
21                // from[0] stocke le type de data (products, categories, ...)
22                $boucles[$id]->from[] = $resource;
23        } else {
24                $boucle->type_requete = false;
25                $msg = array('zbug_requeteur_inconnu',
26                        array(
27                                'requeteur' => 'prestashop',
28                                'type' => $resource
29                        ));
30                erreur_squelette($msg, $boucle);
31        }
32}
33
34
35
36
37/**
38 * Creer une boucle sur un iterateur PRESTASHOP
39 * (PRESTASHOP:Products) ...
40 * annonce au compilo les "champs" disponibles
41 **/
42function iterateur_PRESTASHOP_dist($b, $type) {
43        $b->iterateur = 'PRESTASHOP'; # designe la classe d'iterateur
44        $b->show = array(
45                'field' => array(
46                        'cle' => 'STRING',
47                        'valeur' => 'STRING',
48                        #'rechercher' => 'STRING',
49                        '*' => 'ALL' // Champ joker *
50                )
51        );
52        return $b;
53}
54
55
56
57/**
58 * Extension de l'itérateur Data
59 * pour modifier la procedure de selection
60 *
61 **/
62class IterateurPRESTASHOP extends IterateurData {
63
64        /** La 'resource' souhaitée sur le WS de Prestashop. */
65        protected $resource = '';
66
67        /** Liste des langues (id => code) dans Prestashop */
68        protected $langues = [];
69
70        /**
71         * Retourne la 'resource' souhaitée
72         * @return string
73         */
74        public function get_resource() {
75                return $this->resource;
76        }
77
78        /**
79         * Retourne la liste des langues (id => code) de Prestashop.
80         * @return array
81         */
82        public function get_langues() {
83                if (empty($this->langues)) {
84                        $this->langues = prestashop_ws_list_languages();
85                }
86                return $this->langues;
87        }
88
89        /**
90         * Declarer les criteres exceptions
91         * et pouvoir en ajouter au besoin
92         * @return array
93         */
94        public function exception_des_criteres($add = '') {
95                static $exceptions = array('tableau');
96
97                if (!$add) {
98                        return $exceptions;
99                }
100                $exceptions[] = $add;
101        }
102
103
104        /**
105         * Aller chercher les donnees
106         * Surcharge la selection de l'iterateur DATA
107         * puisque nous n'operons pas pareil.
108         *
109         *
110         * @throws Exception
111         * @param  $command
112         * @return void
113         */
114        protected function select($command) {
115
116                $this->resource = strtolower($this->command['from'][0]);
117
118                // on ne garde pas les where vides
119                $this->command['where'] = array_values(array_filter($this->command['where']));
120
121                // demande sortie du cache ou recalculee
122                $cle = $this->creer_cle_cache();
123                if ($cache = $this->use_cache($cle)) {
124                        // attention, il faut recalculer les filtres
125                        // qui sont a supprimer de la boucle
126                        // sinon l'usage du critere {rechercher} meurt en changeant de pagination :)
127                        if ($exceptions = $this->use_cache($cle . '-filtres')) {
128                                foreach ($exceptions as $ex) {
129                                        $this->exception_des_criteres($ex);
130                                }
131                        }
132                        $this->tableau = $cache;
133                } else {
134                        if (!$select = charger_fonction('prestashop_ws_' . $this->get_resource() . '_select', 'inc', true)) {
135                                $select = charger_fonction('prestashop_ws_select', 'inc', true);
136                        }
137                        $this->tableau = $select($this->command, $this);
138
139                        // cache d'une heure par defaut.
140                        $ttl = isset($this->command['datacache']) ? $this->command['datacache'] : 3600;
141
142                        if (is_array($this->tableau) AND $ttl>0) {
143                                $this->cache_set($cle, $ttl);
144                                $this->cache_set($cle.'-filtres', $ttl, $this->exception_des_criteres());
145                        }
146                }
147
148
149                // Si a ce stade on n'a pas de table, il y a un bug
150                if (!is_array($this->tableau)) {
151                        $this->err = true;
152                        spip_log("erreur datasource PRESTASHOP : " .$this->type);
153                }
154
155
156                // tri {par x}
157                if ($this->command['orderby']) {
158                        $this->select_orderby();
159                }
160
161                // grouper les resultats {fusion /x/y/z} ;
162                if ($this->command['groupby']) {
163                        $this->select_groupby();
164                }
165
166                $this->rewind();
167                #var_dump($this->tableau);
168        }
169
170
171        /**
172         * Retourne les donnees en caches
173         * pour la boucle demandees
174         * si elles existent et ne sont
175         * pas perimees
176         *
177         **/
178        protected function use_cache($cle) {
179
180                $cache = $this->cache_get($cle);
181
182                // Time to live
183                if (isset($this->command['datacache'])) {
184                        $ttl = intval($this->command['datacache']);
185                }
186
187                if (
188                        $cache
189                        AND ($cache['time'] + (isset($ttl) ? $ttl : $cache['ttl']) > time())
190                        AND !prestashop_ws_cache_update()
191                ) {
192                        return $cache['data'];
193                }
194
195                return false;
196        }
197
198
199        /**
200         * Cree une cle unique
201         * pour sauvegarder une analyse de donnees
202         * basee sur les criteres de boucle demandes
203         **/
204        protected function creer_cle_cache() {
205                $cle = $this->command;
206                $cle['from'][0] = $this->type;
207                unset($cle['id']); // pas le nom de la boucle
208                $cle = md5(serialize($cle));
209                return $cle;
210        }
211}
212
213
214
215
216/**
217 * Interroge le Webservice Prestashop et retourne ce qui est demandé.
218 *
219 * @param array $command
220 *     Le tableau command de l'iterateur
221 * @param array $iterateur
222 *     L'iterateur complet
223 **/
224function inc_prestashop_ws_select_dist(&$command, $iterateur) {
225        $criteres = $command['where'];
226        $resource = $iterateur->get_resource();
227
228        $query = [
229                'resource' => $resource,
230        ];
231
232        // on peut fournir une liste l'id
233        // ou egalement un critere id=x
234        $ids = array();
235
236        // depuis une liste
237        if (isset($command['liste']) and is_array($command['liste']) and count($command['liste'])) {
238                $ids = $command['liste'];
239        }
240
241
242        // depuis un critere id=x ou {id?}
243        if ($id = prestashop_ws_critere_valeur($criteres, 'id')) {
244                $ids = prestashop_ws_intersect_ids($ids, $id);
245                // pas la peine de filtrer dessus...
246                $iterateur->exception_des_criteres('id');
247                $query['filter[id]'] = '[' . implode('|', $ids) . ']';
248        }
249
250        // liste des champs possibles pour cette ressource
251        // cela permet de renseigner les filtres automatiquement
252        // et réduire ainsi la taille de la requête retournée,
253        // évitant que ce soit l'itérateur data qui filtre après coup.
254        $champs = prestashop_ws_show_resource($resource);
255        foreach ($champs as $champ => $desc) {
256                if ($val = prestashop_ws_critere_valeur($criteres, $champ)) {
257                        // pas la peine de filtrer dessus...
258                        $iterateur->exception_des_criteres($champ);
259                        $query['filter[' . $champ . ']'] = '[' . implode('|', $val) . ']';
260                }
261        }
262
263        // display (full par défaut)
264        if (!$display = prestashop_ws_critere_valeur($criteres, 'display')) {
265                $display = 'full';
266        }
267        $iterateur->exception_des_criteres('display');
268        $query['display'] = $display;
269
270
271        /*
272                Si on met une limite… on ne sait plus paginer
273                car on ne connait pas le nombre total de résultats.
274
275                // si la boucle contient une pagination {pagination 5}
276                // on retrouve les valeurs de position et de pas
277                if (!empty($command['pagination'])) {
278                        list($debut, $nombre) = $command['pagination'];
279                        if (!$debut) $debut = 0;
280                        $query['limit'] = $debut . ',' . $nombre;
281                }
282        */
283
284        try {
285                $lang = !empty($iterateur->info[4]) ? $iterateur->info[4] : null;
286                $wsps = \SPIP\Prestashop\Webservice::getInstanceByLang($lang);
287        } catch (PrestaShopWebserviceException $ex) {
288                spip_log('Erreur Webservice Prestashop : ' . $ex->getMessage(), 'prestashop.' . _LOG_ERREUR);
289                return [];
290        }
291
292        // Demander les données au Prestashop.
293        try {
294                if ($xml = $wsps->get($query)) {
295                        $arbre = prestashop_ws_nettoyer_reception($xml, $resource, $iterateur->get_langues());
296                        return $arbre;
297                }
298        } catch (PrestaShopWebserviceException $ex) {
299                spip_log('Erreur Webservice Prestashop : ' . $ex->getMessage(), 'prestashop.' . _LOG_ERREUR);
300                spip_log('Query : ', 'prestashop.' . _LOG_ERREUR);
301                spip_log($query, 'prestashop.' . _LOG_ERREUR);
302                return [];
303        }
304
305        return [];
306}
307
308/**
309 * Simplifie les données reçues du webservice de prestashop pour les boucles DATA.
310 * Crée des balise multis sur certains contenus.
311 * @param SimpleXML $xml
312 * @param string $resource
313 */
314function prestashop_ws_nettoyer_reception($xml, $resource, $langues) {
315        if (empty($xml->$resource)) {
316                return [];
317        }
318        $arbre = [];
319        foreach ($xml->$resource as $group) {
320                foreach ($group as $element) {
321                        $arbre[] = prestashop_ws_nettoyer_value($element, $langues);
322                }
323                break;
324        }
325        return $arbre;
326}
327
328/**
329 * Crée un tableau à partir du xml retourné par le webservice prestashop.
330 *
331 * On simplifie certaines entrées, notamment celles qui ont une balise language
332 * pour un faire une balise 'multi' SPIP.
333 *
334 * Également on crée un champ 'nn_url' pour les balises 'nn' qui ont l'attribut
335 * 'xlink:href'. Ça pourra toujours servir.
336 *
337 * @param $value
338 * @param $langues
339 * @return array|string
340 */
341function prestashop_ws_nettoyer_value($value, $langues) {
342        if (!count($value->children())) {
343                return (string)$value;
344        } else {
345                if (isset($value->language)) {
346                        $t = [];
347                        foreach ($value->language as $trad) {
348                                if ($text = (string)$trad) {
349                                        $id = (int)$trad['id'];
350                                        $t[$langues[$id]['code']] = $text;
351                                }
352                        }
353                        if ($t) {
354                                $multi = '<multi>';
355                                foreach ($t as $lang => $text) {
356                                        $multi .= '[' . $lang . ']' . (string)$text;
357                                }
358                                $multi .= '</multi>';
359                                return $multi;
360                        } else {
361                                return '';
362                        }
363                } else {
364                        $res = [];
365                        if (isset($value['nodeType'])) {
366                                $type = (string)$value['nodeType'];
367                                $data = [];
368                                foreach ($value->$type as $k => $v) {
369                                        $data = prestashop_ws_nettoyer_value($v, $langues);
370                                        if ($attr = $v->attributes('xlink', true) and !empty($attr['href'])) {
371                                                $data[$k . '_url'] = (string)$attr['href'];
372                                        }
373                                        $res[] = $data;
374                                }
375                        } else {
376                                foreach ($value as $k => $v) {
377                                        $res[$k] = prestashop_ws_nettoyer_value($v, $langues);
378                                        if ($attr = $v->attributes('xlink', true) and !empty($attr['href'])) {
379                                                $res[$k . '_url'] = (string)$attr['href'];
380                                        }
381                                }
382                        }
383                        return $res;
384                }
385        }
386}
387
388/**
389 * Recuperer un critere dans le tableau where selon une contrainte.
390 *
391 * @return array, un element par valeur trouvee
392 **/
393function prestashop_ws_critere_valeur($criteres, $cle, $op = '=') {
394        $res = array();
395        if (!is_array($criteres) OR !$criteres) {
396                return $res;
397        }
398        foreach ($criteres as $c) {
399                if (is_array($c) AND $c[0] == $op AND $c[1] == $cle) {
400                        // enlever les guillemets si presents
401                        $v = $c[2];
402                        if ($v !== 'NULL') {
403                                if (($v[0] == "'") and ($v[ count($v)-1 ] == "'")) {
404                                        $v = substr($v, 1,-1);
405                                }
406                                $res[] = $v;
407                        }
408                        // ((machin IN ('34','TRUC'))) // magnifique :/
409                        // ((look  IN ('PMB','FIRSTACCESS','ALL')))
410                } elseif (is_string($c)) {
411                        // cf iterateurs->calculer_filtres()
412
413                        $op = $c;
414
415                        // traiter {cle IN a,b} ou {valeur !IN a,b}
416                        // prendre en compte le cas particulier de sous-requetes
417                        // produites par sql_in quand plus de 255 valeurs passees a IN
418                        if (preg_match_all(',\s+IN\s+(\(.*\)),', $op, $s_req)) {
419                                $req = '';
420                                foreach($s_req[1] as $key => $val) {
421                                        $req .= trim($val, '(,)') . ',';
422                                }
423                                $req = '(' . rtrim($req, ',') . ')';
424                        }
425                        if (preg_match(',^\(\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)(?:\s+(AND|OR)\s+\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\))*\)$,', $op, $regs)) {
426                                // 1 'look'
427                                // 2 NOT
428                                // 3 ('TRUC','CHOSE')
429                                if ($regs[1] == $cle and !$regs[2]) {
430                                        $v = explode(',', trim($regs[3], ' ()'));
431                                        // enlever tous les guillemets entourants
432                                        foreach($v as $a=>$b) { $v[$a] = trim($b, "'"); }
433                                        // comme c'est deja un tableau, on le merge aux resultats deja obtenus
434                                        $res = array_unique(array_merge($res, $v));
435                                }
436                        }
437
438                }
439        }
440        // on enleve les valeurs vides ''
441        $res = array_filter($res);
442        return $res;
443}
444
445
446/**
447 * Chercher la presence d'un critere dans le tableau where.
448 *
449 * @return bool vrai si critere trouve.
450 **/
451function prestashop_ws_recherche_critere($criteres, $cle) {
452        if (!is_array($criteres) OR !$criteres OR !$cle) {
453                return false;
454        }
455        foreach ($criteres as $c) {
456                // {c}   =>  array('=', 'c', '')
457                // {c=3} =>  array('=', 'c', '3')
458                // {c 3} =>  array('c', '3')
459                if (is_array($c) AND ($c[1] == $cle OR $c[0] == $cle)) {
460                        return true;
461                }
462        }
463        return false;
464}
465
466
467/**
468 * Chercher la valeur d'un parametre dans un critere
469 * {critere un,deux}
470 *
471 * @return mixed, valeur trouvee, sinon null
472 **/
473function prestashop_ws_interprete_argument_critere($criteres, $cle, $index) {
474        if (!is_array($criteres) OR !$criteres) {
475                return null;
476        }
477        foreach ($criteres as $c) {
478                // {c 3} =>  array('c', '3')
479                if (is_array($c) AND ($c[0] == $cle)) {
480                        if (isset($c[$index])) {
481                                return $c[$index];
482                        }
483                }
484        }
485        return null;
486}
487
488
489/**
490 * Retourne l'intersection des ids trouvés.
491 * Équivalent {...} AND {...}
492 */
493function prestashop_ws_intersect_ids($anciens, $nouveaux) {
494        if ($anciens) {
495                return array_intersect($anciens, $nouveaux);
496        }
497        return $nouveaux;
498}
Note: See TracBrowser for help on using the repository browser.