netplayer
10/2/2018 - 2:07 AM

Library to manipulate Prestashop from scripts

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;
}