Browse Source

Grandes modifications du plugin d'API Prestashop notamment pour avoir des boucles beaucoup plus simples à écrire.

Un itérateur spécifique est créé qui étend l'itérateur DATA et va interroger le Webservice.
On peut ainsi écrire `<BOUCLE_produits(PRESTASHOP:PRODUCTS){id IN 3,4,5}>#NAME</BOUCLE_produuits>`
N'importe quel élément de l'API de webservice peut être utilisée de la sorte. Par exemple `(PRESTASHOP:CATEGORIES)`

Quelques détails techniques
==========================

Langues des boutiques
--------------------

Notre utilisation est basée sur un Prestashop en multi-boutique (chaque boutique a une URL sur un domaine dans une certaine langue (domaine.fr/boutique/ et domaine.en/shop/ par exemple).
Dans la configuration, on renseigne une des URLs avec la clé d'API (avec des droits de lectures suffisants). À partir de là, le plugin saura retrouver / calculer les liens
corrects pour chaque produit pour une langue voulue (la langue en cours du squelette ou du PHP par défaut) et intérrogera l'API en utilisant le bon domaine en fonction de la langue.

L'ennuyant c'est que les produits qui sont traduits en différentes langues et partagés donc entre les boutiques renvoient via l'API de Webservice une sorte de XML équivalent à nos 
balises multis, MAIS au lieu d'avoir un code de langue, on a un identifiant numérique de langue. Il faut appeler le webservice sur api/languages pour avoir la description de cet identifiant.
Mais api/languages ne renvoie que l'identifiant de la langue active de la boutique, ce qui fait que si ce «multi» retourne un texte dans la langue 3, possiblement je ne sais pas à quoi correspond ce 3.
Pour parer à ce problème (en multi-boutique / langue donc), on calcule pour chaque boutique les langues qu'il contient et on crée et sauvegarde un tableau de relation (identifiant de langue, 
url de la boutique, code de langue). C'est ce que font les fonctions prestashop_ws_list_shops_by_lang() prestashop_ws_list_shops() et prestashop_ws_list_languages().

Afin d'éviter de requêter abusivement le Webservice, on met en cache ces infos, en utilisant Memoization.


Interroger le webservice en PHP
-------------------------------

L'itérateur qui sert au boucles utilise en interne un appel à la classe \SPIP\Prestashop\Webservice, qui surcharge la librairie PSWebServiceLibrary que propose 
Prestashop par défaut.

Il y a plusieurs moyens pour utiliser la classe, le plus simple étant :
```
$wsps = \SPIP\Prestashop\Webservice::getInstanceByLang(); // langue automatique
// ou $wsps = \SPIP\Prestashop\Webservice::getInstanceByLang('fr'); // langue fr (si trouvée)

$xml = $wsps->get(['resource' => 'products']);
// éventuellement avoir un arbre nettoyé / simplifié (c'est ce que fait l'itérateur)
$arbre = prestashop_ws_nettoyer_reception($xml, 'products', prestashop_ws_list_languages());
```


Balises Spécifiques
-------------------

Dans les Squelettes, chaque nom de balise du XML devient une balise utilisable dans la boucle.
Par exemple l'appel à l'API 'api/products/1?display=full' (display=full est utilisé par défaut sur les boucles)
retourne entre autres une balise XML `<date_add> ... </date_add>`. 
Du coup, une boucle `(PRESTASHOP:PRODUCTS)` pourra utiliser `#DATE_ADD` qui retourne le texte de cette balise.
Les balises XML qui contiennent d'autres balises, elles retourneront un tableau des ces balises.
À l'exception de la balise `<languages>` qui est l'équivalent de notre balise `<multi>` qui est applatie automatiquement
et transformée en balise `<multi>` comme SPIP en a l'habitude. Ainsi `[(#NAME|typo)]` affiche le nom du produit
dans la langue en cours (pour peu que le produit soit traduit :)).

Enfin les balises ayant un attribut `xlink:href` ajoutent automatiquement un champ `{nom de la balise}_url` qui permet
d'obtenir le lien. Si la balise est `<truc xlink:href="toto">...</truc>` alors on aura accès à `#TRUC` et `#TRUC_URL` 

Deux autres balises spécifiques existent :
- `#URL_PRESTASHOP` retourne l'URL du prestashop dans la langue en cours
- `#URL_PRESTASHOP{product,3}` retourne l'URL du produit, dans la langue en cours
- `#URL_PRESTASHOP{product,4,en}` pareil en langue en.

- `#URL_IMAGE_PRESTASHOP{product,3}` retourne l'url de l'image principale du produit dans la langue en cours
- `#URL_IMAGE_PRESTASHOP{product,3,en}` pareil en anglais

Pour ces 2 balises, c'est un peu compliqué car le webservice ne retourne pas directement ces URLs.
Pour les produits, on "ping" l'URL du controlleur de prestashop, qui va rediriger sur l'URL propre. On capture l'URL de redirection (et on cache dans memoization).
Pour les images, on ne sait (actuellement) que prédire où l'image est rangée dans Prestashop (img/c/{id}.jpg pour les catégories, img/p/{i}/{d}/{id}.jpg pour les produits).

Autant pour les urls c'est passable, autant pour les images, c'est un peu imprevisible (le produit pourrait ne pas avoir d'image par exemple). Mais le webservice
retourne une URL d'image qui nécessite une clé d'API pour fonctionner, ce qui n'est pas du tout pratique, car on ne peut par exemple pas l'utiliser pour appliquer `|image_reduire` 

Il y a également les fonctions PHP correspondantes à ces balises :
- prestashop_image($objet, $id)
- calculer_url_prestashop()


Mini démonstration
------------------

Après avoir configuré l'URL et la clé sur exec=configurer_prestashop_api,
une liste de 5 produits est affichée sur exec=demo_prestashop_api.
Note : Le tout premier affichage peut être long.
master
marcimat@rezo.net 6 years ago
parent
commit
f7c75a35d6
  1. 13
      .gitattributes
  2. 39
      formulaires/configurer_prestashop_api.html
  3. 31
      formulaires/configurer_prestashop_api.php
  4. 80
      inc/prestashop_webservice.php
  5. 152
      inc/prestashop_webservice_utils.php
  6. 4
      lang/paquet-prestashop_api_fr.php
  7. 29
      lang/prestashop_api_fr.php
  8. 412
      lib/PSWebServiceLibrary.php
  9. 12
      paquet.xml
  10. BIN
      prestashop_api.jpg
  11. 25
      prestashop_api_administrations.php
  12. 157
      prestashop_api_fonctions.php
  13. 32
      prive/objets/liste/ps_products.html
  14. 7
      prive/squelettes/contenu/demo_prestashop_api.html
  15. BIN
      prive/themes/spip/images/prestashop_api-128.png
  16. BIN
      prive/themes/spip/images/prestashop_api-32.png
  17. BIN
      prive/themes/spip/images/prestashop_api-64.png
  18. 462
      public/prestashop.php

13
.gitattributes vendored

@ -1,11 +1,18 @@
* text=auto !eol
formulaires/configurer_prestashop_api.html -text
formulaires/configurer_prestashop_api.php -text
inc/prestashop_webservice.php -text
inc/prestashop_webservice_utils.php -text
lang/paquet-prestashop_api_fr.php -text
lang/prestashop_api_fr.php -text
lib/PSWebServiceLibrary.php -text
modeles/prestashop_product.html -text
modeles/prestashop_product_fonctions.php -text
/paquet.xml -text
/prestashop_api.jpg -text
/prestashop_api_administrations.php -text
/prestashop_api_fonctions.php -text
prive/objets/liste/ps_products.html -text
prive/squelettes/contenu/configurer_prestashop_api.html -text
prive/squelettes/contenu/demo_prestashop_api.html -text
prive/themes/spip/images/prestashop_api-128.png -text
prive/themes/spip/images/prestashop_api-32.png -text
prive/themes/spip/images/prestashop_api-64.png -text
public/prestashop.php -text

39
formulaires/configurer_prestashop_api.html

@ -0,0 +1,39 @@
<div class="formulaire_spip formulaire_configurer formulaire_#FORM">
<h3 class="titrem"><:prestashop_api:cfg_titre_parametrages:></h3>
<p class="explication"><:prestashop_api:cfg_parametrages_explications:></p>
[(#BOITE_OUVRIR{Notes de développement, info})]
[(#VAL{
-* Ce plugin sait gérer du multi-boutiques par langue.
-* Dans ce cas l'URL indiquée ici correspond donc à l'un des chemins possibles pour accéder à l'API de Prestashop.
-* Une fois connecté une première fois le plugin va interroger le webservice pour avoir
l'URL des différentes boutiques et les langues qui leur correspondent.
-* Par défaut les boucles PRESTASHOP tel que <code>(PRESTASHOP:PRODUCTS)</code> appeleront
le Webservice en utilisant l'URL d'API correspondant à la boutique dans la langue en cours.
}|propre)]
[(#BOITE_FERMER)]
[<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{message_ok})</p>]
[<p class="reponse_formulaire reponse_formulaire_erreur">(#ENV*{message_erreur})</p>]
<form method="post" action="#ENV{action}">
<div>
#ACTION_FORMULAIRE{#ENV{action}}
<div class="editer-groupe">
[(#SAISIE{input, url,
label=<:prestashop_api:label_url_prestashop:>,
placeholder=https://domaine.tld/boutique})]
[(#SAISIE{input, cle,
label=<:prestashop_api:label_cle_webservice:>,
placeholder=IZEF5CNVCBHKND15CVGM61LPN2Y4D2HF})]
</div>
<input type="hidden" name="_meta_casier" value="prestashop_api" />
<p class="boutons"><span class="image_loading">&nbsp;</span><input type="submit" class="submit" value="<:bouton_enregistrer:>" /></p>
</div>
</form>
</div>

31
formulaires/configurer_prestashop_api.php

@ -1,31 +0,0 @@
<?php
// Sécurité
if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/config');
function formulaires_configurer_prestashop_api_saisies_dist(){
$saisies = array(
array(
'saisie' => 'input',
'options' => array(
'nom' => 'url',
'label' => _T('prestashop_api:configurer_url_label'),
'defaut' => lire_config('prestashop_api/url'),
'obligatoire' => 'oui',
),
),
array(
'saisie' => 'input',
'options' => array(
'nom' => 'cle',
'label' => _T('prestashop_api:configurer_cle_label'),
'defaut' => lire_config('prestashop_api/cle'),
'obligatoire' => 'oui',
),
),
);
return $saisies;
}

80
inc/prestashop_webservice.php

@ -0,0 +1,80 @@
<?php
namespace SPIP\Prestashop;
include_spip('lib/PSWebServiceLibrary');
include_spip('inc/prestashop_ws_utils');
class Webservice extends \PrestaShopWebservice {
protected static $_instance_by_lang = [];
/**
* Returns new or existing Webservice instance
*
* Le webservice utilisera l'URL d'une boutique prestashop
* dans la langue demandée (si possible). Sinon on se rabat
* sur l'URL de configuration.
*
* @param string $lang Code de langue.
* @return Webservice
*/
final public static function getInstanceByLang($lang = null) {
if (!$lang) {
$lang = $GLOBALS['spip_lang'];
}
if (!empty(static::$_instance_by_lang[$lang])){
return static::$_instance_by_lang[$lang];
}
$url = prestashop_ws_list_shops_by_lang($lang);
static::$_instance_by_lang[$lang] = new static($url);
return static::$_instance_by_lang[$lang];
}
/**
* Webservice constructor.
* On passe l'url et la clé configurée.
* @param string|null $url
* URL du prestashop (utilise l'URL configurée par défaut si null)
* @param string|null $cle
* Clé d'API (utilise la clé configurée par défaut si null)
* @param bool|null $debug
* Mode débug. true/false pour activer désactiver. Null: automatique avec
* var_mode=prestashop (+ autorisation debug).
*/
function __construct($url = null, $cle = null, $debug = null) {
include_spip('inc/config');
if (is_null($url)) {
$url = lire_config('prestashop_api/url');
}
if (is_null($cle)) {
$cle = lire_config('prestashop_api/cle');
}
if (is_null($debug)) {
$debug = (_request('var_mode') === 'prestashop' AND include_spip('inc/autoriser') AND autoriser('debug'));
}
if ($url) {
$url = trim(rtrim($url, '/'));
}
parent::__construct($url, $cle, $debug);
}
/**
* On autorise un SSL non vérifié
*
* @note CHIOTTE ! ils utilisent self::executeRequest()
* dans leurs méthodes, ce qui fait qu'on ne peut
* pas surcharger la méthode (la notre n'est pas appelée).
* (vive static::).
*
* @param string $url
* @param array $curl_params
*/
public function executeRequest($url, $curl_params = array()) {
$curl_params += [
CURLOPT_SSL_VERIFYPEER => 0
];
return parent::executeRequest($url, $curl_params);
}
}

152
inc/prestashop_webservice_utils.php

@ -0,0 +1,152 @@
<?php
include_spip('inc/memoization');
/**
* Retourne la liste des différentes boutiques
* connues dans ce Prestashop.
*
* @return array Liste des boutiques et leurs URLs
*/
function prestashop_ws_list_shops() {
if (!is_null($W = cache_me())) {
return $W;
}
try {
// On utilise l'URL de configuration pour retrouver toutes les boutiques / urls.
$wsps = new \SPIP\Prestashop\Webservice();
} catch (PrestaShopWebserviceException $ex) {
spip_log('Erreur Webservice Prestashop : ' . $ex->getMessage());
return [];
}
$shops = [];
// Description des magasins
$xml = $wsps->get([
'resource' => 'shops',
'display' => 'full',
'filter[active]' => 1
]);
if ($xml) {
foreach ($xml->shops->shop as $s) {
$shops[(int)$s->id] = [
'id' => (int)$s->id,
'nom' => (string)$s->name,
'id_category' => (string)$s->id_category,
'id_theme' => (string)$s->id_theme,
];
}
// Obtenir les URLs des magasins.
$xml = $wsps->get([
'resource' => 'shop_urls',
'display' => 'full',
'filter[active]' => 1
]);
if ($xml) {
// On va supposer qu'il n'existe qu'une seule URL par shop…
// ce qui est loin d'être évident.
foreach ($xml->shop_urls->shop_url as $u) {
$id_shop = (int)$u->id_shop;
$domain = (string)$u->domain;
$domain_ssl = (string)$u->domain_ssl;
$physical_uri = (string)$u->physical_uri;
if ($domain_ssl) {
$shops[$id_shop]['url'] = 'https://' . $domain_ssl . $physical_uri;
} else {
$shops[$id_shop]['url'] = 'http://' . $domain . $physical_uri;
}
}
}
}
return $shops;
}
/**
* Retourne la liste des différentes langues
* connues dans ce Prestashop.
*
* Le truc ennuyant, c'est que sur les «multi boutiques»,
* typiquement lorsqu'il y a une boutique pour chaque langue,
* partagées dans le même Prestashop, alors, les produits (par exemple)
* ont des traductions dans N langues (l'identifiant des langues pour ces
* traductions est retournée par l'API), mais l'API pour lister les langues
* (api/languages) ne liste que les langues utilisées par la boutique
* que l'on appelle sur l'API. Du coup, on ne peut pas savoir simplement
* à quel code de langue correspond la traduction avec une langue d'identifiant 2,
* si cette langue n'est pas utilisée dans cette boutique.
*
* Il faut parcourir toutes les boutiques du prestashop pour calculer tous les identifants de langue.
*
* @return array Liste des langues (codes & urls)
*/
function prestashop_ws_list_languages() {
if (!is_null($W = cache_me())) {
return $W;
}
$shops = prestashop_ws_list_shops();
$langues = [];
foreach ($shops as $s) {
$url = $s['url'];
try {
$wsps = new \SPIP\Prestashop\Webservice($url);
} catch (PrestaShopWebserviceException $ex) {
spip_log('Erreur Webservice Prestashop : ' . $ex->getMessage());
return [];
}
// Description des langues du magasin
$xml = $wsps->get([
'resource' => 'languages',
'display' => 'full'
]);
if ($xml) {
// On va supposer qu'il n'existe qu'une seule URL par shop…
// ce qui est loin d'être évident.
foreach ($xml->languages->language as $l) {
$id = (int)$l->id;
$code = (string)$l->iso_code;
$langues[$id] = [
'code' => $code,
'shop' => $s
];
}
}
}
return $langues;
}
/**
* Retourne la liste des différentes langues et urls de shops de prestashop
*
* @uses prestashop_ws_list_languages();
* @param string|null $lang
* Langue souhaitée. Null : utilise la langue en cours.
* @return string
* URL pour cette langue (utilisera URL par défaut si aucune URL spécifique pour cette langue).
*/
function prestashop_ws_list_shops_by_lang($lang = null) {
$langues = [];
$ls = prestashop_ws_list_languages();
foreach ($ls as $l) {
$langues[$l['code']] = $l['shop']['url'];
}
if (is_null($lang)) {
$lang = $GLOBALS['spip_lang'];
}
if (isset($langues[$lang])) {
return $langues[$lang];
}
return lire_config('prestashop_api/url');
}

4
lang/paquet-prestashop_api_fr.php

@ -4,7 +4,7 @@
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
'prestashop_api_description' => 'Ce plugin permet de configurer la clé nécessaire pour accéder à l’API et fournit des inclusions.',
'prestashop_api_description' => 'Ce plugin permet de configurer la clé nécessaire pour accéder à l’API de Webservice d’un site utilisant Prestashop et offre un itérateur et des fonctions pour faciliter l’intégration d’éléments de Prestashop dans les squelettes SPIP.',
'prestashop_api_nom' => 'Liaison avec l’API de Prestashop',
'prestashop_api_slogan' => 'Afficher des choses d’un site sous Prestashop',
'prestashop_api_slogan' => 'Afficher des éléments issus d’un site sous Prestashop',
);

29
lang/prestashop_api_fr.php

@ -2,7 +2,32 @@
if (!defined('_ECRIRE_INC_VERSION')) return;
$GLOBALS[$GLOBALS['idx_lang']] = array(
'configurer_cle_label' => 'Clé d’accès à l’API',
'configurer_url_label' => 'URL de la boutique',
// C
'categorie' => 'Catégorie',
'categories' => 'Catégories',
'cfg_parametrages_explications' => 'Dans votre Prestashop, «Paramètres avancées > Services Web», créez ou réutilisez une clé de webservice.',
'cfg_titre_parametrages' => 'Paramétrages',
// I
'info_1_produit' => 'Un produit',
'info_nb_produits' => '@nb@ produits',
// L
'logo' => 'Logo',
'label_url_prestashop' => 'URL de la boutique Prestashop',
'label_cle_webservice' => 'Clé d’accès au Webservice',
// P
'prestashop_api_titre' => 'Prestashop Webservice API',
'prix' => 'Prix',
'produit' => 'Produit',
'produits' => 'Produits',
'product_acheter' => 'Acheter ce produit',
// T
'titre_page_configurer_prestashop_api' => 'Configurer l’accès au Webservice Prestashop',
'titre_page_demo_prestashop_api' => 'Tester l’accès au Webservice Prestashop',
);

412
lib/PSWebServiceLibrary.php

@ -0,0 +1,412 @@
<?php
/*
* 2007-2013 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2013 PrestaShop SA
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
* PrestaShop Webservice Library
* @package PrestaShopWebservice
*/
/**
* @package PrestaShopWebservice
*/
class PrestaShopWebservice
{
/** @var string Shop URL */
protected $url;
/** @var string Authentification key */
protected $key;
/** @var boolean is debug activated */
protected $debug;
/** @var string PS version */
protected $version;
/** @var array compatible versions of PrestaShop Webservice */
const psCompatibleVersionsMin = '1.4.0.0';
const psCompatibleVersionsMax = '1.6.99.99';
/**
* PrestaShopWebservice constructor. Throw an exception when CURL is not installed/activated
* <code>
* <?php
* require_once('./PrestaShopWebservice.php');
* try
* {
* $ws = new PrestaShopWebservice('http://mystore.com/', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ', false);
* // Now we have a webservice object to play with
* }
* catch (PrestaShopWebserviceException $ex)
* {
* echo 'Error : '.$ex->getMessage();
* }
* ?>
* </code>
* @param string $url Root URL for the shop
* @param string $key Authentification key
* @param mixed $debug Debug mode Activated (true) or deactivated (false)
*/
function __construct($url, $key, $debug = true) {
if (!extension_loaded('curl'))
throw new PrestaShopWebserviceException('Please activate the PHP extension \'curl\' to allow use of PrestaShop webservice library');
$this->url = $url;
$this->key = $key;
$this->debug = $debug;
$this->version = 'unknown';
}
/**
* Take the status code and throw an exception if the server didn't return 200 or 201 code
* @param int $status_code Status code of an HTTP return
*/
protected function checkStatusCode($status_code)
{
$error_label = 'This call to PrestaShop Web Services failed and returned an HTTP status of %d. That means: %s.';
switch($status_code)
{
case 200: case 201: break;
case 204: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'No content'));break;
case 400: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'Bad Request'));break;
case 401: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'Unauthorized'));break;
case 404: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'Not Found'));break;
case 405: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'Method Not Allowed'));break;
case 500: throw new PrestaShopWebserviceException(sprintf($error_label, $status_code, 'Internal Server Error'));break;
default: throw new PrestaShopWebserviceException('This call to PrestaShop Web Services returned an unexpected HTTP status of:' . $status_code);
}
}
/**
* Handles a CURL request to PrestaShop Webservice. Can throw exception.
* @param string $url Resource name
* @param mixed $curl_params CURL parameters (sent to curl_set_opt)
* @return array status_code, response
*/
protected function executeRequest($url, $curl_params = array())
{
$defaultParams = array(
CURLOPT_SSL_VERIFYPEER => 0, ## SURCHARGE MOCHE DE MOCHE POUR SSL POURRIS ##
CURLOPT_HEADER => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLINFO_HEADER_OUT => TRUE,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $this->key.':',
CURLOPT_HTTPHEADER => array( 'Expect:' )
);
$session = curl_init($url);
$curl_options = array();
foreach ($defaultParams as $defkey => $defval)
{
if (isset($curl_params[$defkey]))
$curl_options[$defkey] = $curl_params[$defkey];
else
$curl_options[$defkey] = $defaultParams[$defkey];
}
foreach ($curl_params as $defkey => $defval)
if (!isset($curl_options[$defkey]))
$curl_options[$defkey] = $curl_params[$defkey];
curl_setopt_array($session, $curl_options);
$response = curl_exec($session);
$index = strpos($response, "\r\n\r\n");
if ($index === false && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD') {
throw new PrestaShopWebserviceException('Bad HTTP response');
}
$header = substr($response, 0, $index);
$body = substr($response, $index + 4);
$headerArrayTmp = explode("\n", $header);
$headerArray = array();
foreach ($headerArrayTmp as &$headerItem)
{
$tmp = explode(':', $headerItem);
$tmp = array_map('trim', $tmp);
if (count($tmp) == 2)
$headerArray[$tmp[0]] = $tmp[1];
}
if (array_key_exists('PSWS-Version', $headerArray))
{
$this->version = $headerArray['PSWS-Version'];
if (
version_compare(PrestaShopWebservice::psCompatibleVersionsMin, $headerArray['PSWS-Version']) == 1 ||
version_compare(PrestaShopWebservice::psCompatibleVersionsMax, $headerArray['PSWS-Version']) == -1
)
throw new PrestaShopWebserviceException('This library is not compatible with this version of PrestaShop. Please upgrade/downgrade this library');
}
if ($this->debug)
{
$this->printDebug('HTTP REQUEST HEADER', curl_getinfo($session, CURLINFO_HEADER_OUT));
$this->printDebug('HTTP RESPONSE HEADER', $header);
}
$status_code = curl_getinfo($session, CURLINFO_HTTP_CODE);
if ($status_code === 0)
throw new PrestaShopWebserviceException('CURL Error: '.curl_error($session));
curl_close($session);
if ($this->debug)
{
if ($curl_params[CURLOPT_CUSTOMREQUEST] == 'PUT' || $curl_params[CURLOPT_CUSTOMREQUEST] == 'POST')
$this->printDebug('XML SENT', urldecode($curl_params[CURLOPT_POSTFIELDS]));
if ($curl_params[CURLOPT_CUSTOMREQUEST] != 'DELETE' && $curl_params[CURLOPT_CUSTOMREQUEST] != 'HEAD')
$this->printDebug('RETURN HTTP BODY', $body);
}
return array('status_code' => $status_code, 'response' => $body, 'header' => $header);
}
public function printDebug($title, $content)
{
echo '<div style="display:table;background:#CCC;font-size:8pt;padding:7px"><h6 style="font-size:9pt;margin:0">'.$title.'</h6><pre>'.htmlentities($content).'</pre></div>';
}
public function getVersion()
{
return $this->version;
}
/**
* Load XML from string. Can throw exception
* @param string $response String from a CURL response
* @return SimpleXMLElement status_code, response
*/
protected function parseXML($response)
{
if ($response != '')
{
libxml_clear_errors();
libxml_use_internal_errors(true);
$xml = simplexml_load_string($response,'SimpleXMLElement', LIBXML_NOCDATA);
if (libxml_get_errors())
{
$msg = var_export(libxml_get_errors(), true);
libxml_clear_errors();
throw new PrestaShopWebserviceException('HTTP XML response is not parsable: '.$msg);
}
return $xml;
}
else
throw new PrestaShopWebserviceException('HTTP response is empty');
}
/**
* Add (POST) a resource
* <p>Unique parameter must take : <br><br>
* 'resource' => Resource name<br>
* 'postXml' => Full XML string to add resource<br><br>
* Examples are given in the tutorial</p>
* @param array $options
* @return SimpleXMLElement status_code, response
*/
public function add($options)
{
$xml = '';
if (isset($options['resource'], $options['postXml']) || isset($options['url'], $options['postXml']))
{
$url = (isset($options['resource']) ? $this->url.'/api/'.$options['resource'] : $options['url']);
$xml = $options['postXml'];
if (isset($options['id_shop']))
$url .= '&id_shop='.$options['id_shop'];
if (isset($options['id_group_shop']))
$url .= '&id_group_shop='.$options['id_group_shop'];
}
else
throw new PrestaShopWebserviceException('Bad parameters given');
$request = self::executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => $xml));
self::checkStatusCode($request['status_code']);
return self::parseXML($request['response']);
}
/**
* Retrieve (GET) a resource
* <p>Unique parameter must take : <br><br>
* 'url' => Full URL for a GET request of Webservice (ex: http://mystore.com/api/customers/1/)<br>
* OR<br>
* 'resource' => Resource name,<br>
* 'id' => ID of a resource you want to get<br><br>
* </p>
* <code>
* <?php
* require_once('./PrestaShopWebservice.php');
* try
* {
* $ws = new PrestaShopWebservice('http://mystore.com/', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ', false);
* $xml = $ws->get(array('resource' => 'orders', 'id' => 1));
* // Here in $xml, a SimpleXMLElement object you can parse
* foreach ($xml->children()->children() as $attName => $attValue)
* echo $attName.' = '.$attValue.'<br />';
* }
* catch (PrestaShopWebserviceException $ex)
* {
* echo 'Error : '.$ex->getMessage();
* }
* ?>
* </code>
* @param array $options Array representing resource to get.
* @return SimpleXMLElement status_code, response
*/
public function get($options)
{
if (isset($options['url']))
$url = $options['url'];
elseif (isset($options['resource']))
{
$url = $this->url.'/api/'.$options['resource'];
$url_params = array();
if (isset($options['id']))
$url .= '/'.$options['id'];
$params = array('filter', 'display', 'sort', 'limit', 'id_shop', 'id_group_shop');
foreach ($params as $p)
foreach ($options as $k => $o)
if (strpos($k, $p) !== false)
$url_params[$k] = $options[$k];
if (count($url_params) > 0)
$url .= '?'.http_build_query($url_params);
}
else
throw new PrestaShopWebserviceException('Bad parameters given');
$request = self::executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'GET'));
self::checkStatusCode($request['status_code']);// check the response validity
return self::parseXML($request['response']);
}
/**
* Head method (HEAD) a resource
*
* @param array $options Array representing resource for head request.
* @return SimpleXMLElement status_code, response
*/
public function head($options)
{
if (isset($options['url']))
$url = $options['url'];
elseif (isset($options['resource']))
{
$url = $this->url.'/api/'.$options['resource'];
$url_params = array();
if (isset($options['id']))
$url .= '/'.$options['id'];
$params = array('filter', 'display', 'sort', 'limit');
foreach ($params as $p)
foreach ($options as $k => $o)
if (strpos($k, $p) !== false)
$url_params[$k] = $options[$k];
if (count($url_params) > 0)
$url .= '?'.http_build_query($url_params);
}
else
throw new PrestaShopWebserviceException('Bad parameters given');
$request = self::executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'HEAD', CURLOPT_NOBODY => true));
self::checkStatusCode($request['status_code']);// check the response validity
return $request['header'];
}
/**
* Edit (PUT) a resource
* <p>Unique parameter must take : <br><br>
* 'resource' => Resource name ,<br>
* 'id' => ID of a resource you want to edit,<br>
* 'putXml' => Modified XML string of a resource<br><br>
* Examples are given in the tutorial</p>
* @param array $options Array representing resource to edit.
*/
public function edit($options)
{
$xml = '';
if (isset($options['url']))
$url = $options['url'];
elseif ((isset($options['resource'], $options['id']) || isset($options['url'])) && $options['putXml'])
{
$url = (isset($options['url']) ? $options['url'] : $this->url.'/api/'.$options['resource'].'/'.$options['id']);
$xml = $options['putXml'];
if (isset($options['id_shop']))
$url .= '&id_shop='.$options['id_shop'];
if (isset($options['id_group_shop']))
$url .= '&id_group_shop='.$options['id_group_shop'];
}
else
throw new PrestaShopWebserviceException('Bad parameters given');
$request = self::executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'PUT', CURLOPT_POSTFIELDS => $xml));
self::checkStatusCode($request['status_code']);// check the response validity
return self::parseXML($request['response']);
}
/**
* Delete (DELETE) a resource.
* Unique parameter must take : <br><br>
* 'resource' => Resource name<br>
* 'id' => ID or array which contains IDs of a resource(s) you want to delete<br><br>
* <code>
* <?php
* require_once('./PrestaShopWebservice.php');
* try
* {
* $ws = new PrestaShopWebservice('http://mystore.com/', 'ZQ88PRJX5VWQHCWE4EE7SQ7HPNX00RAJ', false);
* $xml = $ws->delete(array('resource' => 'orders', 'id' => 1));
* // Following code will not be executed if an exception is thrown.
* echo 'Successfully deleted.';
* }
* catch (PrestaShopWebserviceException $ex)
* {
* echo 'Error : '.$ex->getMessage();
* }
* ?>
* </code>
* @param array $options Array representing resource to delete.
*/
public function delete($options)
{
if (isset($options['url']))
$url = $options['url'];
elseif (isset($options['resource']) && isset($options['id']))
if (is_array($options['id']))
$url = $this->url.'/api/'.$options['resource'].'/?id=['.implode(',', $options['id']).']';
else
$url = $this->url.'/api/'.$options['resource'].'/'.$options['id'];
if (isset($options['id_shop']))
$url .= '&id_shop='.$options['id_shop'];
if (isset($options['id_group_shop']))
$url .= '&id_group_shop='.$options['id_group_shop'];
$request = self::executeRequest($url, array(CURLOPT_CUSTOMREQUEST => 'DELETE'));
self::checkStatusCode($request['status_code']);// check the response validity
return true;
}
}
/**
* @package PrestaShopWebservice
*/
class PrestaShopWebserviceException extends Exception { }

12
paquet.xml

@ -1,17 +1,19 @@
<paquet
prefix="prestashop_api"
categorie="outil"
version="1.0.2"
schema="1.0.0"
version="2.0.0"
etat="test"
compatibilite="[3.0.0;3.0.*]"
logo="prestashop_api.jpg"
compatibilite="[3.1.0;3.1.*]"
logo="prive/themes/spip/images/prestashop_api-64.png"
documentation=""
>
<nom>Liaison avec l'API de Prestashop</nom>
<auteur lien="http://www.ldd.fr">Les Développements Durables</auteur>
<auteur>Matthieu Marcillaud</auteur>
<licence>GNU/GPL</licence>
<necessite nom="saisies" compatibilite="[1.28.2;]" />
<necessite nom="memoization" compatibilite="[1.7.0;]" />
<necessite nom="saisies" compatibilite="[2.8.0;]" />
<necessite nom="php" compatibilite="[5.4.0;]" /><!-- + php:curl -->
</paquet>

BIN
prestashop_api.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

25
prestashop_api_administrations.php

@ -1,25 +0,0 @@
<?php
// Sécurité
if (!defined('_ECRIRE_INC_VERSION')) return;
/**
* Fonction d'installation du plugin et de mise à jour.
**/
function prestashop_api_upgrade($nom_meta_base_version, $version_cible) {
$maj = array();
// $maj['create'] = array();
include_spip('base/upgrade');
maj_plugin($nom_meta_base_version, $version_cible, $maj);
}
/**
* Fonction de désinstallation du plugin.
**/
function prestashop_api_vider_tables($nom_meta_base_version) {
effacer_meta('prestashop_api');
effacer_meta($nom_meta_base_version);
}

157
prestashop_api_fonctions.php

@ -0,0 +1,157 @@
<?php
/**
* Fonctions utiles au plugin Webservice Prestashop
*
* @plugin Webservice Prestashop
* @copyright 2017
* @author Matthieu Marcillaud
* @licence GNU/GPL
* @package SPIP\Wsps\Fonctions
*/
if (!defined('_ECRIRE_INC_VERSION')) {
return;
}
// charger les fonctions pour le compilateur SPIP
// boucles (PRESTASHOP:PRODUCTS) ...
include_spip('public/prestashop');
/**
* Retourne l'URL de prestashop ou d'un élément de Prestashop.
*
* L'URL de base du prestashop peut dépendre de la langue.
* On peut forcer une langue en utilisant le 3è argument.
*
* @example
* ```
* #URL_PRESTASHOP
* #URL_PRESTASHOP{product,51}
* #URL_PRESTASHOP{category,3}
* #URL_PRESTASHOP{product,51,en}
* #URL_PRESTASHOP{'','',en}
* ```
*
* @param $p
* @return mixed
*/
function balise_URL_PRESTASHOP_dist($p) {
if (!$type = interprete_argument_balise(1, $p)) {
$type = "''";
}
if (!$id = interprete_argument_balise(2, $p)) {
$id = "''";
}
if (!$lang = interprete_argument_balise(3, $p)) {
$lang = "''";
}
$p->code="calculer_url_prestashop($type, $id, $lang)";
return $p;
}
/**
* Retourne une URL pour Prestashop.
*
* @param string $type
* Type d'objet de prestashop
* @param int $id
* Identifiant d'objet de prestashop
* @param string $lang
* Langue désirée.
* @param string $lang
*/
function calculer_url_prestashop($type = '', $id = '', $lang = '') {
$url_prestashop = rtrim(prestashop_ws_list_shops_by_lang($lang), '/');
if (!$type) {
return $url_prestashop;
}
$url = $url_prestashop . '/index.php';
$url = parametre_url($url, 'controller', $type, '&');
if ($id) {
$url = parametre_url($url, 'id_' . $type, $id, '&');
}
// Pour éviter des cURL à tout bout de champ… on cache
if ($W = cache_get($key = 'url_presta_' . $url)) {
return $W;
}
// Calculer l'URL de redirection qu'utilise prestashop pour obtenir une belle URL.
// http://stackoverflow.com/questions/22633395/get-product-url-using-prestashop-api
// C'est franchement pas ce qu'on fait de mieux…
$ch = curl_init($url);
curl_exec($ch);
if ($_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL)) {
$url = $_url;
}
curl_close($ch);
cache_set($key, $url);
return $url;
}
/**
* Retourne l'URL l'image principale d'un élément de Prestashop.
*
* L'URL de base du prestashop peut dépendre de la langue.
* On peut forcer une langue en utilisant le 3è argument.
*
* @example
* ```
* #URL_IMAGE_PRESTASHOP{product,51}
* #URL_IMAGE_PRESTASHOP{category,3}
* #URL_IMAGE_PRESTASHOP{category,3,en}
* ```
*
* @param $p
* @return mixed
*/
function balise_URL_IMAGE_PRESTASHOP_dist($p) {
$type = interprete_argument_balise(1, $p);
$id = interprete_argument_balise(2, $p);
if (!$type OR !$id) {
$err_b_s_a = array('zbug_balise_sans_argument', array('balise' => 'URL_IMAGE_PRESTASHOP'));
erreur_squelette($err_b_s_a, $p);
} else {
if (!$lang = interprete_argument_balise(3, $p)) {
$lang = "''";
}
$p->code = "prestashop_image($type, $id, $lang)";
}
return $p;
}
/**
* Retourne l'URL théorique d'une image d'un objet prestashop (produit, category)
*
* @param int $id
* @param string $objet
* @param string $lang
* Langue désirée.
* @return mixed
*/
function prestashop_image($objet, $id, $lang = '') {
$url = calculer_url_prestashop() . '/img/';
$id = trim($id);
$objet = trim($objet);
switch ($objet) {
case 'product':
case 'products':
$url .= 'p/' . implode('/', str_split($id)) . '/' . $id . '.jpg';
break;
case 'category':
case 'categories':
$url .= 'c/' . $id . '.jpg';
break;
default:
$url = '';
break;
}
#$img = '<img src="' . $url . '" alt="" />';
return $url;
}

32
prive/objets/liste/ps_products.html

@ -0,0 +1,32 @@
<B_produits>
#ANCRE_PAGINATION
<div class="liste-objets ps-products">
<table class='spip liste'>
[<caption><strong class="caption">(#ENV*{titre,#GRAND_TOTAL|singulier_ou_pluriel{prestashop_api:info_1_produit,prestashop_api:info_nb_produits}})</strong></caption>]
<thead>
<tr class='first_row'>
<th class='logo' scope='col'><:prestashop_api:logo:></th>
<th class='titre principale' scope='col'><:info_titre:></th>
<th class='date secondaire' scope='col'><:date:></th>
<th class='prix' scope='col'><:prestashop_api:prix:></th>
<th class='id' scope='col'><:info_numero_abbreviation:></th>
</tr>
</thead>
<tbody>
<BOUCLE_produits(PRESTASHOP:PRODUCTS){active=1}{pagination #ENV{nb,5}}>
<tr class="[(#COMPTEUR_BOUCLE|alterner{row_odd,row_even})]">
<td class="logo"><a href="#URL_IMAGE_PRESTASHOP{product,#ID}" class="mediabox">[(#URL_IMAGE_PRESTASHOP{product,#ID}|image_reduire{80,80})]</a></td>
<td class='titre principale'><a href="#URL_PRESTASHOP{product,#ID}"><strong>[(#NAME|typo)]</strong></a></td>
<td class='date secondaire'>[<span title="[(#DATE_ADD|heures_minutes)]">(#DATE_ADD|affdate_jourcourt)</span>]</td>
<td class='prix'>[(#PRICE|number_format{2})]</td>
<td class='id'>#ID</td>
</tr>
</BOUCLE_produits>
</tbody>
</table>
[<p class='pagination'>(#PAGINATION{#ENV{pagination,prive}})</p>]
</div>
</B_produits>
[<div class="liste-objets ps_products caption-wrap"><strong class="caption">(#ENV*{sinon,''})</strong></div>]
<//B_produits>

7
prive/squelettes/contenu/demo_prestashop_api.html

@ -0,0 +1,7 @@
[(#AUTORISER{configurer,_prestashop_api}|sinon_interdire_acces)]
<h1 class="grostitre"><:prestashop_api:titre_page_demo_prestashop_api:></h1>
<h2><:prestashop_api:produits:></h2>
<INCLURE{fond=prive/objets/liste/ps_products, env, ajax} />

BIN
prive/themes/spip/images/prestashop_api-128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
prive/themes/spip/images/prestashop_api-32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
prive/themes/spip/images/prestashop_api-64.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

462
public/prestashop.php

@ -0,0 +1,462 @@
<?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;
}
Loading…
Cancel
Save