Library to manipulate Prestashop from scripts
<?php
/**
prestashop-lib — © 2015 Chloé Tigre Rouge <chloe@tigres-rouges.net>
This is licensed under the same terms as Prestashop.
PrestaShop library to create products, categories and all sorts of things
programmatically by passing arrays around
It needs a bootstrapped PrestaShop framework (you can get one by defining
_PS_ROOT_DIR_ and by a
require_once(_PS_ROOT_DIR_.'config/config.inc.php');
**/
/** this function borrowed from a Gist
https://gist.github.com/freekrai/9588315
**/
if (!function_exists('slugify'))
{
function slugify($text)
{
// replace non letter or digits by -
$text = preg_replace('~[^\\pL\d]+~u', '-', $text);
// trim
$text = trim($text, '-');
// transliterate
if (function_exists('iconv'))
{
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
}
// lowercase
$text = strtolower($text);
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
if (empty($text))
{
return 'n-a';
}
return $text;
}
}
if (!function_exists('coalesce'))
{
function coalesce() {
$args = func_get_args();
foreach ($args as $arg) {
if (!empty($arg)) {
return $arg;
}
}
return NULL;
}
}
// necessary hack to be able to manipulate data properly
class AdminImportController extends AdminImportControllerCore
{
public static function copyImg($i,$s,$o,$g = 'products', $r = true)
{
return parent::copyImg($i, $s, $o, $g, $r);
}
}
// book-keeping class to map old and new IDs
class bookkeeper
{
private function __construct()
{}
private static $instance = null;
public static function getInstance()
{
if (self::$instance == null)
{
self::buildInstance();
}
return self::$instance;
}
private static function buildInstance()
{
self::$instance = new bookkeeper();
self::$instance->datastore = array();
if (file_exists('.bookkeeper.srz'))
{
self::$instance->datastore = unserialize(
file_get_contents('.bookkeeper.srz') );
}
}
public $datastore;
public function persist()
{
file_put_contents('.bookkeeper.srz', serialize($this->datastore));
//echo "Persisted to ".dirname(__FILE__)."/.bookkeeper.srz";
}
public function reload()
{
$i = unserialize(file_get_contents('.bookkeeper.srz'));
$this->datastore = $i;
}
public function reinit($cls = null)
{
if ($cls == null)
$this->datastore = array();
else
$this->datastore[$cls] = null;
}
public function storekey($cls, $key, $value)
{
if (!isset($this->datastore[$cls]))
{
$this->datastore[$cls] = array();
}
$this->datastore[$cls][$key] = $value;
return $this->datastore[$cls][$key];
}
public function delkey($cls, $key)
{
if (isset($this->datastore[$cls], $this->datastore[$cls][$key]))
{
unset($this->datastore[$cls][$key]);
}
}
public function getval($cls, $key)
{
if (isset($this->datastore[$cls][$key]))
return $this->datastore[$cls][$key];
else
return null;
}
public function getcached($cls, $key, $misscb)
{
if (isset($this->datastore[$cls], $this->datastore[$cls][$key]))
{
return $this->datastore[$cls][$key];
}
return $this->storekey($cls, $key, call_user_func($misscb));
}
}
// quick lang array
function ctr_psvla($text)
{
return array(
(int)(Configuration::get('PS_LANG_DEFAULT')) => $text
);
}
/**
create attribute group
**/
function ctr_ps_create_attributegroup($id, $name, $type, $options)
{
$g = new AttributeGroup();
$g->name = ctr_psvla($name);
$g->public_name = $g->name;
$g->group_type = $type;
$g->add();
foreach ($options as $o)
{
$a = new Attribute();
$a->name = ctr_psvla($o);
$a->id_attribute_group = $g->id;
$a->add();
}
return $g->id;
}
/**
get attribute id by attribute group and text
**/
function ctr_ps_get_attribute($attr_grp, $attr_txt)
{
$groups = bookkeeper::getInstance()->getcached(
'system', 'attributegroups',
function(){
return AttributeGroup::getAttributesGroups(Configuration::get('PS_LANG_DEFAULT'));
});
$group = null;
foreach ($groups as $g) {
if (trim($g['name']) == trim($attr_grp))
{
$group = new AttributeGroup($g['id_attribute_group']);
break;
}
}
if ($group == null)
{
e_("Cannot find group ".$attr_grp);
e_("Groups: ".print_r($groups, true));
die();
return;
}
$attributes = AttributeGroup::getAttributes(
Configuration::get('PS_LANG_DEFAULT'),
$group->id
);
$attribute = null;
foreach ($attributes as $attr)
{
if ($attr['name'] == $attr_txt)
{
$attribute = $attr;
break;
}
}
return $attribute;
}
/**
add attribute to product, with specific price
**/
function ctr_ps_add_attribute_to_product($product_id, $attr_grp, $attr_txt, $opt_price, $ref,
$ean13, $is_default)
{
$p = new Product($product_id);
$attribute = ctr_ps_get_attribute($attr_grp, $attr_txt);
if ($attribute == null)
{
e_("Cannot find attribute <$attr_txt> in group <$attr_grp>");
return;
}
// here we have all needed elements so we add the attribute with proper impact
$idPAC = $p->addAttribute($opt_price, 0, 0, 0, array(), $ref, $ean13, $is_default);
// now we do a dirty database insert :(
ctr_ps_add_combination_to_product_attribute($idPAC, $attribute['id_attribute']);
return $idPAC;
}
function ctr_ps_add_combination_to_product_attribute($id_product_attr,$id_attribute)
{
return Db::getInstance()->insert('product_attribute_combination',
array (
'id_product_attribute' => $id_product_attr,
'id_attribute' => $id_attribute
));
}
function ctr_ps_add_other_option_to_combination($pac_id, $attr_grp, $attr_txt)
{
$attribute = ctr_ps_get_attribute($attr_grp, $attr_txt);
echo "Adding <$attr_grp>::$attr_txt to Combination $pac_id\n";
return Db::getInstance()->insert('product_attribute_combination',
array (
'id_product_attribute' => $pac_id,
'id_attribute' => $attribute['id_attribute']
));
}
/**
create a single prestashop product according to the
data passed in the array
Return the product ID
$parameters: array with the following informations:
product_title: array of l7d strings or mere string if only default
description: ditto
short_description: ditto
reference: string
ean13: string
price: float
wholesale_price: float
sales_price: float
primary_category: int or null
tax_rate: float
stock_available: int
features: dict
enabled: boolean
images: array of strings pointing to the images
$id: if set, update a product by ID rather than setting it
(not well supported)
**/
function ctr_ps_create_product($parameters,$id=null)
{
if ($id!==null) $p = new Product($id);
else $p = new Product();
// perform transformations
$slug = ctr_psvla(slugify($parameters['product_title']));
if (!is_array($parameters['product_title']))
$parameters['product_title'] = ctr_psvla($parameters['product_title']);
if (!is_array($parameters['description']))
$parameters['description'] = ctr_psvla($parameters['description']);
if (!is_array($parameters['short_description']))
$parameters['short_description'] =
ctr_psvla($parameters['short_description']);
// set basic data - pointless repetitive code
$p->name = $parameters['product_title'];
$p->description = $parameters['description'];
$p->description_short = $parameters['short_description'];
$p->link_rewrite = $slug;
$p->reference = $parameters['reference'];
$p->ean13 = $parameters['ean13'];
$p->id_category_default = $parameters['primary_category'];
$p->category = $parameters['categories'];
$p->price = $parameters['price'];
$p->wholesale_price = $parameters['wholesale_price'];
$p->tax_rate = $parameters['tax_rate'];
if ($p->tax_rate == 20) $p->id_tax_rules_group = 1;
elseif ($p->tax_rate == 5.5) $p->id_tax_rules_group = 3;
elseif ($p->tax_rate == 10) $p->id_tax_rules_group = 2;
elseif ($p->tax_rate == 2.1) $p->id_tax_rules_group = 4;
$p->tax_name = 'TVA';
$p->weight = $parameters['weight'];
$p->visibility = $parameters['enabled']?'both':'none';
$p->available_for_order = $parameters['enabled'];
$p->available_date = $parameters['date_available'];
$p->active = $parameters['enabled'];
// save so we have a product ID
//echo "Trying to create product ";
//echo print_r($parameters['product_title'],true)."\n";
$p->save();
$id = $p->id;
// now add some features to it.
// features can be identified by name
foreach ($parameters['features'] as $k=>$v)
{
$id_feature = Feature::addFeatureImport($k);
Product::addFeatureProductImport($id, $id_feature, $v);
}
// add to categories
//echo "Now adding product $id to categories ".join(", ",$parameters['categories'])."\n";
$p->addToCategories($parameters['categories']);
// set stock
//echo "Now setting stock for product $id to ".$parameters['stock_available']."\n";
StockAvailable::setQuantity($id, 0, $parameters['stock_available'], (int)Configuration::get('PS_SHOP_DEFAULT'));
// now add images
$cover = false;
foreach ($parameters['images'] as $img)
{
ctr_ps_addimagetoproduct($id, $img,!$cover);
$cover=true;
}
return $id;
}
function ctr_ps_addimagetoproduct($id_product, $img, $cover)
{
$image = new Image();
$image->id_product = $id_product;
$image->legend = ctr_psvla("default caption");
$image->position = Image::getHighestPosition($id_product)+1;
$image->cover = $cover;
if (($image->validateFields(false, true)) === true &&
($image->validateFieldsLang(false, true)) === true && $image->add())
{
$image->associateTo(Shop::getShops(true, null, true));
if (!AdminImportController::copyImg(
$id_product, $image->id, dirname(__FILE__).'/'.$img, 'products', false))
{
echo "error adding image $img to associate with
$id_product and {$image->id}\n";
print_r($image);
// $image->delete();
}
else
{
copy(dirname(__FILE__)."/$img", $image->getPathForCreation().'.jpg');
}
}
}
/**
create a single prestashop category. Return its persisted ID
parameters: array
label: name of the category
description: description
slug: URLable designation
**/
function ctr_ps_create_category($parameters, $parent=null)
{
$cat = new Category();
if ($parameters['slug']=='_')
{
$cat = new Category(Configuration::get('PS_HOME_CATEGORY'));
}
else
{
if ($parent == null) //it's a new root
{
//$cat->is_root_category = 1;
$cat->id_parent == Configuration::get('PS_HOME_CATEGORY');
}
else {
//echo "Forced parent: $parent\n";
$cat->id_parent = $parent;
}
$cat->link_rewrite = ctr_psvla($parameters['slug']);
$cat->name = ctr_psvla($parameters['label']);
$cat->description = ctr_psvla($parameters['description']);
$cat->save();
}
if ($parent == null) //it's a new root
{
// Configuration::set('PS_ROOT_CATEGORY', $cat->id);
}
bookkeeper::getInstance()->storekey('category-mapping', $parameters['original_id'], $cat->id);
return $cat->id;
}
/**
create a category tree and return an exhaustive mapping
category names => ID
Expected format:
same as for ctr_ps_create_category with an additional
[children] entry allowing recursion
**/
function ctr_ps_create_category_tree($tree, $forced_parent=null)
{
$firstrun = null;
foreach ($tree as $wood)
{
$id = ctr_ps_create_category($wood,$forced_parent);
if ($firstrun == null) $firstrun = $id;
//echo "Created category with ID $id.\n";
if (isset($wood['children']))
{
//echo "Proceeding with ". count($wood['children'])." kids of $id …\n";
ctr_ps_create_category_tree($wood['children'], $id);
}
else
{
//echo "Category $id has no children.\n";
}
}
echo "Done creating all categories.\n";
return $firstrun;
}