You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

462 lines
11 KiB

<?php
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/prestashop_webservice');
include_spip('inc/prestashop_webservice_utils');
include_spip('iterateur/data');
/**
* Requeteur pour les boucles (prestashop:products)
*
* @param $boucles Liste des boucles
* @param $boucle La boucle parcourue
* @param $id L'identifiant de la boucle parcourue
*
**/
function requeteur_PRESTASHOP_dist(&$boucles, &$boucle, &$id) {
$resource = $boucle->type_requete;
if ($g = charger_fonction('prestashop', 'iterateur', true)) {
$boucles[$id] = $g($boucle, $resource);
// from[0] stocke le type de data (products, categories, ...)
$boucles[$id]->from[] = $resource;
} else {
$boucle->type_requete = false;
$msg = array('zbug_requeteur_inconnu',
array(
'requeteur' => 'prestashop',
'type' => $resource
));
erreur_squelette($msg, $boucle);
}
}
/**
* Creer une boucle sur un iterateur PRESTASHOP
* (PRESTASHOP:Products) ...
* annonce au compilo les "champs" disponibles
**/
function iterateur_PRESTASHOP_dist($b, $type) {
$b->iterateur = 'PRESTASHOP'; # designe la classe d'iterateur
$b->show = array(
'field' => array(
'cle' => 'STRING',
'valeur' => 'STRING',
#'rechercher' => 'STRING',
'*' => 'ALL' // Champ joker *
)
);
return $b;
}
/**
* Extension de l'itérateur Data
* pour modifier la procedure de selection
*
**/
class IterateurPRESTASHOP extends IterateurData {
/** La 'resource' souhaitée sur le WS de Prestashop. */
protected $resource = '';
/** Liste des langues (id => code) dans Prestashop */
protected $langues = [];
/**
* Retourne la 'resource' souhaitée
* @return string
*/
public function get_resource() {
return $this->resource;
}
/**
* Retourne la liste des langues (id => code) de Prestashop.
* @return array
*/
public function get_langues() {
if (empty($this->langues)) {
$this->langues = prestashop_ws_list_languages();
}
return $this->langues;
}
/**
* Declarer les criteres exceptions
* et pouvoir en ajouter au besoin
* @return array
*/
public function exception_des_criteres($add = '') {
static $exceptions = array('tableau');
if (!$add) {
return $exceptions;
}
$exceptions[] = $add;
}
/**
* Aller chercher les donnees
* Surcharge la selection de l'iterateur DATA
* puisque nous n'operons pas pareil.
*
*
* @throws Exception
* @param $command
* @return void
*/
protected function select($command) {
$this->resource = strtolower($this->command['from'][0]);
// on ne garde pas les where vides
$this->command['where'] = array_values(array_filter($this->command['where']));
// demande sortie du cache ou recalculee
$cle = $this->creer_cle_cache();
if ($cache = $this->use_cache($cle)) {
// attention, il faut recalculer les filtres
// qui sont a supprimer de la boucle
// sinon l'usage du critere {rechercher} meurt en changeant de pagination :)
if ($exceptions = $this->use_cache($cle . '-filtres')) {
foreach ($exceptions as $ex) {
$this->exception_des_criteres($ex);
}
}
$this->tableau = $cache;
} else {
if (!$select = charger_fonction('prestashop_ws_' . $this->type . '_select', 'inc', true)) {
$select = charger_fonction('prestashop_ws_select', 'inc', true);
}
$this->tableau = $select($this->command, $this);
// cache d'une heure par defaut.
$ttl = isset($this->command['datacache']) ? $this->command['datacache'] : 3600;
if (is_array($this->tableau) AND $ttl>0) {
$this->cache_set($cle, $ttl);
$this->cache_set($cle.'-filtres', $ttl, $this->exception_des_criteres());
}
}
// Si a ce stade on n'a pas de table, il y a un bug
if (!is_array($this->tableau)) {
$this->err = true;
spip_log("erreur datasource PRESTASHOP : " .$this->type);
}
// tri {par x}
if ($this->command['orderby']) {
$this->select_orderby();
}
// grouper les resultats {fusion /x/y/z} ;
if ($this->command['groupby']) {
$this->select_groupby();
}
$this->rewind();
#var_dump($this->tableau);
}
/**
* Retourne les donnees en caches
* pour la boucle demandees
* si elles existent et ne sont
* pas perimees
*
**/
protected function use_cache($cle) {
$cache = $this->cache_get($cle);
// Time to live
if (isset($this->command['datacache'])) {
$ttl = intval($this->command['datacache']);
}
if ($cache AND ($cache['time'] + (isset($ttl) ? $ttl : $cache['ttl']) > time())
AND !(_request('var_mode') === 'recalcul' AND include_spip('inc/autoriser') AND autoriser('recalcul'))) {
return $cache['data'];
}
return false;
}
/**
* Cree une cle unique
* pour sauvegarder une analyse de donnees
* basee sur les criteres de boucle demandes
**/
protected function creer_cle_cache() {
$cle = $this->command;
$cle['from'][0] = $this->type;
unset($cle['id']); // pas le nom de la boucle
$cle = md5(serialize($cle));
return $cle;
}
}
/**
* Interroge le Webservice Prestashop et retourne ce qui est demandé.
*
* @param array $command
* Le tableau command de l'iterateur
* @param array $iterateur
* L'iterateur complet
**/
function inc_prestashop_ws_select_dist(&$command, $iterateur) {
$criteres = $command['where'];
$resource = $iterateur->get_resource();
$query = [
'resource' => $resource,
];
// on peut fournir une liste l'id
// ou egalement un critere id=x
$ids = array();
// depuis une liste
if (isset($command['liste']) and is_array($command['liste']) and count($command['liste'])) {
$ids = $command['liste'];
}
// depuis un critere id=x ou {id?}
if ($id = prestashop_ws_critere_valeur($criteres, 'id')) {
$ids = prestashop_ws_intersect_ids($ids, $id);
// pas la peine de filtrer dessus...
$iterateur->exception_des_criteres('id');
$query['filter[id]'] = '[' . implode('|', $ids) . ']';
}
// display (full par défaut)
if (!$display = prestashop_ws_critere_valeur($criteres, 'display')) {
$display = 'full';
}
$iterateur->exception_des_criteres('display');
$query['display'] = $display;
/*
Si on met une limite… on ne sait plus paginer
car on ne connait pas le nombre total de résultats.
// si la boucle contient une pagination {pagination 5}
// on retrouve les valeurs de position et de pas
if (!empty($command['pagination'])) {
list($debut, $nombre) = $command['pagination'];
if (!$debut) $debut = 0;
$query['limit'] = $debut . ',' . $nombre;
}
*/
try {
$lang = !empty($iterateur->info[4]) ? $iterateur->info[4] : null;
$wsps = \SPIP\Prestashop\Webservice::getInstanceByLang($lang);
} catch (PrestaShopWebserviceException $ex) {
spip_log('Erreur Webservice Prestashop : ' . $ex->getMessage());
return [];
}
// Demander les données au Prestashop.
if ($xml = $wsps->get($query)) {
$arbre = prestashop_ws_nettoyer_reception($xml, $resource, $iterateur->get_langues());
return $arbre;
}
return [];
}
/**
* Simplifie les données reçues du webservice de prestashop pour les boucles DATA.
* Crée des balise multis sur certains contenus.
* @param SimpleXML $xml
* @param string $resource
*/
function prestashop_ws_nettoyer_reception($xml, $resource, $langues) {
if (empty($xml->$resource)) {
return [];
}
$arbre = [];
foreach ($xml->$resource as $group) {
foreach ($group as $element) {
$arbre[] = prestashop_ws_nettoyer_value($element, $langues);
}
break;
}
return $arbre;
}
/**
* Crée un tableau à partir du xml retourné par le webservice prestashop.
*
* On simplifie certaines entrées, notamment celles qui ont une balise language
* pour un faire une balise 'multi' SPIP.
*
* Également on crée un champ 'nn_url' pour les balises 'nn' qui ont l'attribut
* 'xlink:href'. Ça pourra toujours servir.
*
* @param $value
* @param $langues
* @return array|string
*/
function prestashop_ws_nettoyer_value($value, $langues) {
if (!count($value->children())) {
return (string)$value;
} else {
if (isset($value->language)) {
$t = [];
foreach ($value->language as $trad) {
if ($text = (string)$trad) {
$id = (int)$trad['id'];
$t[$langues[$id]['code']] = $text;
}
}
if ($t) {
$multi = '<multi>';
foreach ($t as $lang => $text) {
$multi .= '[' . $lang . ']' . (string)$text;
}
$multi .= '</multi>';
return $multi;
} else {
return '';
}
} else {
$res = [];
foreach ($value as $k => $v) {
$res[$k] = prestashop_ws_nettoyer_value($v, $langues);
if ($attr = $v->attributes('xlink', true) and !empty($attr['href'])) {
$res[$k . '_url'] = (string)$attr['href'];
}
}
return $res;
}
}
}
/**
* Recuperer un critere dans le tableau where selon une contrainte.
*
* @return array, un element par valeur trouvee
**/
function prestashop_ws_critere_valeur($criteres, $cle, $op = '=') {
$res = array();
if (!is_array($criteres) OR !$criteres) {
return $res;
}
foreach ($criteres as $c) {
if (is_array($c) AND $c[0] == $op AND $c[1] == $cle) {
// enlever les guillemets si presents
$v = $c[2];
if ($v !== 'NULL') {
if (($v[0] == "'") and ($v[ count($v)-1 ] == "'")) {
$v = substr($v, 1,-1);
}
$res[] = $v;
}
// ((machin IN ('34','TRUC'))) // magnifique :/
// ((look IN ('PMB','FIRSTACCESS','ALL')))
} elseif (is_string($c)) {
// cf iterateurs->calculer_filtres()
$op = $c;
// traiter {cle IN a,b} ou {valeur !IN a,b}
// prendre en compte le cas particulier de sous-requetes
// produites par sql_in quand plus de 255 valeurs passees a IN
if (preg_match_all(',\s+IN\s+(\(.*\)),', $op, $s_req)) {
$req = '';
foreach($s_req[1] as $key => $val) {
$req .= trim($val, '(,)') . ',';
}
$req = '(' . rtrim($req, ',') . ')';
}
if (preg_match(',^\(\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\)(?:\s+(AND|OR)\s+\(([\w/]+)(\s+NOT)?\s+IN\s+(\(.*\))\))*\)$,', $op, $regs)) {
// 1 'look'
// 2 NOT
// 3 ('TRUC','CHOSE')
if ($regs[1] == $cle and !$regs[2]) {
$v = explode(',', trim($regs[3], ' ()'));
// enlever tous les guillemets entourants
foreach($v as $a=>$b) { $v[$a] = trim($b, "'"); }
// comme c'est deja un tableau, on le merge aux resultats deja obtenus
$res = array_unique(array_merge($res, $v));
}
}
}
}
// on enleve les valeurs vides ''
$res = array_filter($res);
return $res;
}
/**
* Chercher la presence d'un critere dans le tableau where.
*
* @return bool vrai si critere trouve.
**/
function prestashop_ws_recherche_critere($criteres, $cle) {
if (!is_array($criteres) OR !$criteres OR !$cle) {
return false;
}
foreach ($criteres as $c) {
// {c} => array('=', 'c', '')
// {c=3} => array('=', 'c', '3')
// {c 3} => array('c', '3')
if (is_array($c) AND ($c[1] == $cle OR $c[0] == $cle)) {
return true;
}
}
return false;
}
/**
* Chercher la valeur d'un parametre dans un critere
* {critere un,deux}
*
* @return mixed, valeur trouvee, sinon null
**/
function prestashop_ws_interprete_argument_critere($criteres, $cle, $index) {
if (!is_array($criteres) OR !$criteres) {
return null;
}
foreach ($criteres as $c) {
// {c 3} => array('c', '3')
if (is_array($c) AND ($c[0] == $cle)) {
if (isset($c[$index])) {
return $c[$index];
}
}
}
return null;
}
/**
* Retourne l'intersection des ids trouvés.
* Équivalent {...} AND {...}
*/
function prestashop_ws_intersect_ids($anciens, $nouveaux) {
if ($anciens) {
return array_intersect($anciens, $nouveaux);
}
return $nouveaux;
}