CLance Posted March 12, 2014 Share Posted March 12, 2014 I have this notification message: Strict Standards: Declaration of ProductCore::validateField() should be compatible with that of ObjectModelCore::validateField() in /home/eviewtra/public_html/denmall/classes/Product.php on line 0 I have a blank product page, but I do not know how to solve this. Please help me or guide me. Thanks Link to comment Share on other sites More sharing options...
math_php Posted March 12, 2014 Share Posted March 12, 2014 Hello validateField() in line 0 ? You should look at product.php to see if file is not corrupted. Or show us first few lines. When did this occurs ? Did you modify core ? Regards Link to comment Share on other sites More sharing options...
CLance Posted March 12, 2014 Author Share Posted March 12, 2014 (edited) I did a lot of customization. But I am not sure when this happen.Actually if I ignore the error, the webpage still look like usual, I just want to solve this to avoid any problem in future. I am using Prestashop 1.5.6.1 This is the code in shop/product.php <?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 [email protected] 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 <[email protected]> * @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 */ /** * This file will be removed in 1.6 * You have to use index.php?controller=page_name instead of this page * * @deprecated 1.5.0 */ require(dirname(__FILE__).'/config/config.inc.php'); Tools::displayFileAsDeprecated(); Tools::redirect('index.php?controller=product'.((count($_GET) || count($_POST)) ? '&'.http_build_query(array_merge($_GET, $_POST), '', '&') : ''), __PS_BASE_URI__, null, 'HTTP/1.1 301 Moved Permanently'); You can have a look on my website:http://eviewtrading.com/denmall/denmall/ Edited March 12, 2014 by Lance Chan (see edit history) Link to comment Share on other sites More sharing options...
math_php Posted March 12, 2014 Share Posted March 12, 2014 I meant shop/classes/Product.php not shop/Product.php Link to comment Share on other sites More sharing options...
CLance Posted March 13, 2014 Author Share Posted March 13, 2014 Ops. Sorry. Here you are the full version. <?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 [email protected] 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 <[email protected]> * @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 */ /** * @deprecated 1.5.0.1 */ define('_CUSTOMIZE_FILE_', 0); /** * @deprecated 1.5.0.1 */ define('_CUSTOMIZE_TEXTFIELD_', 1); class ProductCore extends ObjectModel { /** @var string Tax name */ public $tax_name; /** @var string Tax rate */ public $tax_rate; /** @var integer Manufacturer id */ public $id_manufacturer; /** @var integer Supplier id */ public $id_supplier; /** @var integer default Category id */ public $id_category_default; /** @var integer default Shop id */ public $id_shop_default; /** @var string Manufacturer name */ public $manufacturer_name; /** @var string Supplier name */ public $supplier_name; /** @var string Name */ public $name; /** @var string Long description */ public $description; /** @var string Short description */ public $description_short; /** @var integer Quantity available */ public $quantity = 0; /** @var integer Minimal quantity for add to cart */ public $minimal_quantity = 1; /** @var string available_now */ public $available_now; /** @var string available_later */ public $available_later; /** @var float Price in euros */ public $price = 0; /** @var float Additional shipping cost */ public $additional_shipping_cost = 0; /** @var float Wholesale Price in euros */ public $wholesale_price = 0; /** @var boolean on_sale */ public $on_sale = false; /** @var boolean online_only */ public $online_only = false; /** @var string unity */ public $unity = null; /** @var float price for product's unity */ public $unit_price; /** @var float price for product's unity ratio */ public $unit_price_ratio = 0; /** @var float Ecotax */ public $ecotax = 0; /** @var string Reference */ public $reference; /** @var string Supplier Reference */ public $supplier_reference; /** @var string Location */ public $location; /** @var string Width in default width unit */ public $width = 0; /** @var string Height in default height unit */ public $height = 0; /** @var string Depth in default depth unit */ public $depth = 0; /** @var string Weight in default weight unit */ public $weight = 0; /** @var string Ean-13 barcode */ public $ean13; /** @var string Upc barcode */ public $upc; /** @var string Friendly URL */ public $link_rewrite; /** @var string Meta tag description */ public $meta_description; /** @var string Meta tag keywords */ public $meta_keywords; /** @var string Meta tag title */ public $meta_title; /** @var boolean Product statuts */ public $quantity_discount = 0; /** @var boolean Product customization */ public $customizable; /** @var boolean Product is new */ public $new = null; /** @var integer Number of uploadable files (concerning customizable products) */ public $uploadable_files; /** @var int Number of text fields */ public $text_fields; /** @var boolean Product statuts */ public $active = true; /** @var boolean Product statuts */ public $redirect_type = ''; /** @var boolean Product statuts */ public $id_product_redirected = 0; /** @var boolean Product available for order */ public $available_for_order = true; /** @var string Object available order date */ public $available_date = '0000-00-00'; /** @var enum Product condition (new, used, refurbished) */ public $condition; /** @var boolean Show price of Product */ public $show_price = true; /** @var boolean is the product indexed in the search index? */ public $indexed = 0; /** @var string ENUM('both', 'catalog', 'search', 'none') front office visibility */ public $visibility; /** @var string Object creation date */ public $date_add; /** @var string Object last modification date */ public $date_upd; /*** @var array Tags */ public $tags; public $id_tax_rules_group = 1; /** * We keep this variable for retrocompatibility for themes * @deprecated 1.5.0 */ public $id_color_default = 0; /** * @since 1.5.0 * @var boolean Tells if the product uses the advanced stock management */ public $advanced_stock_management = 0; public $out_of_stock; public $depends_on_stock; public $isFullyLoaded = false; public $cache_is_pack; public $cache_has_attachments; public $is_virtual; public $cache_default_attribute; /** * @var string If product is populated, this property contain the rewrite link of the default category */ public $category; public static $_taxCalculationMethod = null; protected static $_prices = array(); protected static $_pricesLevel2 = array(); protected static $_incat = array(); protected static $_cart_quantity = array(); protected static $_tax_rules_group = array(); protected static $_cacheFeatures = array(); protected static $_frontFeaturesCache = array(); protected static $producPropertiesCache = array(); /** @var array cache stock data in getStock() method */ protected static $cacheStock = array(); public static $definition = array( 'table' => 'product', 'primary' => 'id_product', 'multilang' => true, 'multilang_shop' => true, 'fields' => array( // Classic fields 'id_shop_default' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'id_manufacturer' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'id_supplier' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'reference' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 32), 'supplier_reference' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 32), 'location' => array('type' => self::TYPE_STRING, 'validate' => 'isReference', 'size' => 64), 'width' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'), 'height' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'), 'depth' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'), 'weight' => array('type' => self::TYPE_FLOAT, 'validate' => 'isUnsignedFloat'), 'quantity_discount' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'), 'ean13' => array('type' => self::TYPE_STRING, 'validate' => 'isEan13', 'size' => 13), 'upc' => array('type' => self::TYPE_STRING, 'validate' => 'isUpc', 'size' => 12), 'cache_is_pack' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'), 'cache_has_attachments' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'), 'is_virtual' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'), /* Shop fields */ 'id_category_default' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'), 'id_tax_rules_group' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'), 'on_sale' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'online_only' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'ecotax' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'), 'minimal_quantity' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'), 'price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice', 'required' => true), 'wholesale_price' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'), 'unity' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isString'), 'unit_price_ratio' => array('type' => self::TYPE_FLOAT, 'shop' => true), 'additional_shipping_cost' => array('type' => self::TYPE_FLOAT, 'shop' => true, 'validate' => 'isPrice'), 'customizable' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'), 'text_fields' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'), 'uploadable_files' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'), 'active' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'redirect_type' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isString'), 'id_product_redirected' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedId'), 'available_for_order' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'available_date' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'), 'condition' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isGenericName', 'values' => array('new', 'used', 'refurbished'), 'default' => 'new'), 'show_price' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'indexed' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'visibility' => array('type' => self::TYPE_STRING, 'shop' => true, 'validate' => 'isProductVisibility', 'values' => array('both', 'catalog', 'search', 'none'), 'default' => 'both'), 'cache_default_attribute' => array('type' => self::TYPE_INT, 'shop' => true), 'advanced_stock_management' => array('type' => self::TYPE_BOOL, 'shop' => true, 'validate' => 'isBool'), 'date_add' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'), 'date_upd' => array('type' => self::TYPE_DATE, 'shop' => true, 'validate' => 'isDateFormat'), /* Lang fields */ 'meta_description' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255), 'meta_keywords' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255), 'meta_title' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 128), 'link_rewrite' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isLinkRewrite', 'required' => true, 'size' => 128), 'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isCatalogName', 'required' => true, 'size' => 128), 'description' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isString'), 'description_short' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isString'), 'available_now' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'size' => 255), 'available_later' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'IsGenericName', 'size' => 255), ), 'associations' => array( 'manufacturer' => array('type' => self::HAS_ONE), 'supplier' => array('type' => self::HAS_ONE), 'default_category' => array('type' => self::HAS_ONE, 'field' => 'id_category_default', 'object' => 'Category'), 'tax_rules_group' => array('type' => self::HAS_ONE), 'categories' => array('type' => self::HAS_MANY, 'field' => 'id_category', 'object' => 'Category', 'association' => 'category_product'), 'stock_availables' => array('type' => self::HAS_MANY, 'field' => 'id_stock_available', 'object' => 'StockAvailable', 'association' => 'stock_availables'), ), ); protected $webserviceParameters = array( 'objectMethods' => array( 'add' => 'addWs', 'update' => 'updateWs' ), 'objectNodeNames' => 'products', 'fields' => array( 'id_manufacturer' => array( 'xlink_resource' => 'manufacturers' ), 'id_supplier' => array( 'xlink_resource' => 'suppliers' ), 'id_category_default' => array( 'xlink_resource' => 'categories' ), 'new' => array(), 'cache_default_attribute' => array(), 'id_default_image' => array( 'getter' => 'getCoverWs', 'setter' => 'setCoverWs', 'xlink_resource' => array( 'resourceName' => 'images', 'subResourceName' => 'products' ) ), 'id_default_combination' => array( 'getter' => 'getWsDefaultCombination', 'setter' => 'setWsDefaultCombination', 'xlink_resource' => array( 'resourceName' => 'combinations' ) ), 'id_tax_rules_group' => array( 'xlink_resource' => array( 'resourceName' => 'tax_rules_group' ) ), 'position_in_category' => array( 'getter' => 'getWsPositionInCategory', 'setter' => false ), 'manufacturer_name' => array( 'getter' => 'getWsManufacturerName', 'setter' => false ), 'quantity' => array( 'getter' => false, 'setter' => false ), 'type' => array( 'getter' => 'getWsType', 'setter' => false, ), ), 'associations' => array( 'categories' => array( 'resource' => 'category', 'fields' => array( 'id' => array('required' => true), ) ), 'images' => array( 'resource' => 'image', 'fields' => array('id' => array()) ), 'combinations' => array( 'resource' => 'combinations', 'fields' => array( 'id' => array('required' => true), ) ), 'product_option_values' => array( 'resource' => 'product_options_values', 'fields' => array( 'id' => array('required' => true), ) ), 'product_features' => array( 'resource' => 'product_feature', 'fields' => array( 'id' => array('required' => true), 'id_feature_value' => array( 'required' => true, 'xlink_resource' => 'product_feature_values' ), ) ), 'tags' => array('resource' => 'tag', 'fields' => array( 'id' => array('required' => true), )), 'stock_availables' => array('resource' => 'stock_available', 'fields' => array( 'id' => array('required' => true), 'id_product_attribute' => array('required' => true), ), 'setter' => false ), 'accessories' => array( 'resource' => 'product', 'fields' => array( 'id' => array( 'required' => true, 'xlink_resource' => 'product'), ) ), 'product_bundle' => array( 'resource' => 'products', 'fields' => array( 'id' => array('required' => true), 'quantity' => array(), ), ), ), ); const CUSTOMIZE_FILE = 0; const CUSTOMIZE_TEXTFIELD = 1; /** * Note: prefix is "PTYPE" because TYPE_ is used in ObjectModel (definition) */ const PTYPE_SIMPLE = 0; const PTYPE_PACK = 1; const PTYPE_VIRTUAL = 2; public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null) { parent::__construct($id_product, $id_lang, $id_shop); if (!$context) $context = Context::getContext(); if ($full && $this->id) { $this->isFullyLoaded = $full; $this->tax_name = 'deprecated'; // The applicable tax may be BOTH the product one AND the state one (moreover this variable is some deadcode) $this->manufacturer_name = Manufacturer::getNameById((int)$this->id_manufacturer); $this->supplier_name = Supplier::getNameById((int)$this->id_supplier); $address = null; if (is_object($context->cart) && $context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')} != null) $address = $context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}; $this->tax_rate = $this->getTaxesRate(new Address($address)); $this->new = $this->isNew(); // keep base price $this->base_price = $this->price; $this->price = Product::getPriceStatic((int)$this->id, false, null, 6, null, false, true, 1, false, null, null, null, $this->specificPrice); $this->unit_price = ($this->unit_price_ratio != 0 ? $this->price / $this->unit_price_ratio : 0); if ($this->id) $this->tags = Tag::getProductTags((int)$this->id); $this->loadStockData(); } if ($this->id_category_default) $this->category = Category::getLinkRewrite((int)$this->id_category_default, (int)$id_lang); } /** * @see ObjectModel::getFieldsShop() * @return array */ public function getFieldsShop() { $fields = parent::getFieldsShop(); if (is_null($this->update_fields) || (!empty($this->update_fields['price']) && !empty($this->update_fields['unit_price']))) $fields['unit_price_ratio'] = (float)$this->unit_price > 0 ? $this->price / $this->unit_price : 0; return $fields; } public function add($autodate = true, $null_values = false) { if (!parent::add($autodate, $null_values)) return false; if ($this->getType() == Product::PTYPE_VIRTUAL) StockAvailable::setProductOutOfStock((int)$this->id, 1); else StockAvailable::setProductOutOfStock((int)$this->id, 2); $this->setGroupReduction(); Hook::exec('actionProductSave', array('id_product' => $this->id)); return true; } public function update($null_values = false) { $return = parent::update($null_values); $this->setGroupReduction(); Hook::exec('actionProductSave', array('id_product' => $this->id)); return $return; } public static function initPricesComputation($id_customer = null) { if ($id_customer) { $customer = new Customer((int)$id_customer); if (!Validate::isLoadedObject($customer)) die(Tools::displayError()); self::$_taxCalculationMethod = Group::getPriceDisplayMethod((int)$customer->id_default_group); } else self::$_taxCalculationMethod = Group::getPriceDisplayMethod(Group::getCurrent()->id); } public static function getTaxCalculationMethod($id_customer = null) { if (self::$_taxCalculationMethod === null || $id_customer !== null) Product::initPricesComputation($id_customer); return (int)self::$_taxCalculationMethod; } /** * Move a product inside its category * @param boolean $way Up (1) or Down (0) * @param integer $position * return boolean Update result */ public function updatePosition($way, $position) { if (!$res = Db::getInstance()->executeS(' SELECT cp.`id_product`, cp.`position`, cp.`id_category` FROM `'._DB_PREFIX_.'category_product` cp WHERE cp.`id_category` = '.(int)Tools::getValue('id_category', 1).' ORDER BY cp.`position` ASC' )) return false; foreach ($res as $product) if ((int)$product['id_product'] == (int)$this->id) $moved_product = $product; if (!isset($moved_product) || !isset($position)) return false; // < and > statements rather than BETWEEN operator // since BETWEEN is treated differently according to databases return (Db::getInstance()->execute(' UPDATE `'._DB_PREFIX_.'category_product` SET `position`= `position` '.($way ? '- 1' : '+ 1').' WHERE `position` '.($way ? '> '.(int)$moved_product['position'].' AND `position` <= '.(int)$position : '< '.(int)$moved_product['position'].' AND `position` >= '.(int)$position).' AND `id_category`='.(int)$moved_product['id_category']) && Db::getInstance()->execute(' UPDATE `'._DB_PREFIX_.'category_product` SET `position` = '.(int)$position.' WHERE `id_product` = '.(int)$moved_product['id_product'].' AND `id_category`='.(int)$moved_product['id_category'])); } /* * Reorder product position in category $id_category. * Call it after deleting a product from a category. * * @param int $id_category */ public static function cleanPositions($id_category) { $return = true; $result = Db::getInstance()->executeS(' SELECT `id_product` FROM `'._DB_PREFIX_.'category_product` WHERE `id_category` = '.(int)$id_category.' ORDER BY `position` '); $total = count($result); for ($i = 0; $i < $total; $i++) $return &= Db::getInstance()->update('category_product', array( 'position' => $i, ), '`id_category` = '.(int)$id_category.' AND `id_product` = '.(int)$result[$i]['id_product']); return $return; } /** * Get the default attribute for a product * * @return int Attributes list */ public static function getDefaultAttribute($id_product, $minimum_quantity = 0) { static $combinations = array(); if (!Combination::isFeatureActive()) return 0; if (!isset($combinations[$id_product])) $combinations[$id_product] = array(); if (isset($combinations[$id_product][$minimum_quantity])) return $combinations[$id_product][$minimum_quantity]; $sql = 'SELECT pa.id_product_attribute FROM '._DB_PREFIX_.'product_attribute pa '.Shop::addSqlAssociation('product_attribute', 'pa').' '.($minimum_quantity > 0 ? Product::sqlStock('pa', 'pa') : ''). ' WHERE product_attribute_shop.default_on = 1 ' .($minimum_quantity > 0 ? ' AND IFNULL(stock.quantity, 0) >= '.(int)$minimum_quantity : ''). ' AND pa.id_product = '.(int)$id_product; $result = Db::getInstance()->getValue($sql); if (!$result) { $sql = 'SELECT pa.id_product_attribute FROM '._DB_PREFIX_.'product_attribute pa '.Shop::addSqlAssociation('product_attribute', 'pa').' '.($minimum_quantity > 0 ? Product::sqlStock('pa', 'pa') : ''). ' WHERE pa.id_product = '.(int)$id_product .($minimum_quantity > 0 ? ' AND IFNULL(stock.quantity, 0) >= '.(int)$minimum_quantity : ''); $result = Db::getInstance()->getValue($sql); } if (!$result) { $sql = 'SELECT pa.id_product_attribute FROM '._DB_PREFIX_.'product_attribute pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE product_attribute_shop.`default_on` = 1 AND pa.id_product = '.(int)$id_product; $result = Db::getInstance()->getValue($sql); } if (!$result) { $sql = 'SELECT pa.id_product_attribute FROM '._DB_PREFIX_.'product_attribute pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.id_product = '.(int)$id_product; $result = Db::getInstance()->getValue($sql); } $combinations[$id_product][$minimum_quantity] = $result; return $result; } public function setAvailableDate($available_date = '0000-00-00') { if (Validate::isDateFormat($available_date) && $this->available_date != $available_date) { $this->available_date = $available_date; return $this->update(); } return false; } public static function updateIsVirtual($id_product) { Db::getInstance()->update('product', array( 'is_virtual' => (int)$id_product, ), 'id_product = '.(int)$id_product); } /** * @see ObjectModel::validateFieldsLang() */ public function validateFieldsLang($die = true, $error_return = false) { $limit = (int)Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT'); if ($limit <= 0) $limit = 800; $this->def['fields']['description_short']['size'] = $limit; return parent::validateFieldsLang($die, $error_return); } /** * @see ObjectModel::validateField() */ public function validateField($field, $value, $id_lang = null) { $value = ($field == 'description_short' ? strip_tags($value) : $value); return parent::validateField($field, $value, $id_lang); } public function toggleStatus() { //test if the product is active and if redirect_type is empty string and set default value to id_product_redirected & redirect_type // /!\ after parent::toggleStatus() active will be false, that why we set 404 by default if ($this->active) { //case where active will be false after parent::toggleStatus() $this->id_product_redirected = 0; $this->redirect_type = '404'; } else { //case where active will be true after parent::toggleStatus() $this->id_product_redirected = 0; $this->redirect_type = ''; } return parent::toggleStatus(); } public function delete() { /* * @since 1.5.0 * It is NOT possible to delete a product if there are currently: * - physical stock for this product * - supply order(s) for this product */ if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && $this->advanced_stock_management) { $stock_manager = StockManagerFactory::getManager(); $physical_quantity = $stock_manager->getProductPhysicalQuantities($this->id, 0); $real_quantity = $stock_manager->getProductRealQuantities($this->id, 0); if ($physical_quantity > 0) return false; if ($real_quantity > $physical_quantity) return false; } $result = parent::delete(); // Removes the product from StockAvailable, for the current shop StockAvailable::removeProductFromStockAvailable($this->id); $result &= ($this->deleteProductAttributes() && $this->deleteImages() && $this->deleteSceneProducts()); // If there are still entries in product_shop, don't remove completly the product if ($this->hasMultishopEntries()) return true; Product::cleanPositions($this->id); Hook::exec('actionProductDelete', array('product' => $this)); if (!$result || !GroupReduction::deleteProductReduction($this->id) || !$this->deleteCategories(true) || !$this->deleteProductFeatures() || !$this->deleteTags() || !$this->deleteCartProducts() || !$this->deleteAttributesImpacts() || !$this->deleteAttachments() || !$this->deleteCustomization() || !SpecificPrice::deleteByProductId((int)$this->id) || !$this->deletePack() || !$this->deleteProductSale() || !$this->deleteSearchIndexes() || !$this->deleteAccessories() || !$this->deleteFromAccessories() || !$this->deleteFromSupplier() || !$this->deleteDownload() || !$this->deleteFromCartRules()) return false; return true; } public function deleteSelection($products) { $return = 1; if (is_array($products) && ($count = count($products))) { // Deleting products can be quite long on a cheap server. Let's say 1.5 seconds by product (I've seen it!). if (intval(ini_get('max_execution_time')) < round($count * 1.5)) ini_set('max_execution_time', round($count * 1.5)); foreach ($products as $id_product) { $product = new Product((int)$id_product); $return &= $product->delete(); } } return $return; } public function deleteFromCartRules() { CartRule::cleanProductRuleIntegrity('products', $this->id); return true; } public function deleteFromSupplier() { return Db::getInstance()->delete('product_supplier', 'id_product = '.(int)$this->id); } /** * addToCategories add this product to the category/ies if not exists. * * @param mixed $categories id_category or array of id_category * @return boolean true if succeed */ public function addToCategories($categories = array()) { if (empty($categories)) return false; if (!is_array($categories)) $categories = array($categories); if (!count($categories)) return false; $categories = array_map('intval', $categories); $current_categories = $this->getCategories(); $current_categories = array_map('intval', $current_categories); // for new categ, put product at last position $res_categ_new_pos = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT id_category, MAX(position)+1 newPos FROM `'._DB_PREFIX_.'category_product` WHERE `id_category` IN('.implode(',', $categories).') GROUP BY id_category'); foreach ($res_categ_new_pos as $array) $new_categories[(int)$array['id_category']] = (int)$array['newPos']; $new_categ_pos = array(); foreach ($categories as $id_category) $new_categ_pos[$id_category] = isset($new_categories[$id_category]) ? $new_categories[$id_category] : 0; $product_cats = array(); foreach ($categories as $new_id_categ) if (!in_array($new_id_categ, $current_categories)) $product_cats[] = array( 'id_category' => (int)$new_id_categ, 'id_product' => (int)$this->id, 'position' => (int)$new_categ_pos[$new_id_categ], ); Db::getInstance()->insert('category_product', $product_cats); return true; } /** * Update categories to index product into * * @param string $productCategories Categories list to index product into * @param boolean $keeping_current_pos (deprecated, no more used) * @return array Update/insertion result */ public function updateCategories($categories, $keeping_current_pos = false) { if (empty($categories)) return false; $result = Db::getInstance()->executeS(' SELECT c.`id_category` FROM `'._DB_PREFIX_.'category_product` cp LEFT JOIN `'._DB_PREFIX_.'category` c ON (c.`id_category` = cp.`id_category`) '.Shop::addSqlAssociation('category', 'c', true, null, true).' WHERE cp.`id_category` NOT IN ('.implode(',', array_map('intval', $categories)).') AND cp.id_product = '.$this->id ); foreach ($result as $categ_to_delete) $this->deleteCategory($categ_to_delete['id_category']); // if none are found, it's an error if (!is_array($result)) return false; if (!$this->addToCategories($categories)) return false; SpecificPriceRule::applyAllRules(array((int)$this->id)); return true; } /** * deleteCategory delete this product from the category $id_category * * @param mixed $id_category * @param mixed $clean_positions * @return boolean */ public function deleteCategory($id_category, $clean_positions = true) { $result = Db::getInstance()->executeS( 'SELECT `id_category` FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.(int)$this->id.' AND id_category = '.(int)$id_category.'' ); $return = Db::getInstance()->delete('category_product', 'id_product = '.(int)$this->id.' AND id_category = '.(int)$id_category); if ($clean_positions === true) foreach ($result as $row) $this->cleanPositions((int)$row['id_category']); SpecificPriceRule::applyAllRules(array((int)$this->id)); return $return; } /** * Delete all association to category where product is indexed * * @param boolean $clean_positions clean category positions after deletion * @return array Deletion result */ public function deleteCategories($clean_positions = false) { if ($clean_positions === true) $result = Db::getInstance()->executeS( 'SELECT `id_category` FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.(int)$this->id ); $return = Db::getInstance()->delete('category_product', 'id_product = '.(int)$this->id); if ($clean_positions === true && is_array($result)) foreach ($result as $row) $return &= $this->cleanPositions((int)$row['id_category']); return $return; } /** * Delete products tags entries * * @return array Deletion result */ public function deleteTags() { return Db::getInstance()->delete('product_tag', 'id_product = '.(int)$this->id) && Db::getInstance()->delete('tag', 'id_tag NOT IN (SELECT id_tag FROM '._DB_PREFIX_.'product_tag)'); } /** * Delete product from cart * * @return array Deletion result */ public function deleteCartProducts() { return Db::getInstance()->delete('cart_product', 'id_product = '.(int)$this->id); } /** * Delete product images from database * * @return bool success */ public function deleteImages() { $result = Db::getInstance()->executeS(' SELECT `id_image` FROM `'._DB_PREFIX_.'image` WHERE `id_product` = '.(int)$this->id ); $status = true; if ($result) foreach ($result as $row) { $image = new Image($row['id_image']); $status &= $image->delete(); } return $status; } /** * @deprecated 1.5.0 Use Combination::getPrice() */ public static function getProductAttributePrice($id_product_attribute) { return Combination::getPrice($id_product_attribute); } /** * Get all available products * * @param integer $id_lang Language id * @param integer $start Start number * @param integer $limit Number of products to return * @param string $order_by Field for ordering * @param string $order_way Way for ordering (ASC or DESC) * @return array Products details */ public static function getProducts($id_lang, $start, $limit, $order_by, $order_way, $id_category = false, $only_active = false, Context $context = null) { if (!$context) $context = Context::getContext(); $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) die (Tools::displayError()); if ($order_by == 'id_product' || $order_by == 'price' || $order_by == 'date_add' || $order_by == 'date_upd') $order_by_prefix = 'p'; else if ($order_by == 'name') $order_by_prefix = 'pl'; else if ($order_by == 'position') $order_by_prefix = 'c'; if (strpos($order_by, '.') > 0) { $order_by = explode('.', $order_by); $order_by_prefix = $order_by[0]; $order_by = $order_by[1]; } $sql = 'SELECT p.*, product_shop.*, pl.* , m.`name` AS manufacturer_name, s.`name` AS supplier_name FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.Shop::addSqlRestrictionOnLang('pl').') LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`) LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (s.`id_supplier` = p.`id_supplier`)'. ($id_category ? 'LEFT JOIN `'._DB_PREFIX_.'category_product` c ON (c.`id_product` = p.`id_product`)' : '').' WHERE pl.`id_lang` = '.(int)$id_lang. ($id_category ? ' AND c.`id_category` = '.(int)$id_category : ''). ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : ''). ($only_active ? ' AND product_shop.`active` = 1' : '').' ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').'`'.pSQL($order_by).'` '.pSQL($order_way). ($limit > 0 ? ' LIMIT '.(int)$start.','.(int)$limit : ''); $rq = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); if ($order_by == 'price') Tools::orderbyPrice($rq, $order_way); foreach ($rq as &$row) $row = Product::getTaxesInformations($row); return ($rq); } public static function getSimpleProducts($id_lang, Context $context = null) { if (!$context) $context = Context::getContext(); $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; $sql = 'SELECT p.`id_product`, pl.`name` FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` '.Shop::addSqlRestrictionOnLang('pl').') WHERE pl.`id_lang` = '.(int)$id_lang.' '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').' ORDER BY pl.`name`'; return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } public function isNew() { $result = Db::getInstance()->executeS(' SELECT p.id_product FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE p.id_product = '.(int)$this->id.' AND DATEDIFF( product_shop.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0 '); return count($result) > 0; } public function productAttributeExists($attributes_list, $current_product_attribute = false, Context $context = null, $all_shops = false, $return_id = false) { if (!Combination::isFeatureActive()) return false; if ($context === null) $context = Context::getContext(); $result = Db::getInstance()->executeS( 'SELECT pac.`id_attribute`, pac.`id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` pa JOIN `'._DB_PREFIX_.'product_attribute_shop` pas ON (pas.id_product_attribute = pa.id_product_attribute) LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_product_attribute` = pa.`id_product_attribute`) WHERE 1 '.(!$all_shops ? ' AND pas.id_shop ='.(int)$context->shop->id : '').' AND pa.`id_product` = '.(int)$this->id. ($all_shops ? ' GROUP BY pac.id_attribute, pac.id_product_attribute ' : '') ); /* If something's wrong */ if (!$result || empty($result)) return false; /* Product attributes simulation */ $product_attributes = array(); foreach ($result as $product_attribute) $product_attributes[$product_attribute['id_product_attribute']][] = $product_attribute['id_attribute']; /* Checking product's attribute existence */ foreach ($product_attributes as $key => $product_attribute) if (count($product_attribute) == count($attributes_list)) { $diff = false; for ($i = 0; $diff == false && isset($product_attribute[$i]); $i++) if (!in_array($product_attribute[$i], $attributes_list) || $key == $current_product_attribute) $diff = true; if (!$diff) { if ($return_id) return $key; return true; } } return false; } /** * addProductAttribute is deprecated * * The quantity params now set StockAvailable for the current shop with the specified quantity * The supplier_reference params now set the supplier reference of the default supplier of the product if possible * * @see StockManager if you want to manage real stock * @see StockAvailable if you want to manage available quantities for sale on your shop(s) * @see ProductSupplier for manage supplier reference(s) * * @deprecated since 1.5.0 */ public function addProductAttribute($price, $weight, $unit_impact, $ecotax, $quantity, $id_images, $reference, $id_supplier = null, $ean13, $default, $location = null, $upc = null, $minimal_quantity = 1) { Tools::displayAsDeprecated(); $id_product_attribute = $this->addAttribute( $price, $weight, $unit_impact, $ecotax, $id_images, $reference, $ean13, $default, $location, $upc, $minimal_quantity ); if (!$id_product_attribute) return false; StockAvailable::setQuantity($this->id, $id_product_attribute, $quantity); //Try to set the default supplier reference $this->addSupplierReference($id_supplier, $id_product_attribute); return $id_product_attribute; } public function generateMultipleCombinations($combinations, $attributes) { $attributes_list = array(); $res = true; $default_on = 1; foreach ($combinations as $key => $combination) { $id_combination = (int)$this->productAttributeExists($attributes[$key], false, null, true, true); $obj = new Combination($id_combination); if ($id_combination) { $obj->minimal_quantity = 1; $obj->available_date = '0000-00-00'; } foreach ($combination as $field => $value) $obj->$field = $value; $obj->default_on = $default_on; $default_on = 0; $obj->save(); if (!$id_combination) { $attribute_list = array(); foreach ($attributes[$key] as $id_attribute) $attribute_list[] = array( 'id_product_attribute' => (int)$obj->id, 'id_attribute' => (int)$id_attribute ); $res &= Db::getInstance()->insert('product_attribute_combination', $attribute_list); } } return $res; } /** * @param integer $quantity DEPRECATED * @param string $supplier_reference DEPRECATED */ public function addCombinationEntity($wholesale_price, $price, $weight, $unit_impact, $ecotax, $quantity, $id_images, $reference, $id_supplier, $ean13, $default, $location = null, $upc = null, $minimal_quantity = 1, array $id_shop_list = array()) { $id_product_attribute = $this->addAttribute( $price, $weight, $unit_impact, $ecotax, $id_images, $reference, $ean13, $default, $location, $upc, $minimal_quantity, $id_shop_list); $this->addSupplierReference($id_supplier, $id_product_attribute); $result = ObjectModel::updateMultishopTable('Combination', array( 'wholesale_price' => (float)$wholesale_price, ), 'a.id_product_attribute = '.(int)$id_product_attribute); if (!$id_product_attribute || !$result) return false; return $id_product_attribute; } public function addProductAttributeMultiple($attributes, $set_default = true) { Tools::displayAsDeprecated(); $return = array(); $default_value = 1; foreach ($attributes as &$attribute) { $obj = new Combination(); foreach ($attribute as $key => $value) $obj->$key = $value; if ($set_default) { $obj->default_on = $default_value; $default_value = 0; // if we add a combination for this shop and this product does not use the combination feature in other shop, // we clone the default combination in every shop linked to this product if (!$this->hasAttributesInOtherShops()) { $id_shop_list_array = Product::getShopsByProduct($this->id); $id_shop_list = array(); foreach ($id_shop_list_array as $array_shop) $id_shop_list[] = $array_shop['id_shop']; $obj->id_shop_list = $id_shop_list; } } $obj->add(); $return[] = $obj->id; } return $return; } /** * Del all default attributes for product */ public function deleteDefaultAttributes() { return ObjectModel::updateMultishopTable('Combination', array( 'default_on' => 0, ), 'id_product = '.(int)$this->id); } public function setDefaultAttribute($id_product_attribute) { $result = ObjectModel::updateMultishopTable('Combination', array( 'default_on' => 1 ), '`id_product` = '.(int)$this->id.' AND a.`id_product_attribute` = '.(int)$id_product_attribute); $result &= ObjectModel::updateMultishopTable('product', array( 'cache_default_attribute' => (int)$id_product_attribute, ), 'a.`id_product` = '.(int)$this->id); $this->cache_default_attribute = (int)$id_product_attribute; return $result; } public static function updateDefaultAttribute($id_product) { $id_default_attribute = (int)Product::getDefaultAttribute($id_product); $result = Db::getInstance()->update('product_shop', array( 'cache_default_attribute' => $id_default_attribute, ), 'id_product = '.(int)$id_product); $result &= Db::getInstance()->update('product', array( 'cache_default_attribute' => $id_default_attribute, ), 'id_product = '.(int)$id_product); return $result; } /** * Update a product attribute * * @deprecated since 1.5 * @see updateAttribute() to use instead * @see ProductSupplier for manage supplier reference(s) * */ public function updateProductAttribute($id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax, $id_images, $reference, $id_supplier = null, $ean13, $default, $location = null, $upc = null, $minimal_quantity, $available_date) { Tools::displayAsDeprecated(); $return = $this->updateAttribute( $id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax, $id_images, $reference, $ean13, $default, $location = null, $upc = null, $minimal_quantity, $available_date ); $this->addSupplierReference($id_supplier, $id_product_attribute); return $return; } /** * Sets Supplier Reference * * @param int $id_supplier * @param int $id_product_attribute * @param string $supplier_reference * @param float $price * @param int $id_currency */ public function addSupplierReference($id_supplier, $id_product_attribute, $supplier_reference = null, $price = null, $id_currency = null) { //in some case we need to add price without supplier reference if ($supplier_reference === null) $supplier_reference = ''; //Try to set the default supplier reference if ($id_supplier > 0) { $id_product_supplier = (int)ProductSupplier::getIdByProductAndSupplier($this->id, $id_product_attribute, $id_supplier); if (!$id_product_supplier) { //create new record $product_supplier_entity = new ProductSupplier(); $product_supplier_entity->id_product = (int)$this->id; $product_supplier_entity->id_product_attribute = (int)$id_product_attribute; $product_supplier_entity->id_supplier = (int)$id_supplier; $product_supplier_entity->product_supplier_reference = pSQL($supplier_reference); $product_supplier_entity->product_supplier_price_te = (int)$price; $product_supplier_entity->id_currency = (int)$id_currency; $product_supplier_entity->save(); } else { $product_supplier = new ProductSupplier((int)$id_product_supplier); $product_supplier->product_supplier_reference = pSQL($supplier_reference); $product_supplier->update(); } } } /** * Update a product attribute * * @param integer $id_product_attribute Product attribute id * @param float $wholesale_price Wholesale price * @param float $price Additional price * @param float $weight Additional weight * @param float $unit * @param float $ecotax Additional ecotax * @param integer $id_image Image id * @param string $reference Reference * @param string $ean13 Ean-13 barcode * @param int $default Default On * @param string $upc Upc barcode * @param string $minimal_quantity Minimal quantity * @return array Update result */ public function updateAttribute($id_product_attribute, $wholesale_price, $price, $weight, $unit, $ecotax, $id_images, $reference, $ean13, $default, $location = null, $upc = null, $minimal_quantity = null, $available_date = null, $update_all_fields = true, array $id_shop_list = array()) { $combination = new Combination($id_product_attribute); if (!$update_all_fields) $combination->setFieldsToUpdate(array( 'price' => !is_null($price), 'wholesale_price' => !is_null($wholesale_price), 'ecotax' => !is_null($ecotax), 'weight' => !is_null($weight), 'unit_price_impact' => !is_null($unit), 'default_on' => !is_null($default), 'minimal_quantity' => !is_null($minimal_quantity), 'available_date' => !is_null($available_date), )); $price = str_replace(',', '.', $price); $weight = str_replace(',', '.', $weight); $combination->price = (float)$price; $combination->wholesale_price = (float)$wholesale_price; $combination->ecotax = (float)$ecotax; $combination->weight = (float)$weight; $combination->unit_price_impact = (float)$unit; $combination->reference = pSQL($reference); $combination->location = pSQL($location); $combination->ean13 = pSQL($ean13); $combination->upc = pSQL($upc); $combination->default_on = (int)$default; $combination->minimal_quantity = (int)$minimal_quantity; $combination->available_date = $available_date ? pSQL($available_date) : '0000-00-00'; if (count($id_shop_list)) $combination->id_shop_list = $id_shop_list; $combination->save(); if (!empty($id_images)) $combination->setImages($id_images); Product::updateDefaultAttribute($this->id); Hook::exec('actionProductAttributeUpdate', array('id_product_attribute' => $id_product_attribute)); return true; } /** * Add a product attribute * @since 1.5.0.1 * * @param float $price Additional price * @param float $weight Additional weight * @param float $ecotax Additional ecotax * @param integer $id_images Image ids * @param string $reference Reference * @param string $location Location * @param string $ean13 Ean-13 barcode * @param boolean $default Is default attribute for product * @param integer $minimal_quantity Minimal quantity to add to cart * @return mixed $id_product_attribute or false */ public function addAttribute($price, $weight, $unit_impact, $ecotax, $id_images, $reference, $ean13, $default, $location = null, $upc = null, $minimal_quantity = 1, array $id_shop_list = array()) { if (!$this->id) return; $price = str_replace(',', '.', $price); $weight = str_replace(',', '.', $weight); $combination = new Combination(); $combination->id_product = (int)$this->id; $combination->price = (float)$price; $combination->ecotax = (float)$ecotax; $combination->quantity = 0; $combination->weight = (float)$weight; $combination->unit_price_impact = (float)$unit_impact; $combination->reference = pSQL($reference); $combination->location = pSQL($location); $combination->ean13 = pSQL($ean13); $combination->upc = pSQL($upc); $combination->default_on = (int)$default; $combination->minimal_quantity = (int)$minimal_quantity; // if we add a combination for this shop and this product does not use the combination feature in other shop, // we clone the default combination in every shop linked to this product if ($default && !$this->hasAttributesInOtherShops()) { $id_shop_list_array = Product::getShopsByProduct($this->id); foreach ($id_shop_list_array as $array_shop) $id_shop_list[] = $array_shop['id_shop']; } if (count($id_shop_list)) $combination->id_shop_list = $id_shop_list; $combination->add(); if (!$combination->id) return false; Product::updateDefaultAttribute($this->id); if (!empty($id_images)) $combination->setImages($id_images); return (int)$combination->id; } /** * @deprecated since 1.5.0 */ public function updateQuantityProductWithAttributeQuantity() { Tools::displayAsDeprecated(); return Db::getInstance()->execute(' UPDATE `'._DB_PREFIX_.'product` SET `quantity` = IFNULL( ( SELECT SUM(`quantity`) FROM `'._DB_PREFIX_.'product_attribute` WHERE `id_product` = '.(int)$this->id.' ), \'0\') WHERE `id_product` = '.(int)$this->id); } /** * Delete product attributes * * @return array Deletion result */ public function deleteProductAttributes() { Hook::exec('actionProductAttributeDelete', array('id_product_attribute' => 0, 'id_product' => $this->id, 'deleteAllAttributes' => true)); $result = true; $combinations = new Collection('Combination'); $combinations->where('id_product', '=', $this->id); foreach ($combinations as $combination) $result &= $combination->delete(); SpecificPriceRule::applyAllRules(array((int)$this->id)); return $result; } /** * Delete product attributes impacts * * @return Deletion result */ public function deleteAttributesImpacts() { return Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'attribute_impact` WHERE `id_product` = '.(int)$this->id ); } /** * Delete product features * * @return array Deletion result */ public function deleteProductFeatures() { SpecificPriceRule::applyAllRules(array((int)$this->id)); return $this->deleteFeatures(); } public static function updateCacheAttachment($id_product) { $value = (bool)Db::getInstance()->getValue(' SELECT id_attachment FROM '._DB_PREFIX_.'product_attachment WHERE id_product='.(int)$id_product); return Db::getInstance()->update( 'product', array('cache_has_attachments' => (int)$value), 'id_product = '.(int)$id_product ); } /** * Delete product attachments * * @return array Deletion result */ public function deleteAttachments() { $res = Db::getInstance()->execute(' DELETE FROM `'._DB_PREFIX_.'product_attachment` WHERE `id_product` = '.(int)$this->id ); Product::updateCacheAttachment((int)$this->id); return $res; } /** * Delete product customizations * * @return array Deletion result */ public function deleteCustomization() { return ( Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id ) && Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field_lang` WHERE `id_customization_field` NOT IN (SELECT id_customization_field FROM `'._DB_PREFIX_.'customization_field`)' ) ); } /** * Delete product pack details * * @return array Deletion result */ public function deletePack() { return Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'pack` WHERE `id_product_pack` = '.(int)$this->id.' OR `id_product_item` = '.(int)$this->id ); } /** * Delete product sales * * @return array Deletion result */ public function deleteProductSale() { return Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'product_sale` WHERE `id_product` = '.(int)$this->id ); } /** * Delete product in its scenes * * @return array Deletion result */ public function deleteSceneProducts() { return Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'scene_products` WHERE `id_product` = '.(int)$this->id ); } /** * Delete product indexed words * * @return array Deletion result */ public function deleteSearchIndexes() { return ( Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'search_index` WHERE `id_product` = '.(int)$this->id ) && Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'search_word` WHERE `id_word` NOT IN ( SELECT id_word FROM `'._DB_PREFIX_.'search_index` )' ) ); } /** * Add a product attributes combinaison * * @param integer $id_product_attribute Product attribute id * @param array $attributes Attributes to forge combinaison * @return array Insertion result * @deprecated since 1.5.0.7 */ public function addAttributeCombinaison($id_product_attribute, $attributes) { Tools::displayAsDeprecated(); if (!is_array($attributes)) die(Tools::displayError()); if (!count($attributes)) return false; $combination = new Combination((int)$id_product_attribute); return $combination->setAttributes($attributes); } public function addAttributeCombinationMultiple($id_attributes, $combinations) { Tools::displayAsDeprecated(); $attributes_list = array(); foreach ($id_attributes as $nb => $id_product_attribute) if (isset($combinations[$nb])) foreach ($combinations[$nb] as $id_attribute) $attributes_list[] = array( 'id_product_attribute' => (int)$id_product_attribute, 'id_attribute' => (int)$id_attribute, ); return Db::getInstance()->insert('product_attribute_combination', $attributes_list); } /** * Delete a product attributes combination * * @param integer $id_product_attribute Product attribute id * @return array Deletion result */ public function deleteAttributeCombination($id_product_attribute) { if (!$this->id || !$id_product_attribute || !is_numeric($id_product_attribute)) return false; Hook::exec( 'deleteProductAttribute', array( 'id_product_attribute' => $id_product_attribute, 'id_product' => $this->id, 'deleteAllAttributes' => false ) ); $combination = new Combination($id_product_attribute); $res = $combination->delete(); SpecificPriceRule::applyAllRules(array((int)$this->id)); return $res; } /** * Delete features * */ public function deleteFeatures() { // List products features $features = Db::getInstance()->executeS(' SELECT p.*, f.* FROM `'._DB_PREFIX_.'feature_product` as p LEFT JOIN `'._DB_PREFIX_.'feature_value` as f ON (f.`id_feature_value` = p.`id_feature_value`) WHERE `id_product` = '.(int)$this->id); foreach ($features as $tab) // Delete product custom features if ($tab['custom']) { Db::getInstance()->execute(' DELETE FROM `'._DB_PREFIX_.'feature_value` WHERE `id_feature_value` = '.(int)$tab['id_feature_value']); Db::getInstance()->execute(' DELETE FROM `'._DB_PREFIX_.'feature_value_lang` WHERE `id_feature_value` = '.(int)$tab['id_feature_value']); } // Delete product features $result = Db::getInstance()->execute(' DELETE FROM `'._DB_PREFIX_.'feature_product` WHERE `id_product` = '.(int)$this->id); SpecificPriceRule::applyAllRules(array((int)$this->id)); return ($result); } /** * Get all available product attributes resume * * @param integer $id_lang Language id * @return array Product attributes combinations */ public function getAttributesResume($id_lang, $attribute_value_separator = ' - ', $attribute_separator = ', ') { if (!Combination::isFeatureActive()) return array(); $add_shop = ''; $combinations = Db::getInstance()->executeS('SELECT pa.*, product_attribute_shop.* FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$this->id.' GROUP BY pa.`id_product_attribute`'); if (!$combinations) return false; $product_attributes = array(); foreach ($combinations as $combination) $product_attributes[] = (int)$combination['id_product_attribute']; $lang = Db::getInstance()->executeS('SELECT pac.id_product_attribute, GROUP_CONCAT(agl.`name`, \''.pSQL($attribute_value_separator).'\',al.`name` ORDER BY agl.`id_attribute_group` SEPARATOR \''.pSQL($attribute_separator).'\') as attribute_designation FROM `'._DB_PREFIX_.'product_attribute_combination` pac LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)$id_lang.') WHERE pac.id_product_attribute IN ('.implode(',', $product_attributes).') GROUP BY pac.id_product_attribute'); foreach ($lang as $k => $row) $combinations[$k]['attribute_designation'] = $row['attribute_designation']; //Get quantity of each variations foreach ($combinations as $key => $row) { $cache_key = $row['id_product'].'_'.$row['id_product_attribute'].'_quantity'; if (!Cache::isStored($cache_key)) Cache::store( $cache_key, StockAvailable::getQuantityAvailableByProduct($row['id_product'], $row['id_product_attribute']) ); $combinations[$key]['quantity'] = Cache::retrieve($cache_key); } return $combinations; } /** * Get all available product attributes combinations * * @param integer $id_lang Language id * @return array Product attributes combinations */ public function getAttributeCombinations($id_lang) { if (!Combination::isFeatureActive()) return array(); $sql = 'SELECT pa.*, product_attribute_shop.*, ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, al.`name` AS attribute_name, a.`id_attribute`, pa.`unit_price_impact` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)$id_lang.') WHERE pa.`id_product` = '.(int)$this->id.' GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group` ORDER BY pa.`id_product_attribute`'; $res = Db::getInstance()->executeS($sql); //Get quantity of each variations foreach ($res as $key => $row) { $cache_key = $row['id_product'].'_'.$row['id_product_attribute'].'_quantity'; if (!Cache::isStored($cache_key)) Cache::store( $cache_key, StockAvailable::getQuantityAvailableByProduct($row['id_product'], $row['id_product_attribute']) ); $res[$key]['quantity'] = Cache::retrieve($cache_key); } return $res; } /** * Get product attribute combination by id_product_attribute * * @param integer $id_product_attribute * @param integer $id_lang Language id * @return array Product attribute combination by id_product_attribute */ public function getAttributeCombinationsById($id_product_attribute, $id_lang) { if (!Combination::isFeatureActive()) return array(); $sql = 'SELECT pa.*, product_attribute_shop.*, ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, al.`name` AS attribute_name, a.`id_attribute`, pa.`unit_price_impact` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)$id_lang.') WHERE pa.`id_product` = '.(int)$this->id.' AND pa.`id_product_attribute` = '.(int)$id_product_attribute.' GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group` ORDER BY pa.`id_product_attribute`'; $res = Db::getInstance()->executeS($sql); //Get quantity of each variations foreach ($res as $key => $row) { $cache_key = $row['id_product'].'_'.$row['id_product_attribute'].'_quantity'; if (!Cache::isStored($cache_key)) Cache::store( $cache_key, StockAvailable::getQuantityAvailableByProduct($row['id_product'], $row['id_product_attribute']) ); $res[$key]['quantity'] = Cache::retrieve($cache_key); } return $res; } public function getCombinationImages($id_lang) { if (!Combination::isFeatureActive()) return false; $product_attributes = Db::getInstance()->executeS( 'SELECT `id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` WHERE `id_product` = '.(int)$this->id ); if (!$product_attributes) return false; $ids = array(); foreach ($product_attributes as $product_attribute) $ids[] = (int)$product_attribute['id_product_attribute']; $result = Db::getInstance()->executeS(' SELECT pai.`id_image`, pai.`id_product_attribute`, il.`legend` FROM `'._DB_PREFIX_.'product_attribute_image` pai LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (il.`id_image` = pai.`id_image`) LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_image` = pai.`id_image`) WHERE pai.`id_product_attribute` IN ('.implode(', ', $ids).') AND il.`id_lang` = '.(int)$id_lang.' ORDER by i.`position`' ); if (!$result) return false; $images = array(); foreach ($result as $row) $images[$row['id_product_attribute']][] = $row; return $images; } /** * Check if product has attributes combinations * * @return integer Attributes combinations number */ public function hasAttributes() { if (!Combination::isFeatureActive()) return 0; return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT COUNT(*) FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$this->id ); } /** * Get new products * * @param integer $id_lang Language id * @param integer $pageNumber Start from (optional) * @param integer $nbProducts Number of products to return (optional) * @return array New products */ public static function getAllProducts($id_lang, $page_number = 0, $nb_products = 10, $count = false, $order_by = null, $order_way = null, Context $context = null) { if (!$context) $context = Context::getContext(); $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; if ($page_number < 0) $page_number = 0; if ($nb_products < 1) $nb_products = 10; if (empty($order_by) || $order_by == 'position') $order_by = 'date_add'; if (empty($order_way)) $order_way = 'DESC'; if ($order_by == 'id_product' || $order_by == 'price' || $order_by == 'date_add' || $order_by == 'date_upd') $order_by_prefix = 'p'; else if ($order_by == 'name') $order_by_prefix = 'pl'; if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) die(Tools::displayError()); $groups = FrontController::getCurrentCustomerGroups(); $sql_groups = (count($groups) ? 'IN ('.implode(',', $groups).')' : '= 1'); if (strpos($order_by, '.') > 0) { $order_by = explode('.', $order_by); $order_by_prefix = $order_by[0]; $order_by = $order_by[1]; } if ($count) { $sql = 'SELECT COUNT(p.`id_product`) AS nb FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE product_shop.`active` = 1 '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').' AND p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.' )'; return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql); } $sql = new DbQuery(); $sql->select( 'p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` AS manufacturer_name ' ); $sql->from('product', 'p'); $sql->join(Shop::addSqlAssociation('product', 'p')); $sql->leftJoin('product_lang', 'pl', ' p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl') ); $sql->leftJoin('image', 'i', 'i.`id_product` = p.`id_product`'); $sql->join(Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1')); $sql->leftJoin('image_lang', 'il', 'i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang); $sql->leftJoin('manufacturer', 'm', 'm.`id_manufacturer` = p.`id_manufacturer`'); $sql->where('product_shop.`active` = 1'); if ($front) $sql->where('product_shop.`visibility` IN ("both", "catalog")'); $sql->where('p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.')' ); $sql->groupBy('product_shop.id_product'); $sql->orderBy((isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').'`'.pSQL($order_by).'` '.pSQL($order_way)); $sql->limit($nb_products, $page_number * $nb_products); if (Combination::isFeatureActive()) { $sql->select('MAX(product_attribute_shop.id_product_attribute) id_product_attribute'); $sql->leftOuterJoin('product_attribute', 'pa', 'p.`id_product` = pa.`id_product`'); $sql->join(Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.default_on = 1')); } $sql->join(Product::sqlStock('p', Combination::isFeatureActive() ? 'product_attribute_shop' : 0)); $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); if ($order_by == 'price') Tools::orderbyPrice($result, $order_way); if (!$result) return false; $products_ids = array(); foreach ($result as $row) $products_ids[] = $row['id_product']; // Thus you can avoid one query per product, because there will be only one query for all the products of the cart Product::cacheFrontFeatures($products_ids, $id_lang); return Product::getProductsProperties((int)$id_lang, $result); } public static function getNewProducts($id_lang, $page_number = 0, $nb_products = 10, $count = false, $order_by = null, $order_way = null, Context $context = null) { if (!$context) $context = Context::getContext(); $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; if ($page_number < 0) $page_number = 0; if ($nb_products < 1) $nb_products = 10; if (empty($order_by) || $order_by == 'position') $order_by = 'date_add'; if (empty($order_way)) $order_way = 'DESC'; if ($order_by == 'id_product' || $order_by == 'price' || $order_by == 'date_add' || $order_by == 'date_upd') $order_by_prefix = 'p'; else if ($order_by == 'name') $order_by_prefix = 'pl'; if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) die(Tools::displayError()); $groups = FrontController::getCurrentCustomerGroups(); $sql_groups = (count($groups) ? 'IN ('.implode(',', $groups).')' : '= 1'); if (strpos($order_by, '.') > 0) { $order_by = explode('.', $order_by); $order_by_prefix = $order_by[0]; $order_by = $order_by[1]; } if ($count) { $sql = 'SELECT COUNT(p.`id_product`) AS nb FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE product_shop.`active` = 1 AND DATEDIFF( product_shop.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0 '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').' AND p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.' )'; return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql); } $sql = new DbQuery(); $sql->select( 'p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` AS manufacturer_name, DATEDIFF( product_shop.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0 AS new' ); $sql->from('product', 'p'); $sql->join(Shop::addSqlAssociation('product', 'p')); $sql->leftJoin('product_lang', 'pl', ' p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl') ); $sql->leftJoin('image', 'i', 'i.`id_product` = p.`id_product`'); $sql->join(Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1')); $sql->leftJoin('image_lang', 'il', 'i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang); $sql->leftJoin('manufacturer', 'm', 'm.`id_manufacturer` = p.`id_manufacturer`'); $sql->where('product_shop.`active` = 1'); if ($front) $sql->where('product_shop.`visibility` IN ("both", "catalog")'); $sql->where(' DATEDIFF( product_shop.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0' ); $sql->where('p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.')' ); $sql->groupBy('product_shop.id_product'); $sql->orderBy((isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').'`'.pSQL($order_by).'` '.pSQL($order_way)); $sql->limit($nb_products, $page_number * $nb_products); if (Combination::isFeatureActive()) { $sql->select('MAX(product_attribute_shop.id_product_attribute) id_product_attribute'); $sql->leftOuterJoin('product_attribute', 'pa', 'p.`id_product` = pa.`id_product`'); $sql->join(Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.default_on = 1')); } $sql->join(Product::sqlStock('p', Combination::isFeatureActive() ? 'product_attribute_shop' : 0)); $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); if ($order_by == 'price') Tools::orderbyPrice($result, $order_way); if (!$result) return false; $products_ids = array(); foreach ($result as $row) $products_ids[] = $row['id_product']; // Thus you can avoid one query per product, because there will be only one query for all the products of the cart Product::cacheFrontFeatures($products_ids, $id_lang); return Product::getProductsProperties((int)$id_lang, $result); } protected static function _getProductIdByDate($beginning, $ending, Context $context = null, $with_combination = false) { if (!$context) $context = Context::getContext(); $id_address = $context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}; $ids = Address::getCountryAndState($id_address); $id_country = (int)($ids['id_country'] ? $ids['id_country'] : Configuration::get('PS_COUNTRY_DEFAULT')); return SpecificPrice::getProductIdByDate( $context->shop->id, $context->currency->id, $id_country, $context->customer->id_default_group, $beginning, $ending, 0, $with_combination ); } /** * Get a random special * * @param integer $id_lang Language id * @return array Special */ public static function getRandomSpecial($id_lang, $beginning = false, $ending = false, Context $context = null) { if (!$context) $context = Context::getContext(); $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; $current_date = date('Y-m-d H:i:s'); $product_reductions = Product::_getProductIdByDate((!$beginning ? $current_date : $beginning), (!$ending ? $current_date : $ending), $context, true); if ($product_reductions) { $ids_product = ' AND ('; foreach ($product_reductions as $product_reduction) $ids_product .= '( product_shop.`id_product` = '.(int)$product_reduction['id_product'].($product_reduction['id_product_attribute'] ? ' AND product_attribute_shop.`id_product_attribute`='.(int)$product_reduction['id_product_attribute'] :'').') OR'; $ids_product = rtrim($ids_product, 'OR').')'; $groups = FrontController::getCurrentCustomerGroups(); $sql_groups = (count($groups) ? 'IN ('.implode(',', $groups).')' : '= 1'); // Please keep 2 distinct queries because RAND() is an awful way to achieve this result $sql = 'SELECT product_shop.id_product, MAX(product_attribute_shop.id_product_attribute) id_product_attribute FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (product_shop.id_product = pa.id_product) '.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.default_on = 1').' WHERE product_shop.`active` = 1 '.(($ids_product) ? $ids_product : '').' AND p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.' ) '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').' GROUP BY product_shop.id_product ORDER BY RAND()'; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); if (!$id_product = $result['id_product']) return false; $sql = 'SELECT p.*, product_shop.*, stock.`out_of_stock` out_of_stock, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, p.`ean13`, p.`upc`, MAX(image_shop.`id_image`) id_image, il.`legend`, DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY)) > 0 AS new FROM `'._DB_PREFIX_.'product` p LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON ( p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').' ) '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'. Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').' LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang.') '.Product::sqlStock('p', 0).' WHERE p.id_product = '.(int)$id_product.' GROUP BY product_shop.id_product'; $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); if (!$row) return false; if ($result['id_product_attribute']) $row['id_product_attribute'] = $result['id_product_attribute']; return Product::getProductProperties($id_lang, $row); } else return false; } /** * Get prices drop * * @param integer $id_lang Language id * @param integer $pageNumber Start from (optional) * @param integer $nbProducts Number of products to return (optional) * @param boolean $count Only in order to get total number (optional) * @return array Prices drop */ public static function getPricesDrop($id_lang, $page_number = 0, $nb_products = 10, $count = false, $order_by = null, $order_way = null, $beginning = false, $ending = false, Context $context = null) { if (!Validate::isBool($count)) die(Tools::displayError()); if (!$context) $context = Context::getContext(); if ($page_number < 0) $page_number = 0; if ($nb_products < 1) $nb_products = 10; if (empty($order_by) || $order_by == 'position') $order_by = 'price'; if (empty($order_way)) $order_way = 'DESC'; if ($order_by == 'id_product' || $order_by == 'price' || $order_by == 'date_add' || $order_by == 'date_upd') $order_by_prefix = 'p'; else if ($order_by == 'name') $order_by_prefix = 'pl'; if (!Validate::isOrderBy($order_by) || !Validate::isOrderWay($order_way)) die (Tools::displayError()); $current_date = date('Y-m-d H:i:s'); $ids_product = Product::_getProductIdByDate((!$beginning ? $current_date : $beginning), (!$ending ? $current_date : $ending), $context); $tab_id_product = array(); foreach ($ids_product as $product) if (is_array($product)) $tab_id_product[] = (int)$product['id_product']; else $tab_id_product[] = (int)$product; $front = true; if (!in_array($context->controller->controller_type, array('front', 'modulefront'))) $front = false; $groups = FrontController::getCurrentCustomerGroups(); $sql_groups = (count($groups) ? 'IN ('.implode(',', $groups).')' : '= 1'); if ($count) { $sql = 'SELECT COUNT(DISTINCT p.`id_product`) AS nb FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE product_shop.`active` = 1 AND product_shop.`show_price` = 1 '.($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '').' '.((!$beginning && !$ending) ? 'AND p.`id_product` IN('.((is_array($tab_id_product) && count($tab_id_product)) ? implode(', ', $tab_id_product) : 0).')' : '').' AND p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.' )'; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($sql); return (int)$result['nb']; } if (strpos($order_by, '.') > 0) { $order_by = explode('.', $order_by); $order_by = pSQL($order_by[0]).'.`'.pSQL($order_by[1]).'`'; } $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, pl.`description`, pl.`description_short`, MAX(product_attribute_shop.id_product_attribute) id_product_attribute, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` AS manufacturer_name, DATEDIFF( p.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0 AS new FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN '._DB_PREFIX_.'product_attribute pa ON (pa.id_product = p.id_product) '.Shop::addSqlAssociation('product_attribute', 'pa', false, 'product_attribute_shop.default_on=1').' '.Product::sqlStock('p', 0, false, $context->shop).' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON ( p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').' ) LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'. Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').' LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (m.`id_manufacturer` = p.`id_manufacturer`) WHERE product_shop.`active` = 1 AND product_shop.`show_price` = 1 '.($front ? ' AND p.`visibility` IN ("both", "catalog")' : '').' '.((!$beginning && !$ending) ? ' AND p.`id_product` IN ('.((is_array($tab_id_product) && count($tab_id_product)) ? implode(', ', $tab_id_product) : 0).')' : '').' AND p.`id_product` IN ( SELECT cp.`id_product` FROM `'._DB_PREFIX_.'category_group` cg LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON (cp.`id_category` = cg.`id_category`) WHERE cg.`id_group` '.$sql_groups.' ) GROUP BY product_shop.id_product ORDER BY '.(isset($order_by_prefix) ? pSQL($order_by_prefix).'.' : '').pSQL($order_by).' '.pSQL($order_way).' LIMIT '.(int)($page_number * $nb_products).', '.(int)$nb_products; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); if ($order_by == 'price') Tools::orderbyPrice($result, $order_way); if (!$result) return false; return Product::getProductsProperties($id_lang, $result); } /** * getProductCategories return an array of categories which this product belongs to * * @return array of categories */ public static function getProductCategories($id_product = '') { $ret = array(); $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT `id_category` FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.(int)$id_product ); if ($row) foreach ($row as $val) $ret[] = $val['id_category']; return $ret; } public static function getProductCategoriesFull($id_product = '', $id_lang = null) { if (!$id_lang) $id_lang = Context::getContext()->language->id; $ret = array(); $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT cp.`id_category`, cl.`name`, cl.`link_rewrite` FROM `'._DB_PREFIX_.'category_product` cp LEFT JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category) LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (cp.`id_category` = cl.`id_category`'.Shop::addSqlRestrictionOnLang('cl').') '.Shop::addSqlAssociation('category', 'c').' WHERE cp.`id_product` = '.(int)$id_product.' AND cl.`id_lang` = '.(int)$id_lang ); foreach ($row as $val) $ret[$val['id_category']] = $val; return $ret; } /** * getCategories return an array of categories which this product belongs to * * @return array of categories */ public function getCategories() { return Product::getProductCategories($this->id); } /** * Gets carriers assigned to the product */ public function getCarriers() { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT c.* FROM `'._DB_PREFIX_.'product_carrier` pc INNER JOIN `'._DB_PREFIX_.'carrier` c ON (c.`id_reference` = pc.`id_carrier_reference` AND c.`deleted` = 0) WHERE pc.`id_product` = '.(int)$this->id.' AND pc.`id_shop` = '.(int)$this->id_shop); } /** * Sets carriers assigned to the product */ public function setCarriers($carrier_list) { $data = array(); foreach ($carrier_list as $carrier) { $data[] = array( 'id_product' => (int)$this->id, 'id_carrier_reference' => (int)$carrier, 'id_shop' => (int)$this->id_shop ); } Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'product_carrier` WHERE id_product = '.(int)$this->id.' AND id_shop = '.(int)$this->id_shop ); if (count($data)) Db::getInstance()->insert('product_carrier', $data); } /** * Get product images and legends * * @param integer $id_lang Language id for multilingual legends * @return array Product images and legends */ public function getImages($id_lang, Context $context = null) { if (!$context) $context = Context::getContext(); $sql = 'SELECT image_shop.`cover`, i.`id_image`, il.`legend`, i.`position` FROM `'._DB_PREFIX_.'image` i '.Shop::addSqlAssociation('image', 'i').' LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang.') WHERE i.`id_product` = '.(int)$this->id.' ORDER BY `position`'; return Db::getInstance()->executeS($sql); } /** * Get product cover image * * @return array Product cover image */ public static function getCover($id_product, Context $context = null) { if (!$context) $context = Context::getContext(); $sql = 'SELECT image_shop.`id_image` FROM `'._DB_PREFIX_.'image` i '.Shop::addSqlAssociation('image', 'i').' WHERE i.`id_product` = '.(int)$id_product.' AND image_shop.`cover` = 1'; return Db::getInstance()->getRow($sql); } /** * Get product price * * @param integer $id_product Product id * @param boolean $usetax With taxes or not (optional) * @param integer $id_product_attribute Product attribute id (optional). * If set to false, do not apply the combination price impact. NULL does apply the default combination price impact. * @param integer $decimals Number of decimals (optional) * @param integer $divisor Useful when paying many time without fees (optional) * @param boolean $only_reduc Returns only the reduction amount * @param boolean $usereduc Set if the returned amount will include reduction * @param integer $quantity Required for quantity discount application (default value: 1) * @param boolean $forceAssociatedTax DEPRECATED - NOT USED Force to apply the associated tax. Only works when the parameter $usetax is true * @param integer $id_customer Customer ID (for customer group reduction) * @param integer $id_cart Cart ID. Required when the cookie is not accessible (e.g., inside a payment module, a cron task...) * @param integer $id_address Customer address ID. Required for price (tax included) calculation regarding the guest localization * @param variable_reference $specificPriceOutput. * If a specific price applies regarding the previous parameters, this variable is filled with the corresponding SpecificPrice object * @param boolean $with_ecotax insert ecotax in price output. * @return float Product price */ public static function getPriceStatic($id_product, $usetax = true, $id_product_attribute = null, $decimals = 6, $divisor = null, $only_reduc = false, $usereduc = true, $quantity = 1, $force_associated_tax = false, $id_customer = null, $id_cart = null, $id_address = null, &$specific_price_output = null, $with_ecotax = true, $use_group_reduction = true, Context $context = null, $use_customer_price = true) { if (!$context) $context = Context::getContext(); $cur_cart = $context->cart; if ($divisor !== null) Tools::displayParameterAsDeprecated('divisor'); if (!Validate::isBool($usetax) || !Validate::isUnsignedId($id_product)) die(Tools::displayError()); // Initializations $id_group = (isset($context->customer) ? $context->customer->id_default_group : (int)Configuration::get('PS_CUSTOMER_GROUP')); // If there is cart in context or if the specified id_cart is different from the context cart id if (!is_object($cur_cart) || (Validate::isUnsignedInt($id_cart) && $id_cart && $cur_cart->id != $id_cart)) { /* * When a user (e.g., guest, customer, Google...) is on PrestaShop, he has already its cart as the global (see /init.php) * When a non-user calls directly this method (e.g., payment module...) is on PrestaShop, he does not have already it BUT knows the cart ID * When called from the back office, cart ID can be inexistant */ if (!$id_cart && !isset($context->employee)) die(Tools::displayError()); $cur_cart = new Cart($id_cart); // Store cart in context to avoid multiple instantiations in BO if (!Validate::isLoadedObject($context->cart)) $context->cart = $cur_cart; } $cart_quantity = 0; if ((int)$id_cart) { $condition = ''; $cache_name = (int)$id_cart.'_'.(int)$id_product.'_'.(int)$id_product_attribute; if (!isset(self::$_cart_quantity[$cache_name]) || self::$_cart_quantity[$cache_name] != (int)$quantity) self::$_cart_quantity[$cache_name] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT SUM(`quantity`) FROM `'._DB_PREFIX_.'cart_product` WHERE `id_product` = '.(int)$id_product.' AND `id_product_attribute` = '.(int)$id_product_attribute.' AND `id_cart` = '.(int)$id_cart); $cart_quantity = self::$_cart_quantity[$cache_name]; } $id_currency = (int)Validate::isLoadedObject($context->currency) ? $context->currency->id : Configuration::get('PS_CURRENCY_DEFAULT'); // retrieve address informations $id_country = (int)$context->country->id; $id_state = 0; $zipcode = 0; if (!$id_address) $id_address = $cur_cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}; if ($id_address) { $address_infos = Address::getCountryAndState($id_address); if ($address_infos['id_country']) { $id_country = (int)$address_infos['id_country']; $id_state = (int)$address_infos['id_state']; $zipcode = $address_infos['postcode']; } } else if (isset($context->customer->geoloc_id_country)) { $id_country = (int)$context->customer->geoloc_id_country; $id_state = (int)$context->customer->id_state; $zipcode = (int)$context->customer->postcode; } if (Tax::excludeTaxeOption()) $usetax = false; if ($usetax != false && !empty($address_infos['vat_number']) && $address_infos['id_country'] != Configuration::get('VATNUMBER_COUNTRY') && Configuration::get('VATNUMBER_MANAGEMENT')) $usetax = false; if (is_null($id_customer) && Validate::isLoadedObject($context->customer)) $id_customer = $context->customer->id; return Product::priceCalculation( $context->shop->id, $id_product, $id_product_attribute, $id_country, $id_state, $zipcode, $id_currency, $id_group, $cart_quantity, $usetax, $decimals, $only_reduc, $usereduc, $with_ecotax, $specific_price_output, $use_group_reduction, $id_customer, $use_customer_price, $id_cart, $quantity ); } /** * Price calculation / Get product price * * @param integer $id_shop Shop id * @param integer $id_product Product id * @param integer $id_product_attribute Product attribute id * @param integer $id_country Country id * @param integer $id_state State id * @param integer $id_currency Currency id * @param integer $id_group Group id * @param integer $quantity Quantity Required for Specific prices : quantity discount application * @param boolean $use_tax with (1) or without (0) tax * @param integer $decimals Number of decimals returned * @param boolean $only_reduc Returns only the reduction amount * @param boolean $use_reduc Set if the returned amount will include reduction * @param boolean $with_ecotax insert ecotax in price output. * @param variable_reference $specific_price_output * If a specific price applies regarding the previous parameters, this variable is filled with the corresponding SpecificPrice object * @return float Product price **/ public static function priceCalculation($id_shop, $id_product, $id_product_attribute, $id_country, $id_state, $zipcode, $id_currency, $id_group, $quantity, $use_tax, $decimals, $only_reduc, $use_reduc, $with_ecotax, &$specific_price, $use_group_reduction, $id_customer = 0, $use_customer_price = true, $id_cart = 0, $real_quantity = 0) { static $address = null; static $context = null; if ($address === null) $address = new Address(); if ($context == null) $context = Context::getContext()->cloneContext(); if ($id_shop !== null && $context->shop->id != (int)$id_shop) $context->shop = new Shop((int)$id_shop); if (!$use_customer_price) $id_customer = 0; if ($id_product_attribute === null) $id_product_attribute = Product::getDefaultAttribute($id_product); $cache_id = $id_product.'-'.$id_shop.'-'.$id_currency.'-'.$id_country.'-'.$id_state.'-'.$zipcode.'-'.$id_group. '-'.$quantity.'-'.$id_product_attribute.'-'.($use_tax?'1':'0').'-'.$decimals.'-'.($only_reduc?'1':'0'). '-'.($use_reduc?'1':'0').'-'.$with_ecotax.'-'.$id_customer.'-'.(int)$use_group_reduction.'-'.(int)$id_cart.'-'.(int)$real_quantity; // reference parameter is filled before any returns $specific_price = SpecificPrice::getSpecificPrice( (int)$id_product, $id_shop, $id_currency, $id_country, $id_group, $quantity, $id_product_attribute, $id_customer, $id_cart, $real_quantity ); if (isset(self::$_prices[$cache_id])) return self::$_prices[$cache_id]; // fetch price & attribute price $cache_id_2 = $id_product.'-'.$id_shop; if (!isset(self::$_pricesLevel2[$cache_id_2])) { $sql = new DbQuery(); $sql->select('product_shop.`price`, product_shop.`ecotax`'); $sql->from('product', 'p'); $sql->innerJoin('product_shop', 'product_shop', '(product_shop.id_product=p.id_product AND product_shop.id_shop = '.(int)$id_shop.')'); $sql->where('p.`id_product` = '.(int)$id_product); if (Combination::isFeatureActive()) { $sql->select('product_attribute_shop.id_product_attribute, product_attribute_shop.`price` AS attribute_price, product_attribute_shop.default_on'); $sql->leftJoin('product_attribute', 'pa', 'pa.`id_product` = p.`id_product`'); $sql->leftJoin('product_attribute_shop', 'product_attribute_shop', '(product_attribute_shop.id_product_attribute = pa.id_product_attribute AND product_attribute_shop.id_shop = '.(int)$id_shop.')'); } else $sql->select('0 as id_product_attribute'); $res = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); foreach ($res as $row) { $array_tmp = array( 'price' => $row['price'], 'ecotax' => $row['ecotax'], 'attribute_price' => (isset($row['attribute_price']) ? $row['attribute_price'] : null) ); self::$_pricesLevel2[$cache_id_2][(int)$row['id_product_attribute']] = $array_tmp; if (isset($row['default_on']) && $row['default_on'] == 1) self::$_pricesLevel2[$cache_id_2][0] = $array_tmp; } } if (!isset(self::$_pricesLevel2[$cache_id_2][(int)$id_product_attribute])) return; $result = self::$_pricesLevel2[$cache_id_2][(int)$id_product_attribute]; if (!$specific_price || $specific_price['price'] < 0) $price = (float)$result['price']; else $price = (float)$specific_price['price']; // convert only if the specific price is in the default currency (id_currency = 0) if (!$specific_price || !($specific_price['price'] >= 0 && $specific_price['id_currency'])) $price = Tools::convertPrice($price, $id_currency); // Attribute price if (is_array($result) && (!$specific_price || !$specific_price['id_product_attribute'] || $specific_price['price'] < 0)) { $attribute_price = Tools::convertPrice($result['attribute_price'] !== null ? (float)$result['attribute_price'] : 0, $id_currency); // If you want the default combination, please use NULL value instead if ($id_product_attribute !== false) $price += $attribute_price; } // Tax $address->id_country = $id_country; $address->id_state = $id_state; $address->postcode = $zipcode; $tax_manager = TaxManagerFactory::getManager($address, Product::getIdTaxRulesGroupByIdProduct((int)$id_product, $context)); $product_tax_calculator = $tax_manager->getTaxCalculator(); // Add Tax if ($use_tax) $price = $product_tax_calculator->addTaxes($price); // Reduction $reduc = 0; if (($only_reduc || $use_reduc) && $specific_price) { if ($specific_price['reduction_type'] == 'amount') { $reduction_amount = $specific_price['reduction']; if (!$specific_price['id_currency']) $reduction_amount = Tools::convertPrice($reduction_amount, $id_currency); $reduc = !$use_tax ? $product_tax_calculator->removeTaxes($reduction_amount) : $reduction_amount; } else $reduc = $price * $specific_price['reduction']; } if ($only_reduc) return Tools::ps_round($reduc, $decimals); if ($use_reduc) $price -= $reduc; // Group reduction if ($use_group_reduction) { if ($reduction_from_category = (float)GroupReduction::getValueForProduct($id_product, $id_group)) $price -= $price * $reduction_from_category; else // apply group reduction if there is no group reduction for this category $price *= ((100 - Group::getReductionByIdGroup($id_group)) / 100); } // Eco Tax if (($result['ecotax'] || isset($result['attribute_ecotax'])) && $with_ecotax) { $ecotax = $result['ecotax']; if (isset($result['attribute_ecotax']) && $result['attribute_ecotax'] > 0) $ecotax = $result['attribute_ecotax']; if ($id_currency) $ecotax = Tools::convertPrice($ecotax, $id_currency); if ($use_tax) { // reinit the tax manager for ecotax handling $tax_manager = TaxManagerFactory::getManager( $address, (int)Configuration::get('PS_ECOTAX_TAX_RULES_GROUP_ID') ); $ecotax_tax_calculator = $tax_manager->getTaxCalculator(); $price += $ecotax_tax_calculator->addTaxes($ecotax); } else $price += $ecotax; } $price = Tools::ps_round($price, $decimals); if ($price < 0) $price = 0; self::$_prices[$cache_id] = $price; return self::$_prices[$cache_id]; } public static function convertAndFormatPrice($price, $currency = false, Context $context = null) { if (!$context) $context = Context::getContext(); if (!$currency) $currency = $context->currency; return Tools::displayPrice(Tools::convertPrice($price, $currency), $currency); } public static function isDiscounted($id_product, $quantity = 1, Context $context = null) { if (!$context) $context = Context::getContext(); $id_group = $context->customer->id_default_group; $cart_quantity = !$context->cart ? 0 : Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT SUM(`quantity`) FROM `'._DB_PREFIX_.'cart_product` WHERE `id_product` = '.(int)$id_product.' AND `id_cart` = '.(int)$context->cart->id ); $quantity = $cart_quantity ? $cart_quantity : $quantity; $id_currency = (int)$context->currency->id; $ids = Address::getCountryAndState((int)$context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}); $id_country = (int)($ids['id_country'] ? $ids['id_country'] : Configuration::get('PS_COUNTRY_DEFAULT')); return (bool)SpecificPrice::getSpecificPrice((int)$id_product, $context->shop->id, $id_currency, $id_country, $id_group, $quantity, null, 0, 0, $quantity); } /** * Get product price * Same as static function getPriceStatic, no need to specify product id * * @param boolean $tax With taxes or not (optional) * @param integer $id_product_attribute Product attribute id (optional) * @param integer $decimals Number of decimals (optional) * @param integer $divisor Util when paying many time without fees (optional) * @return float Product price in euros */ public function getPrice($tax = true, $id_product_attribute = null, $decimals = 6, $divisor = null, $only_reduc = false, $usereduc = true, $quantity = 1) { return Product::getPriceStatic((int)$this->id, $tax, $id_product_attribute, $decimals, $divisor, $only_reduc, $usereduc, $quantity); } public function getPublicPrice($tax = true, $id_product_attribute = null, $decimals = 6, $divisor = null, $only_reduc = false, $usereduc = true, $quantity = 1) { $specific_price_output = null; return Product::getPriceStatic((int)$this->id, $tax, $id_product_attribute, $decimals, $divisor, $only_reduc, $usereduc, $quantity, false, null, null, null, $specific_price_output, true, true, null, false); } public function getIdProductAttributeMostExpensive() { if (!Combination::isFeatureActive()) return 0; $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' SELECT pa.`id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$this->id.' ORDER BY product_attribute_shop.`price` DESC'); return (isset($row['id_product_attribute']) && $row['id_product_attribute']) ? (int)$row['id_product_attribute'] : 0; } public function getDefaultIdProductAttribute() { if (!Combination::isFeatureActive()) return 0; $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' SELECT pa.`id_product_attribute` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$this->id.' AND product_attribute_shop.default_on = 1' ); return (isset($row['id_product_attribute']) && $row['id_product_attribute']) ? (int)$row['id_product_attribute'] : 0; } public function getPriceWithoutReduct($notax = false, $id_product_attribute = false) { return Product::getPriceStatic((int)$this->id, !$notax, $id_product_attribute, 6, null, false, false); } /** * Display price with right format and currency * * @param array $params Params * @param $smarty Smarty object * @return string Price with right format and currency */ public static function convertPrice($params, &$smarty) { return Tools::displayPrice($params['price'], Context::getContext()->currency); } /** * Convert price with currency * * @param array $params * @param object $smarty DEPRECATED * @return Ambigous <string, mixed, Ambigous <number, string>> */ public static function convertPriceWithCurrency($params, &$smarty) { return Tools::displayPrice($params['price'], $params['currency'], false); } public static function displayWtPrice($params, &$smarty) { return Tools::displayPrice($params['p'], Context::getContext()->currency); } /** * Display WT price with currency * * @param array $params * @param object DEPRECATED $smarty * @return Ambigous <string, mixed, Ambigous <number, string>> */ public static function displayWtPriceWithCurrency($params, &$smarty) { return Tools::displayPrice($params['price'], $params['currency'], false); } /** * Get available product quantities * * @param integer $id_product Product id * @param integer $id_product_attribute Product attribute id (optional) * @return integer Available quantities */ public static function getQuantity($id_product, $id_product_attribute = null, $cache_is_pack = null) { $lang = Configuration::get('PS_LANG_DEFAULT'); if ((int)$cache_is_pack || ($cache_is_pack === null && Pack::isPack((int)$id_product))) { if (!Pack::isInStock((int)$id_product)) return 0; } // @since 1.5.0 return (StockAvailable::getQuantityAvailableByProduct($id_product, $id_product_attribute)); } /** * Create JOIN query with 'stock_available' table * * @param string $productAlias Alias of product table * @param string|int $productAttribute If string : alias of PA table ; if int : value of PA ; if null : nothing about PA * @param bool $innerJoin LEFT JOIN or INNER JOIN * @param Shop $shop * @return string */ public static function sqlStock($product_alias, $product_attribute = 0, $inner_join = false, Shop $shop = null) { $id_shop = ($shop !== null ? (int)$shop->id : null); $sql = (($inner_join) ? ' INNER ' : ' LEFT ').' JOIN '._DB_PREFIX_.'stock_available stock ON (stock.id_product = '.pSQL($product_alias).'.id_product'; if (!is_null($product_attribute)) { if (!Combination::isFeatureActive()) $sql .= ' AND stock.id_product_attribute = 0'; elseif (is_numeric($product_attribute)) $sql .= ' AND stock.id_product_attribute = '.$product_attribute; elseif (is_string($product_attribute)) $sql .= ' AND stock.id_product_attribute = IFNULL(`'.bqSQL($product_attribute).'`.id_product_attribute, 0)'; } $sql .= StockAvailable::addSqlShopRestriction(null, $id_shop, 'stock').' )'; return $sql; } /** * @deprecated since 1.5.0 * * It's not possible to use this method with new stockManager and stockAvailable features * Now this method do nothing * * @see StockManager if you want to manage real stock * @see StockAvailable if you want to manage available quantities for sale on your shop(s) * * @param array $product Array with ordered product (quantity, id_product_attribute if applicable) * @return mixed Query result */ public static function updateQuantity() { Tools::displayAsDeprecated(); return false; } /** * @deprecated since 1.5.0 * * It's not possible to use this method with new stockManager and stockAvailable features * Now this method do nothing * * @see StockManager if you want to manage real stock * @see StockAvailable if you want to manage available quantities for sale on your shop(s) * */ public static function reinjectQuantities() { Tools::displayAsDeprecated(); return false; } public static function isAvailableWhenOutOfStock($out_of_stock) { // @TODO 1.5.0 Update of STOCK_MANAGEMENT & ORDER_OUT_OF_STOCK $return = (int)$out_of_stock == 2 ? (int)Configuration::get('PS_ORDER_OUT_OF_STOCK') : (int)$out_of_stock; return !Configuration::get('PS_STOCK_MANAGEMENT') ? true : $return; } /** * Check product availability * * @param integer $qty Quantity desired * @return boolean True if product is available with this quantity */ public function checkQty($qty) { if (Pack::isPack((int)$this->id) && !Pack::isInStock((int)$this->id)) return false; if ($this->isAvailableWhenOutOfStock(StockAvailable::outOfStock($this->id))) return true; if (isset($this->id_product_attribute)) $id_product_attribute = $this->id_product_attribute; else $id_product_attribute = 0; return ($qty <= StockAvailable::getQuantityAvailableByProduct($this->id, $id_product_attribute)); } /** * Check if there is no default attribute and create it if not */ public function checkDefaultAttributes() { if (!$this->id) return false; if (Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE product_attribute_shop.`default_on` = 1 AND pa.`id_product` = '.(int)$this->id) > 1) Db::getInstance()->execute('UPDATE '._DB_PREFIX_.'product_attribute_shop product_attribute_shop, '._DB_PREFIX_.'product_attribute pa SET product_attribute_shop.default_on=0, pa.default_on = 0 WHERE product_attribute_shop.id_product_attribute=pa.id_product_attribute AND pa.id_product='.(int)$this->id .Shop::addSqlRestriction(false, 'product_attribute_shop')); $row = Db::getInstance()->getRow(' SELECT pa.id_product FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE product_attribute_shop.`default_on` = 1 AND pa.`id_product` = '.(int)$this->id ); if ($row) return true; $mini = Db::getInstance()->getRow(' SELECT MIN(pa.id_product_attribute) as `id_attr` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE `id_product` = '.(int)$this->id ); if (!$mini) return false; if (!ObjectModel::updateMultishopTable('Combination', array('default_on' => 1), 'a.id_product_attribute = '.(int)$mini['id_attr'])) return false; return true; } /** * Get all available attribute groups * * @param integer $id_lang Language id * @return array Attribute groups */ public function getAttributesGroups($id_lang) { if (!Combination::isFeatureActive()) return array(); $sql = 'SELECT ag.`id_attribute_group`, ag.`is_color_group`, agl.`name` AS group_name, agl.`public_name` AS public_group_name, a.`id_attribute`, al.`name` AS attribute_name, a.`color` AS attribute_color, pa.`id_product_attribute`, IFNULL(stock.quantity, 0) as quantity, product_attribute_shop.`price`, product_attribute_shop.`ecotax`, pa.`weight`, product_attribute_shop.`default_on`, pa.`reference`, product_attribute_shop.`unit_price_impact`, pa.`minimal_quantity`, pa.`available_date`, ag.`group_type` FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' '.Product::sqlStock('pa', 'pa').' LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute` a ON a.`id_attribute` = pac.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON a.`id_attribute` = al.`id_attribute` LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON ag.`id_attribute_group` = agl.`id_attribute_group` '.Shop::addSqlAssociation('attribute', 'a').' WHERE pa.`id_product` = '.(int)$this->id.' AND al.`id_lang` = '.(int)$id_lang.' AND agl.`id_lang` = '.(int)$id_lang.' GROUP BY id_attribute_group, id_product_attribute ORDER BY ag.`position` ASC, a.`position` ASC, agl.`name` ASC'; return Db::getInstance()->executeS($sql); } /** * Delete product accessories * * @return mixed Deletion result */ public function deleteAccessories() { return Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'accessory` WHERE `id_product_1` = '.(int)$this->id); } /** * Delete product from other products accessories * * @return mixed Deletion result */ public function deleteFromAccessories() { return Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'accessory` WHERE `id_product_2` = '.(int)$this->id); } /** * Get product accessories (only names) * * @param integer $id_lang Language id * @param integer $id_product Product id * @return array Product accessories */ public static function getAccessoriesLight($id_lang, $id_product, Context $context = null) { if (!$context) $context = Context::getContext(); $sql = 'SELECT p.`id_product`, p.`reference`, pl.`name` FROM `'._DB_PREFIX_.'accessory` LEFT JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product`= `id_product_2`) '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON ( p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').' ) WHERE `id_product_1` = '.(int)$id_product; return Db::getInstance()->executeS($sql); } /** * Get product accessories * * @param integer $id_lang Language id * @return array Product accessories */ public function getAccessories($id_lang, $active = true, Context $context = null) { if (!$context) $context = Context::getContext(); $sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity, pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, MAX(image_shop.`id_image`) id_image, il.`legend`, m.`name` as manufacturer_name, cl.`name` AS category_default, DATEDIFF( p.`date_add`, DATE_SUB( NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY ) ) > 0 AS new FROM `'._DB_PREFIX_.'accessory` LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = `id_product_2` '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON ( p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').' ) LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON ( product_shop.`id_category_default` = cl.`id_category` AND cl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl').' ) LEFT JOIN `'._DB_PREFIX_.'image` i ON (i.`id_product` = p.`id_product`)'. Shop::addSqlAssociation('image', 'i', false, 'image_shop.cover=1').' LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = '.(int)$id_lang.') LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (p.`id_manufacturer`= m.`id_manufacturer`) '.Product::sqlStock('p', 0).' WHERE `id_product_1` = '.(int)$this->id. ($active ? ' AND product_shop.`active` = 1' : '').' GROUP BY product_shop.id_product'; if (!$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql)) return false; foreach ($result as &$row) $row['id_product_attribute'] = Product::getDefaultAttribute((int)$row['id_product']); return $this->getProductsProperties($id_lang, $result); } public static function getAccessoryById($accessory_id) { return Db::getInstance()->getRow('SELECT `id_product`, `name` FROM `'._DB_PREFIX_.'product_lang` WHERE `id_product` = '.(int)$accessory_id); } /** * Link accessories with product * * @param array $accessories_id Accessories ids */ public function changeAccessories($accessories_id) { foreach ($accessories_id as $id_product_2) Db::getInstance()->insert('accessory', array( 'id_product_1' => (int)$this->id, 'id_product_2' => (int)$id_product_2 )); } /** * Add new feature to product */ public function addFeaturesCustomToDB($id_value, $lang, $cust) { $row = array('id_feature_value' => (int)$id_value, 'id_lang' => (int)$lang, 'value' => pSQL($cust)); return Db::getInstance()->insert('feature_value_lang', $row); } public function addFeaturesToDB($id_feature, $id_value, $cust = 0) { if ($cust) { $row = array('id_feature' => (int)$id_feature, 'custom' => 1); Db::getInstance()->insert('feature_value', $row); $id_value = Db::getInstance()->Insert_ID(); } $row = array('id_feature' => (int)$id_feature, 'id_product' => (int)$this->id, 'id_feature_value' => (int)$id_value); Db::getInstance()->insert('feature_product', $row); SpecificPriceRule::applyAllRules(array((int)$this->id)); if ($id_value) return ($id_value); } public static function addFeatureProductImport($id_product, $id_feature, $id_feature_value) { return Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'feature_product` (`id_feature`, `id_product`, `id_feature_value`) VALUES ('.(int)$id_feature.', '.(int)$id_product.', '.(int)$id_feature_value.') ON DUPLICATE KEY UPDATE `id_feature_value` = '.(int)$id_feature_value ); } /** * Select all features for the object * * @return array Array with feature product's data */ public function getFeatures() { return Product::getFeaturesStatic((int)$this->id); } public static function getFeaturesStatic($id_product) { if (!Feature::isFeatureActive()) return array(); if (!array_key_exists($id_product, self::$_cacheFeatures)) self::$_cacheFeatures[$id_product] = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT id_feature, id_product, id_feature_value FROM `'._DB_PREFIX_.'feature_product` WHERE `id_product` = '.(int)$id_product ); return self::$_cacheFeatures[$id_product]; } public static function cacheProductsFeatures($product_ids) { if (!Feature::isFeatureActive()) return; $product_implode = array(); foreach ($product_ids as $id_product) if ((int)$id_product && !array_key_exists($id_product, self::$_cacheFeatures)) $product_implode[] = (int)$id_product; if (!count($product_implode)) return; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT id_feature, id_product, id_feature_value FROM `'._DB_PREFIX_.'feature_product` WHERE `id_product` IN ('.implode($product_implode, ',').')'); foreach ($result as $row) { if (!array_key_exists($row['id_product'], self::$_cacheFeatures)) self::$_cacheFeatures[$row['id_product']] = array(); self::$_cacheFeatures[$row['id_product']][] = $row; } } public static function cacheFrontFeatures($product_ids, $id_lang) { if (!Feature::isFeatureActive()) return; $product_implode = array(); foreach ($product_ids as $id_product) if ((int)$id_product && !array_key_exists($id_product.'-'.$id_lang, self::$_cacheFeatures)) $product_implode[] = (int)$id_product; if (!count($product_implode)) return; $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT id_product, name, value, pf.id_feature FROM '._DB_PREFIX_.'feature_product pf LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = pf.id_feature AND fl.id_lang = '.(int)$id_lang.') LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = pf.id_feature_value AND fvl.id_lang = '.(int)$id_lang.') LEFT JOIN '._DB_PREFIX_.'feature f ON (f.id_feature = pf.id_feature) WHERE `id_product` IN ('.implode($product_implode, ',').') ORDER BY f.position ASC'); foreach ($result as $row) { if (!array_key_exists($row['id_product'].'-'.$id_lang, self::$_frontFeaturesCache)) self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang] = array(); if (!isset(self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_feature']])) self::$_frontFeaturesCache[$row['id_product'].'-'.$id_lang][$row['id_feature']] = $row; } } /** * Admin panel product search * * @param integer $id_lang Language id * @param string $query Search query * @return array Matching products */ public static function searchByName($id_lang, $query, Context $context = null) { if (!$context) $context = Context::getContext(); $sql = new DbQuery(); $sql->select('p.`id_product`, pl.`name`, p.`active`, p.`reference`, m.`name` AS manufacturer_name, stock.`quantity`, product_shop.advanced_stock_management, p.`customizable`'); $sql->from('category_product', 'cp'); $sql->leftJoin('product', 'p', 'p.`id_product` = cp.`id_product`'); $sql->join(Shop::addSqlAssociation('product', 'p')); $sql->leftJoin('product_lang', 'pl', ' p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl') ); $sql->leftJoin('manufacturer', 'm', 'm.`id_manufacturer` = p.`id_manufacturer`'); $where = 'pl.`name` LIKE \'%'.pSQL($query).'%\' OR p.`reference` LIKE \'%'.pSQL($query).'%\' OR p.`supplier_reference` LIKE \'%'.pSQL($query).'%\' OR p.`id_product` IN (SELECT id_product FROM '._DB_PREFIX_.'product_supplier sp WHERE `product_supplier_reference` LIKE \'%'.pSQL($query).'%\')'; $sql->groupBy('`id_product`'); $sql->orderBy('pl.`name` ASC'); if (Combination::isFeatureActive()) { $sql->leftJoin('product_attribute', 'pa', 'pa.`id_product` = p.`id_product`'); $sql->join(Shop::addSqlAssociation('product_attribute', 'pa', false)); $where .= ' OR pa.`reference` LIKE \'%'.pSQL($query).'%\''; } $sql->where($where); $sql->join(Product::sqlStock('p', 'pa', false, $context->shop)); $result = Db::getInstance()->executeS($sql); if (!$result) return false; $results_array = array(); foreach ($result as $row) { $row['price_tax_incl'] = Product::getPriceStatic($row['id_product'], true, null, 2); $row['price_tax_excl'] = Product::getPriceStatic($row['id_product'], false, null, 2); $results_array[] = $row; } return $results_array; } /** * Duplicate attributes when duplicating a product * * @param integer $id_product_old Old product id * @param integer $id_product_new New product id */ public static function duplicateAttributes($id_product_old, $id_product_new) { $return = true; $combination_images = array(); $result = Db::getInstance()->executeS(' SELECT pa.*, product_attribute_shop.* FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$id_product_old ); foreach ($result as $row) { $id_product_attribute_old = (int)$row['id_product_attribute']; $result2 = Db::getInstance()->executeS(' SELECT * FROM `'._DB_PREFIX_.'product_attribute_combination` WHERE `id_product_attribute` = '.$id_product_attribute_old ); $row['id_product'] = $id_product_new; unset($row['id_product_attribute']); $combination = new Combination(); foreach ($row as $k => $v) $combination->$k = $v; $return &= $combination->add(); $id_product_attribute_new = (int)$combination->id; if ($result_images = Product::_getAttributeImageAssociations($id_product_attribute_old)) { $combination_images['old'][$id_product_attribute_old] = $result_images; $combination_images['new'][$id_product_attribute_new] = $result_images; } foreach ($result2 as $row2) { $row2['id_product_attribute'] = $id_product_attribute_new; $return &= Db::getInstance()->insert('product_attribute_combination', $row2); } } return !$return ? false : $combination_images; } /** * Get product attribute image associations * @param integer $id_product_attribute * @return array */ public static function _getAttributeImageAssociations($id_product_attribute) { $combination_images = array(); $data = Db::getInstance()->executeS(' SELECT `id_image` FROM `'._DB_PREFIX_.'product_attribute_image` WHERE `id_product_attribute` = '.(int)$id_product_attribute); foreach ($data as $row) $combination_images[] = (int)$row['id_image']; return $combination_images; } public static function duplicateAccessories($id_product_old, $id_product_new) { $return = true; $result = Db::getInstance()->executeS(' SELECT * FROM `'._DB_PREFIX_.'accessory` WHERE `id_product_1` = '.(int)$id_product_old); foreach ($result as $row) { $data = array( 'id_product_1' => (int)$id_product_new, 'id_product_2' => (int)$row['id_product_2']); $return &= Db::getInstance()->insert('accessory', $data); } return $return; } public static function duplicateTags($id_product_old, $id_product_new) { $tags = Db::getInstance()->executeS('SELECT `id_tag` FROM `'._DB_PREFIX_.'product_tag` WHERE `id_product` = '.(int)$id_product_old); if (!Db::getInstance()->NumRows()) return true; $data = array(); foreach ($tags as $tag) $data[] = array( 'id_product' => (int)$id_product_new, 'id_tag' => (int)$tag['id_tag'], ); return Db::getInstance()->insert('product_tag', $data); } public static function duplicateDownload($id_product_old, $id_product_new) { $sql = 'SELECT `display_filename`, `filename`, `date_add`, `date_expiration`, `nb_days_accessible`, `nb_downloadable`, `active`, `is_shareable` FROM `'._DB_PREFIX_.'product_download` WHERE `id_product` = '.(int)$id_product_old; $results = Db::getInstance()->executeS($sql); if (!$results) return true; $data = array(); $res = true; foreach ($results as $row) { $new_filename = ProductDownload::getNewFilename(); copy(_PS_DOWNLOAD_DIR_.$row['filename'], _PS_DOWNLOAD_DIR_.$new_filename); $data[] = array( 'id_product' => (int)$id_product_new, 'display_filename' => pSQL($row['display_filename']), 'filename' => pSQL($new_filename), 'date_expiration' => pSQL($row['date_expiration']), 'nb_days_accessible' => (int)$row['nb_days_accessible'], 'nb_downloadable' => (int)$row['nb_downloadable'], 'active' => (int)$row['active'], 'is_shareable' => (int)$row['is_shareable'], 'date_add' => date('Y-m-d H:i:s') ); $res &= Db::getInstance()->insert('product_download', $data); } return $res; } public static function duplicateAttachments($id_product_old, $id_product_new) { // Get all ids attachments of the old product $sql = 'SELECT `id_attachment` FROM `'._DB_PREFIX_.'product_attachment` WHERE `id_product` = '.(int)$id_product_old; $results = Db::getInstance()->executeS($sql); if (!$results) return true; $data = array(); // Prepare data of table product_attachment foreach ($results as $row) $data[] = array( 'id_product' => (int)$id_product_new, 'id_attachment' => (int)$row['id_attachment'] ); // Duplicate product attachement $res = Db::getInstance()->insert('product_attachment', $data); Product::updateCacheAttachment((int)$id_product_new); return $res; } /** * Duplicate features when duplicating a product * * @param integer $id_product_old Old product id * @param integer $id_product_old New product id */ public static function duplicateFeatures($id_product_old, $id_product_new) { $return = true; $result = Db::getInstance()->executeS(' SELECT * FROM `'._DB_PREFIX_.'feature_product` WHERE `id_product` = '.(int)$id_product_old); foreach ($result as $row) { $result2 = Db::getInstance()->getRow(' SELECT * FROM `'._DB_PREFIX_.'feature_value` WHERE `id_feature_value` = '.(int)$row['id_feature_value']); // Custom feature value, need to duplicate it if ($result2['custom']) { $old_id_feature_value = $result2['id_feature_value']; unset($result2['id_feature_value']); $return &= Db::getInstance()->insert('feature_value', $result2); $max_fv = Db::getInstance()->getRow(' SELECT MAX(`id_feature_value`) AS nb FROM `'._DB_PREFIX_.'feature_value`'); $new_id_feature_value = $max_fv['nb']; $languages = Language::getLanguages(); foreach ($languages as $language) { $result3 = Db::getInstance()->getRow(' SELECT * FROM `'._DB_PREFIX_.'feature_value_lang` WHERE `id_feature_value` = '.(int)$old_id_feature_value.' AND `id_lang` = '.(int)$language['id_lang']); if ($result3) { $result3['id_feature_value'] = $new_id_feature_value; $return &= Db::getInstance()->insert('feature_value_lang', $result3); } } $row['id_feature_value'] = $new_id_feature_value; } $row['id_product'] = $id_product_new; $return &= Db::getInstance()->insert('feature_product', $row); } return $return; } protected static function _getCustomizationFieldsNLabels($product_id) { if (!Customization::isFeatureActive()) return false; $customizations = array(); if (($customizations['fields'] = Db::getInstance()->executeS(' SELECT `id_customization_field`, `type`, `required` FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$product_id.' ORDER BY `id_customization_field`')) === false) return false; if (empty($customizations['fields'])) return array(); $customization_field_ids = array(); foreach ($customizations['fields'] as $customization_field) $customization_field_ids[] = (int)$customization_field['id_customization_field']; if (($customization_labels = Db::getInstance()->executeS(' SELECT `id_customization_field`, `id_lang`, `name` FROM `'._DB_PREFIX_.'customization_field_lang` WHERE `id_customization_field` IN ('.implode(', ', $customization_field_ids).') ORDER BY `id_customization_field`')) === false) return false; foreach ($customization_labels as $customization_label) $customizations['labels'][$customization_label['id_customization_field']][] = $customization_label; return $customizations; } public static function duplicateSpecificPrices($old_product_id, $product_id) { foreach (SpecificPrice::getIdsByProductId((int)$old_product_id) as $data) { $specific_price = new SpecificPrice((int)$data['id_specific_price']); if (!$specific_price->duplicate((int)$product_id)) return false; } return true; } public static function duplicateCustomizationFields($old_product_id, $product_id) { // If customization is not activated, return success if (!Customization::isFeatureActive()) return true; if (($customizations = Product::_getCustomizationFieldsNLabels($old_product_id)) === false) return false; if (empty($customizations)) return true; foreach ($customizations['fields'] as $customization_field) { /* The new datas concern the new product */ $customization_field['id_product'] = (int)$product_id; $old_customization_field_id = (int)$customization_field['id_customization_field']; unset($customization_field['id_customization_field']); if (!Db::getInstance()->insert('customization_field', $customization_field) || !$customization_field_id = Db::getInstance()->Insert_ID()) return false; if (isset($customizations['labels'])) { $query = 'INSERT INTO `'._DB_PREFIX_.'customization_field_lang` (`id_customization_field`, `id_lang`, `name`) VALUES '; $data = array(); foreach ($customizations['labels'][$old_customization_field_id] as $customization_label) { $data = array( 'id_customization_field' => (int)$customization_field_id, 'id_lang' => (int)$customization_label['id_lang'], 'name' => pSQL($customization_label['name']), ); if (!Db::getInstance()->insert('customization_field_lang', $data)) return false; } } } return true; } /** * Get the link of the product page of this product */ public function getLink(Context $context = null) { if (!$context) $context = Context::getContext(); return $context->link->getProductLink($this); } public function getTags($id_lang) { if (!$this->isFullyLoaded && is_null($this->tags)) $this->tags = Tag::getProductTags($this->id); if (!($this->tags && key_exists($id_lang, $this->tags))) return ''; $result = ''; foreach ($this->tags[$id_lang] as $tag_name) $result .= $tag_name.', '; return rtrim($result, ', '); } public static function defineProductImage($row, $id_lang) { if (isset($row['id_image'])) if ($row['id_image']) return $row['id_product'].'-'.$row['id_image']; return Language::getIsoById((int)$id_lang).'-default'; } public static function getProductProperties($id_lang, $row, Context $context = null) { if (!$row['id_product']) return false; if ($context == null) $context = Context::getContext(); // Product::getDefaultAttribute is only called if id_product_attribute is missing from the SQL query at the origin of it: // consider adding it in order to avoid unnecessary queries $row['allow_oosp'] = Product::isAvailableWhenOutOfStock($row['out_of_stock']); if (Combination::isFeatureActive() && (!isset($row['id_product_attribute']) || !$row['id_product_attribute']) && ((isset($row['cache_default_attribute']) && ($ipa_default = $row['cache_default_attribute']) !== null) || ($ipa_default = Product::getDefaultAttribute($row['id_product'], !$row['allow_oosp'])))) $row['id_product_attribute'] = $ipa_default; if (!Combination::isFeatureActive() || !isset($row['id_product_attribute'])) $row['id_product_attribute'] = 0; // Tax $usetax = Tax::excludeTaxeOption(); $cache_key = $row['id_product'].'-'.$row['id_product_attribute'].'-'.$id_lang.'-'.(int)$usetax; if (isset($row['id_product_pack'])) $cache_key .= '-pack'.$row['id_product_pack']; if (isset(self::$producPropertiesCache[$cache_key])) return array_merge($row, self::$producPropertiesCache[$cache_key]); // Datas $row['category'] = Category::getLinkRewrite((int)$row['id_category_default'], (int)$id_lang); $row['link'] = $context->link->getProductLink((int)$row['id_product'], $row['link_rewrite'], $row['category'], $row['ean13']); $row['attribute_price'] = 0; if (isset($row['id_product_attribute']) && $row['id_product_attribute']) $row['attribute_price'] = (float)Product::getProductAttributePrice($row['id_product_attribute']); $row['price_tax_exc'] = Product::getPriceStatic( (int)$row['id_product'], false, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), (self::$_taxCalculationMethod == PS_TAX_EXC ? 2 : 6) ); if (self::$_taxCalculationMethod == PS_TAX_EXC) { $row['price_tax_exc'] = Tools::ps_round($row['price_tax_exc'], 2); $row['price'] = Product::getPriceStatic( (int)$row['id_product'], true, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), 6 ); $row['price_without_reduction'] = Product::getPriceStatic( (int)$row['id_product'], false, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), 2, null, false, false ); } else { $row['price'] = Tools::ps_round( Product::getPriceStatic( (int)$row['id_product'], true, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), 2 ), 2 ); $row['price_without_reduction'] = Product::getPriceStatic( (int)$row['id_product'], true, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), 6, null, false, false ); } $row['reduction'] = Product::getPriceStatic( (int)$row['id_product'], (bool)$usetax, (int)$row['id_product_attribute'], 6, null, true, true, 1, true, null, null, null, $specific_prices ); $row['specific_prices'] = $specific_prices; $row['quantity'] = Product::getQuantity( (int)$row['id_product'], 0, isset($row['cache_is_pack']) ? $row['cache_is_pack'] : null ); if ($row['id_product_attribute']) { $row['quantity_all_versions'] = $row['quantity']; $row['quantity'] = Product::getQuantity( (int)$row['id_product'], $row['id_product_attribute'], isset($row['cache_is_pack']) ? $row['cache_is_pack'] : null ); } $row['id_image'] = Product::defineProductImage($row, $id_lang); $row['features'] = Product::getFrontFeaturesStatic((int)$id_lang, $row['id_product']); $row['attachments'] = array(); if (!isset($row['cache_has_attachments']) || $row['cache_has_attachments']) $row['attachments'] = Product::getAttachmentsStatic((int)$id_lang, $row['id_product']); $row['virtual'] = ((!isset($row['is_virtual']) || $row['is_virtual']) ? 1 : 0); // Pack management $row['pack'] = (!isset($row['cache_is_pack']) ? Pack::isPack($row['id_product']) : (int)$row['cache_is_pack']); $row['packItems'] = $row['pack'] ? Pack::getItemTable($row['id_product'], $id_lang) : array(); $row['nopackprice'] = $row['pack'] ? Pack::noPackPrice($row['id_product']) : 0; if ($row['pack'] && !Pack::isInStock($row['id_product'])) $row['quantity'] = 0; $row = Product::getTaxesInformations($row, $context); self::$producPropertiesCache[$cache_key] = $row; return self::$producPropertiesCache[$cache_key]; } public static function getTaxesInformations($row, Context $context = null) { static $address = null; if ($context === null) $context = Context::getContext(); if ($address === null) $address = new Address(); $address->id_country = (int)$context->country->id; $address->id_state = 0; $address->postcode = 0; $tax_manager = TaxManagerFactory::getManager($address, Product::getIdTaxRulesGroupByIdProduct((int)$row['id_product'], $context)); $row['rate'] = $tax_manager->getTaxCalculator()->getTotalRate(); $row['tax_name'] = $tax_manager->getTaxCalculator()->getTaxesName(); return $row; } public static function getProductsProperties($id_lang, $query_result) { $results_array = array(); if (is_array($query_result)) foreach ($query_result as $row) if ($row2 = Product::getProductProperties($id_lang, $row)) $results_array[] = $row2; return $results_array; } /* * Select all features for a given language * * @param $id_lang Language id * @return array Array with feature's data */ public static function getFrontFeaturesStatic($id_lang, $id_product) { if (!Feature::isFeatureActive()) return array(); if (!array_key_exists($id_product.'-'.$id_lang, self::$_frontFeaturesCache)) { self::$_frontFeaturesCache[$id_product.'-'.$id_lang] = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT name, value, pf.id_feature FROM '._DB_PREFIX_.'feature_product pf LEFT JOIN '._DB_PREFIX_.'feature_lang fl ON (fl.id_feature = pf.id_feature AND fl.id_lang = '.(int)$id_lang.') LEFT JOIN '._DB_PREFIX_.'feature_value_lang fvl ON (fvl.id_feature_value = pf.id_feature_value AND fvl.id_lang = '.(int)$id_lang.') LEFT JOIN '._DB_PREFIX_.'feature f ON (f.id_feature = pf.id_feature AND fl.id_lang = '.(int)$id_lang.') WHERE pf.id_product = '.(int)$id_product.' ORDER BY f.position ASC' ); } return self::$_frontFeaturesCache[$id_product.'-'.$id_lang]; } public function getFrontFeatures($id_lang) { return Product::getFrontFeaturesStatic($id_lang, $this->id); } public static function getAttachmentsStatic($id_lang, $id_product) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT * FROM '._DB_PREFIX_.'product_attachment pa LEFT JOIN '._DB_PREFIX_.'attachment a ON a.id_attachment = pa.id_attachment LEFT JOIN '._DB_PREFIX_.'attachment_lang al ON (a.id_attachment = al.id_attachment AND al.id_lang = '.(int)$id_lang.') WHERE pa.id_product = '.(int)$id_product); } public function getAttachments($id_lang) { return Product::getAttachmentsStatic($id_lang, $this->id); } /* ** Customization management */ public static function getAllCustomizedDatas($id_cart, $id_lang = null, $only_in_cart = true) { if (!Customization::isFeatureActive()) return false; // No need to query if there isn't any real cart! if (!$id_cart) return false; if (!$id_lang) $id_lang = Context::getContext()->language->id; if (!$result = Db::getInstance()->executeS(' SELECT cd.`id_customization`, c.`id_address_delivery`, c.`id_product`, cfl.`id_customization_field`, c.`id_product_attribute`, cd.`type`, cd.`index`, cd.`value`, cfl.`name` FROM `'._DB_PREFIX_.'customized_data` cd NATURAL JOIN `'._DB_PREFIX_.'customization` c LEFT JOIN `'._DB_PREFIX_.'customization_field_lang` cfl ON (cfl.id_customization_field = cd.`index` AND id_lang = '.(int)$id_lang.') WHERE c.`id_cart` = '.(int)$id_cart. ($only_in_cart ? ' AND c.`in_cart` = 1' : '').' ORDER BY `id_product`, `id_product_attribute`, `type`, `index`')) return false; $customized_datas = array(); foreach ($result as $row) $customized_datas[(int)$row['id_product']][(int)$row['id_product_attribute']][(int)$row['id_address_delivery']][(int)$row['id_customization']]['datas'][(int)$row['type']][] = $row; if (!$result = Db::getInstance()->executeS( 'SELECT `id_product`, `id_product_attribute`, `id_customization`, `id_address_delivery`, `quantity`, `quantity_refunded`, `quantity_returned` FROM `'._DB_PREFIX_.'customization` WHERE `id_cart` = '.(int)($id_cart).($only_in_cart ? ' AND `in_cart` = 1' : ''))) return false; foreach ($result as $row) { $customized_datas[(int)$row['id_product']][(int)$row['id_product_attribute']][(int)$row['id_address_delivery']][(int)$row['id_customization']]['quantity'] = (int)$row['quantity']; $customized_datas[(int)$row['id_product']][(int)$row['id_product_attribute']][(int)$row['id_address_delivery']][(int)$row['id_customization']]['quantity_refunded'] = (int)$row['quantity_refunded']; $customized_datas[(int)$row['id_product']][(int)$row['id_product_attribute']][(int)$row['id_address_delivery']][(int)$row['id_customization']]['quantity_returned'] = (int)$row['quantity_returned']; } return $customized_datas; } public static function addCustomizationPrice(&$products, &$customized_datas) { if (!$customized_datas) return; foreach ($products as &$product_update) { if (!Customization::isFeatureActive()) { $product_update['customizationQuantityTotal'] = 0; $product_update['customizationQuantityRefunded'] = 0; $product_update['customizationQuantityReturned'] = 0; } else { $customization_quantity = 0; $customization_quantity_refunded = 0; $customization_quantity_returned = 0; /* Compatibility */ $product_id = (int)(isset($product_update['id_product']) ? $product_update['id_product'] : $product_update['product_id']); $product_attribute_id = (int)(isset($product_update['id_product_attribute']) ? $product_update['id_product_attribute'] : $product_update['product_attribute_id']); $id_address_delivery = (int)$product_update['id_address_delivery']; $product_quantity = (int)(isset($product_update['cart_quantity']) ? $product_update['cart_quantity'] : $product_update['product_quantity']); $price = isset($product_update['price']) ? $product_update['price'] : $product_update['product_price']; if (isset($product_update['price_wt']) && $product_update['price_wt']) $price_wt = $product_update['price_wt']; else $price_wt = $price * (1 + ((isset($product_update['tax_rate']) ? $product_update['tax_rate'] : $product_update['rate']) * 0.01)); if (isset($customized_datas[$product_id][$product_attribute_id])) foreach ($customized_datas[$product_id][$product_attribute_id][$id_address_delivery] as $customization) { $customization_quantity += (int)$customization['quantity']; $customization_quantity_refunded += (int)$customization['quantity_refunded']; $customization_quantity_returned += (int)$customization['quantity_returned']; } $product_update['customizationQuantityTotal'] = $customization_quantity; $product_update['customizationQuantityRefunded'] = $customization_quantity_refunded; $product_update['customizationQuantityReturned'] = $customization_quantity_returned; if ($customization_quantity) { $product_update['total_wt'] = $price_wt * ($product_quantity - $customization_quantity); $product_update['total_customization_wt'] = $price_wt * $customization_quantity; $product_update['total'] = $price * ($product_quantity - $customization_quantity); $product_update['total_customization'] = $price * $customization_quantity; } } } } /* ** Customization fields' label management */ protected function _checkLabelField($field, $value) { if (!Validate::isLabel($value)) return false; $tmp = explode('_', $field); if (count($tmp) < 4) return false; return $tmp; } protected function _deleteOldLabels() { $max = array( Product::CUSTOMIZE_FILE => (int)Tools::getValue('uploadable_files'), Product::CUSTOMIZE_TEXTFIELD => (int)Tools::getValue('text_fields') ); /* Get customization field ids */ if (($result = Db::getInstance()->executeS( 'SELECT `id_customization_field`, `type` FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id.' ORDER BY `id_customization_field`') ) === false) return false; if (empty($result)) return true; $customization_fields = array( Product::CUSTOMIZE_FILE => array(), Product::CUSTOMIZE_TEXTFIELD => array() ); foreach ($result as $row) $customization_fields[(int)$row['type']][] = (int)$row['id_customization_field']; $extra_file = count($customization_fields[Product::CUSTOMIZE_FILE]) - $max[Product::CUSTOMIZE_FILE]; $extra_text = count($customization_fields[Product::CUSTOMIZE_TEXTFIELD]) - $max[Product::CUSTOMIZE_TEXTFIELD]; /* If too much inside the database, deletion */ if ($extra_file > 0 && count($customization_fields[Product::CUSTOMIZE_FILE]) - $extra_file >= 0 && (!Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id.' AND `type` = '.Product::CUSTOMIZE_FILE.' AND `id_customization_field` >= '.(int)$customization_fields[Product::CUSTOMIZE_FILE][count($customization_fields[Product::CUSTOMIZE_FILE]) - $extra_file] ) || !Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field_lang` WHERE `id_customization_field` NOT IN ( SELECT `id_customization_field` FROM `'._DB_PREFIX_.'customization_field` )' ))) return false; if ($extra_text > 0 && count($customization_fields[Product::CUSTOMIZE_TEXTFIELD]) - $extra_text >= 0 && (!Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id.' AND `type` = '.Product::CUSTOMIZE_TEXTFIELD.' AND `id_customization_field` >= '.(int)$customization_fields[Product::CUSTOMIZE_TEXTFIELD][count($customization_fields[Product::CUSTOMIZE_TEXTFIELD]) - $extra_text] ) || !Db::getInstance()->execute( 'DELETE FROM `'._DB_PREFIX_.'customization_field_lang` WHERE `id_customization_field` NOT IN ( SELECT `id_customization_field` FROM `'._DB_PREFIX_.'customization_field` )' ))) return false; // Refresh cache of feature detachable Configuration::updateGlobalValue('PS_CUSTOMIZATION_FEATURE_ACTIVE', Customization::isCurrentlyUsed()); return true; } protected function _createLabel(&$languages, $type) { // Label insertion if (!Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'customization_field` (`id_product`, `type`, `required`) VALUES ('.(int)$this->id.', '.(int)$type.', 0)') || !$id_customization_field = (int)Db::getInstance()->Insert_ID()) return false; // Multilingual label name creation $values = ''; foreach ($languages as $language) $values .= '('.(int)$id_customization_field.', '.(int)$language['id_lang'].', \'\'), '; $values = rtrim($values, ', '); if (!Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'customization_field_lang` (`id_customization_field`, `id_lang`, `name`) VALUES '.$values)) return false; // Set cache of feature detachable to true Configuration::updateGlobalValue('PS_CUSTOMIZATION_FEATURE_ACTIVE', '1'); return true; } public function createLabels($uploadable_files, $text_fields) { $languages = Language::getLanguages(); if ((int)$uploadable_files > 0) for ($i = 0; $i < (int)$uploadable_files; $i++) if (!$this->_createLabel($languages, Product::CUSTOMIZE_FILE)) return false; if ((int)$text_fields > 0) for ($i = 0; $i < (int)$text_fields; $i++) if (!$this->_createLabel($languages, Product::CUSTOMIZE_TEXTFIELD)) return false; return true; } public function updateLabels() { $has_required_fields = 0; foreach ($_POST as $field => $value) /* Label update */ if (strncmp($field, 'label_', 6) == 0) { if (!$tmp = $this->_checkLabelField($field, $value)) return false; /* Multilingual label name update */ if (!Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'customization_field_lang` (`id_customization_field`, `id_lang`, `name`) VALUES ('.(int)$tmp[2].', '.(int)$tmp[3].', \''.pSQL($value).'\') ON DUPLICATE KEY UPDATE `name` = \''.pSQL($value).'\'')) return false; $is_required = isset($_POST['require_'.(int)$tmp[1].'_'.(int)$tmp[2]]) ? 1 : 0; $has_required_fields |= $is_required; /* Require option update */ if (!Db::getInstance()->execute( 'UPDATE `'._DB_PREFIX_.'customization_field` SET `required` = '.(int)$is_required.' WHERE `id_customization_field` = '.(int)$tmp[2])) return false; } if ($has_required_fields && !ObjectModel::updateMultishopTable('product', array('customizable' => 2), 'a.id_product = '.(int)$this->id)) return false; if (!$this->_deleteOldLabels()) return false; return true; } public function getCustomizationFields($id_lang = false) { if (!Customization::isFeatureActive()) return false; if (!$result = Db::getInstance()->executeS(' SELECT cf.`id_customization_field`, cf.`type`, cf.`required`, cfl.`name`, cfl.`id_lang` FROM `'._DB_PREFIX_.'customization_field` cf NATURAL JOIN `'._DB_PREFIX_.'customization_field_lang` cfl WHERE cf.`id_product` = '.(int)$this->id.($id_lang ? ' AND cfl.`id_lang` = '.(int)$id_lang : '').' ORDER BY cf.`id_customization_field`')) return false; if ($id_lang) return $result; $customization_fields = array(); foreach ($result as $row) $customization_fields[(int)$row['type']][(int)$row['id_customization_field']][(int)$row['id_lang']] = $row; return $customization_fields; } public function getCustomizationFieldIds() { if (!Customization::isFeatureActive()) return array(); return Db::getInstance()->executeS(' SELECT `id_customization_field`, `type`, `required` FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id); } public function getRequiredCustomizableFields() { if (!Customization::isFeatureActive()) return array(); return Db::getInstance()->executeS(' SELECT `id_customization_field`, `type` FROM `'._DB_PREFIX_.'customization_field` WHERE `id_product` = '.(int)$this->id.' AND `required` = 1' ); } public function hasAllRequiredCustomizableFields(Context $context = null) { if (!Customization::isFeatureActive()) return true; if (!$context) $context = Context::getContext(); $fields = $context->cart->getProductCustomization($this->id, null, true); if (($required_fields = $this->getRequiredCustomizableFields()) === false) return false; $fields_present = array(); foreach ($fields as $field) $fields_present[] = array('id_customization_field' => $field['index'], 'type' => $field['type']); foreach ($required_fields as $required_field) if (!in_array($required_field, $fields_present)) return false; return true; } /** * Checks if the product is in at least one of the submited categories * * @param int $id_product * @param array $categories array of category arrays * @return boolean is the product in at least one category */ public static function idIsOnCategoryId($id_product, $categories) { if (!((int)$id_product > 0) || !is_array($categories) || empty($categories)) return false; $sql = 'SELECT id_product FROM `'._DB_PREFIX_.'category_product` WHERE `id_product` = '.(int)$id_product.' AND `id_category` IN ('; foreach ($categories as $category) $sql .= (int)$category['id_category'].','; $sql = rtrim($sql, ',').')'; $hash = md5($sql); if (!isset(self::$_incat[$hash])) { if (!Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($sql)) return false; self::$_incat[$hash] = (Db::getInstance(_PS_USE_SQL_SLAVE_)->NumRows() > 0 ? true : false); } return self::$_incat[$hash]; } public function getNoPackPrice() { return Pack::noPackPrice($this->id); } public function checkAccess($id_customer) { if (!$id_customer) return (bool)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT ctg.`id_group` FROM `'._DB_PREFIX_.'category_product` cp INNER JOIN `'._DB_PREFIX_.'category_group` ctg ON (ctg.`id_category` = cp.`id_category`) WHERE cp.`id_product` = '.(int)$this->id.' AND ctg.`id_group` ='.(int)Group::getCurrent()->id); else return (bool)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT cg.`id_group` FROM `'._DB_PREFIX_.'category_product` cp INNER JOIN `'._DB_PREFIX_.'category_group` ctg ON (ctg.`id_category` = cp.`id_category`) INNER JOIN `'._DB_PREFIX_.'customer_group` cg ON (cg.`id_group` = ctg.`id_group`) WHERE cp.`id_product` = '.(int)$this->id.' AND cg.`id_customer` = '.(int)$id_customer); } /** * Add a stock movement for current product * * Since 1.5, this method only permit to add/remove available quantities of the current product in the current shop * * @see StockManager if you want to manage real stock * @see StockAvailable if you want to manage available quantities for sale on your shop(s) * * @deprecated since 1.5.0 * * @param int $quantity * @param int $id_reason - useless * @param int $id_product_attribute * @param int $id_order - useless * @param int $id_employee - useless * @return bool */ public function addStockMvt($quantity, $id_reason, $id_product_attribute = null, $id_order = null, $id_employee = null) { if (!$this->id || !$id_reason) return false; if ($id_product_attribute == null) $id_product_attribute = 0; $reason = new StockMvtReason((int)$id_reason); if (!Validate::isLoadedObject($reason)) return false; $quantity = abs((int)$quantity) * $reason->sign; return StockAvailable::updateQuantity($this->id, $id_product_attribute, $quantity); } /** * @deprecated since 1.5.0 */ public function getStockMvts($id_lang) { Tools::displayAsDeprecated(); return Db::getInstance()->executeS(' SELECT sm.id_stock_mvt, sm.date_add, sm.quantity, sm.id_order, CONCAT(pl.name, \' \', GROUP_CONCAT(IFNULL(al.name, \'\'), \'\')) product_name, CONCAT(e.lastname, \' \', e.firstname) employee, mrl.name reason FROM `'._DB_PREFIX_.'stock_mvt` sm LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON ( sm.id_product = pl.id_product AND pl.id_lang = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').' ) LEFT JOIN `'._DB_PREFIX_.'stock_mvt_reason_lang` mrl ON ( sm.id_stock_mvt_reason = mrl.id_stock_mvt_reason AND mrl.id_lang = '.(int)$id_lang.' ) LEFT JOIN `'._DB_PREFIX_.'employee` e ON ( e.id_employee = sm.id_employee ) LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON ( pac.id_product_attribute = sm.id_product_attribute ) LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON ( al.id_attribute = pac.id_attribute AND al.id_lang = '.(int)$id_lang.' ) WHERE sm.id_product='.(int)$this->id.' GROUP BY sm.id_stock_mvt '); } public static function getUrlRewriteInformations($id_product) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT pl.`id_lang`, pl.`link_rewrite`, p.`ean13`, cl.`link_rewrite` AS category_rewrite FROM `'._DB_PREFIX_.'product` p LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product`'.Shop::addSqlRestrictionOnLang('pl').') '.Shop::addSqlAssociation('product', 'p').' LEFT JOIN `'._DB_PREFIX_.'lang` l ON (pl.`id_lang` = l.`id_lang`) LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (cl.`id_category` = product_shop.`id_category_default` AND cl.`id_lang` = pl.`id_lang`'.Shop::addSqlRestrictionOnLang('cl').') WHERE p.`id_product` = '.(int)$id_product.' AND l.`active` = 1 '); } public function getIdTaxRulesGroup() { return $this->id_tax_rules_group; } public static function getIdTaxRulesGroupByIdProduct($id_product, Context $context = null) { if (!$context) $context = Context::getContext(); $key = 'product_id_tax_rules_group_'.(int)$id_product.'_'.(int)$context->shop->id; if (!Cache::isStored($key)) Cache::store($key, Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT `id_tax_rules_group` FROM `'._DB_PREFIX_.'product_shop` WHERE `id_product` = '.(int)$id_product.' AND id_shop='.(int)$context->shop->id)); return Cache::retrieve($key); } /** * @return the total taxes rate applied to the product */ public function getTaxesRate(Address $address = null) { if (!$address || !$address->id_country) $address = Address::initialize(); $tax_manager = TaxManagerFactory::getManager($address, $this->id_tax_rules_group); $tax_calculator = $tax_manager->getTaxCalculator(); return $tax_calculator->getTotalRate(); } /** * Webservice getter : get product features association * * @return array */ public function getWsProductFeatures() { $rows = $this->getFeatures(); foreach ($rows as $keyrow => $row) { foreach ($row as $keyfeature => $feature) { if ($keyfeature == 'id_feature') { $rows[$keyrow]['id'] = $feature; unset($rows[$keyrow]['id_feature']); } unset($rows[$keyrow]['id_product']); } asort($rows[$keyrow]); } return $rows; } /** * Webservice setter : set product features association * * @param $productFeatures Product Feature ids * @return boolean */ public function setWsProductFeatures($product_features) { $this->deleteProductFeatures(); foreach ($product_features as $product_feature) $this->addFeaturesToDB($product_feature['id'], $product_feature['id_feature_value']); return true; } /** * Webservice getter : get virtual field default combination * * @return int */ public function getWsDefaultCombination() { return Product::getDefaultAttribute($this->id); } /** * Webservice setter : set virtual field default combination * * @param $id_combination id default combination */ public function setWsDefaultCombination($id_combination) { $this->deleteDefaultAttributes(); return $this->setDefaultAttribute((int)$id_combination); } /** * Webservice getter : get category ids of current product for association * * @return array */ public function getWsCategories() { $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS( 'SELECT cp.`id_category` AS id FROM `'._DB_PREFIX_.'category_product` cp LEFT JOIN `'._DB_PREFIX_.'category` c ON (c.id_category = cp.id_category) '.Shop::addSqlAssociation('category', 'c').' WHERE cp.`id_product` = '.(int)$this->id ); return $result; } /** * Webservice setter : set category ids of current product for association * * @param $category_ids category ids */ public function setWsCategories($category_ids) { $ids = array(); foreach ($category_ids as $value) $ids[] = $value['id']; if ($this->deleteCategories()) { if ($ids) { $sql_values = ''; $ids = array_map('intval', $ids); foreach ($ids as $position => $id) $sql_values[] = '('.(int)$id.', '.(int)$this->id.', '.(int)$position.')'; $result = Db::getInstance()->execute(' INSERT INTO `'._DB_PREFIX_.'category_product` (`id_category`, `id_product`, `position`) VALUES '.implode(',', $sql_values) ); return $result; } } return true; } /** * Webservice getter : get product accessories ids of current product for association * * @return array */ public function getWsAccessories() { $result = Db::getInstance()->executeS( 'SELECT p.`id_product` AS id FROM `'._DB_PREFIX_.'accessory` a LEFT JOIN `'._DB_PREFIX_.'product` p ON (p.id_product = a.id_product_2) '.Shop::addSqlAssociation('product', 'p').' WHERE a.`id_product_1` = '.(int)$this->id ); return $result; } /** * Webservice setter : set product accessories ids of current product for association * * @param $accessories product ids */ public function setWsAccessories($accessories) { foreach ($accessories as $accessory) Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'accessory` (`id_product_1`, `id_product_2`) VALUES ('.(int)$this->id.', '.(int)$accessory['id'].')'); return true; } /** /** * Webservice getter : get combination ids of current product for association * * @return array */ public function getWsCombinations() { $result = Db::getInstance()->executeS( 'SELECT pa.`id_product_attribute` as id FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE `id_product` = '.(int)$this->id ); return $result; } /** * Webservice setter : set combination ids of current product for association * * @param $combinations combination ids */ public function setWsCombinations($combinations) { // No hook exec $ids_new = array(); foreach ($combinations as $combination) $ids_new[] = (int)$combination['id']; $ids_orig = array(); $original = Db::getInstance()->executeS( 'SELECT pa.`id_product_attribute` as id FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE `id_product` = '.(int)$this->id ); if (is_array($original)) foreach ($original as $id) $ids_orig[] = $id['id']; $all_ids = array(); $all = Db::getInstance()->executeS('SELECT pa.`id_product_attribute` as id FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa')); if (is_array($all)) foreach ($all as $id) $all_ids[] = $id['id']; $to_add = array(); foreach ($ids_new as $id) if (!in_array($id, $ids_orig)) $to_add[] = $id; $to_delete = array(); foreach ($ids_orig as $id) if (!in_array($id, $ids_new)) $to_delete[] = $id; // Delete rows if (count($to_delete) > 0) foreach ($to_delete as $id) { $combination = new Combination($id); $combination->delete(); } foreach ($to_add as $id) { // Update id_product if exists else create if (in_array($id, $all_ids)) Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'product_attribute` SET id_product = '.(int)$this->id.' WHERE id_product_attribute='.$id); else Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'product_attribute` (`id_product`) VALUES ('.$this->id.')'); } return true; } /** * Webservice getter : get product option ids of current product for association * * @return array */ public function getWsProductOptionValues() { $result = Db::getInstance()->executeS('SELECT DISTINCT pac.id_attribute as id FROM `'._DB_PREFIX_.'product_attribute` pa '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.id_product_attribute = pa.id_product_attribute) WHERE pa.id_product = '.(int)$this->id); return $result; } /** * Webservice getter : get virtual field position in category * * @return int */ public function getWsPositionInCategory() { $result = Db::getInstance()->executeS('SELECT position FROM `'._DB_PREFIX_.'category_product` WHERE id_category = '.(int)$this->id_category_default.' AND id_product = '.(int)$this->id); if (count($result) > 0) return $result[0]['position']; return ''; } /** * Webservice getter : get virtual field id_default_image in category * * @return int */ public function getCoverWs() { $result = $this->getCover($this->id); return $result['id_image']; } /** * Webservice setter : set virtual field id_default_image in category * * @return bool */ public function setCoverWs($id_image) { Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image_shop` image_shop, `'._DB_PREFIX_.'image` i SET image_shop.`cover` = 0 WHERE i.`id_product` = '.(int)$this->id.' AND i.id_image = image_shop.id_image AND image_shop.id_shop='.(int)Context::getContext()->shop->id); Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image_shop` SET `cover` = 1 WHERE `id_image` = '.(int)$id_image); return true; } /** * Webservice getter : get image ids of current product for association * * @return array */ public function getWsImages() { return Db::getInstance()->executeS(' SELECT i.`id_image` as id FROM `'._DB_PREFIX_.'image` i '.Shop::addSqlAssociation('image', 'i').' WHERE i.`id_product` = '.(int)$this->id.' ORDER BY i.`position`'); } public function getWsStockAvailables() { return Db::getInstance()->executeS('SELECT `id_stock_available` id, `id_product_attribute` FROM `'._DB_PREFIX_.'stock_available` WHERE `id_product`='.($this->id).StockAvailable::addSqlShopRestriction()); } public function getWsTags() { return Db::getInstance()->executeS(' SELECT `id_tag` as id FROM `'._DB_PREFIX_.'product_tag` WHERE `id_product` = '.(int)$this->id); } public function getWsManufacturerName() { return Manufacturer::getNameById((int)$this->id_manufacturer); } public static function resetEcoTax() { return ObjectModel::updateMultishopTable('product', array( 'ecotax' => 0, ), ''); } /** * Set Group reduction if needed */ public function setGroupReduction() { return GroupReduction::setProductReduction($this->id, null, $this->id_category_default); } /** * Checks if reference exists * @return boolean */ public function existsRefInDatabase($reference) { $row = Db::getInstance()->getRow(' SELECT `reference` FROM `'._DB_PREFIX_.'product` p WHERE p.reference = "'.pSQL($reference).'"'); return isset($row['reference']); } /** * Get all product attributes ids * * @since 1.5.0 * @param int $id_product the id of the product * @return array product attribute id list */ public static function getProductAttributesIds($id_product, $shop_only = false) { return Db::getInstance()->executeS(' SELECT pa.id_product_attribute FROM `'._DB_PREFIX_.'product_attribute` pa'. ($shop_only ? Shop::addSqlAssociation('product_attribute', 'pa') : '').' WHERE pa.`id_product` = '.(int)$id_product); } /** * Get label by lang and value by lang too * @todo Remove existing module condition * @param int $id_product * @param int $product_attribute_id * @return array */ public static function getAttributesParams($id_product, $id_product_attribute) { // if blocklayered module is installed we check if user has set custom attribute name if (Module::isInstalled('blocklayered')) { $nb_custom_values = Db::getInstance()->executeS(' SELECT DISTINCT la.`id_attribute`, la.`url_name` as `name` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` la ON (la.`id_attribute` = a.`id_attribute` AND la.`id_lang` = '.(int)Context::getContext()->language->id.') WHERE la.`url_name` IS NOT NULL AND la.`url_name` != \'\' AND pa.`id_product` = '.(int)$id_product.' AND pac.`id_product_attribute` = '.(int)$id_product_attribute); if (!empty($nb_custom_values)) { $tab_id_attribute = array(); foreach ($nb_custom_values as $attribute) { $tab_id_attribute[] = $attribute['id_attribute']; $group = Db::getInstance()->executeS(' SELECT g.`id_attribute_group`, g.`url_name` as `group` FROM `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` g LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute_group` = g.`id_attribute_group`) WHERE a.`id_attribute` = '.(int)$attribute['id_attribute'].' AND g.`id_lang` = '.(int)Context::getContext()->language->id.' AND g.`url_name` IS NOT NULL AND g.`url_name` != \'\''); if (empty($group)) { $group = Db::getInstance()->executeS(' SELECT g.`id_attribute_group`, g.`name` as `group` FROM `'._DB_PREFIX_.'attribute_group_lang` g LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute_group` = g.`id_attribute_group`) WHERE a.`id_attribute` = '.(int)$attribute['id_attribute'].' AND g.`id_lang` = '.(int)Context::getContext()->language->id.' AND g.`name` IS NOT NULL'); } $result[] = array_merge($attribute, $group[0]); } $values_not_custom = Db::getInstance()->executeS(' SELECT DISTINCT a.`id_attribute_group`, al.`name`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' WHERE pa.`id_product` = '.(int)$id_product.' AND pac.id_product_attribute = '.(int)$id_product_attribute.' AND a.`id_attribute` NOT IN('.implode(', ', $tab_id_attribute).')'); $result = array_merge($values_not_custom, $result); } else { $result = Db::getInstance()->executeS(' SELECT al.`name`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (al.`id_attribute` = a.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_attribute` = a.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') WHERE pa.`id_product` = '.(int)$id_product.' AND pac.`id_product_attribute` = '.(int)$id_product_attribute.' AND agl.`id_lang` = '.(int)Context::getContext()->language->id); } } else { $result = Db::getInstance()->executeS(' SELECT al.`name`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (al.`id_attribute` = a.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (pac.`id_attribute` = a.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') WHERE pa.`id_product` = '.(int)$id_product.' AND pac.`id_product_attribute` = '.(int)$id_product_attribute.' AND agl.`id_lang` = '.(int)Context::getContext()->language->id); } return $result; } /** * @todo Remove existing module condition * @param int $id_product */ public static function getAttributesInformationsByProduct($id_product) { // if blocklayered module is installed we check if user has set custom attribute name if (Module::isInstalled('blocklayered')) { $nb_custom_values = Db::getInstance()->executeS(' SELECT DISTINCT la.`id_attribute`, la.`url_name` as `attribute` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' LEFT JOIN `'._DB_PREFIX_.'layered_indexable_attribute_lang_value` la ON (la.`id_attribute` = a.`id_attribute` AND la.`id_lang` = '.(int)Context::getContext()->language->id.') WHERE la.`url_name` IS NOT NULL AND la.`url_name` != \'\' AND pa.`id_product` = '.(int)$id_product); if (!empty($nb_custom_values)) { $tab_id_attribute = array(); foreach ($nb_custom_values as $attribute) { $tab_id_attribute[] = $attribute['id_attribute']; $group = Db::getInstance()->executeS(' SELECT g.`id_attribute_group`, g.`url_name` as `group` FROM `'._DB_PREFIX_.'layered_indexable_attribute_group_lang_value` g LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute_group` = g.`id_attribute_group`) WHERE a.`id_attribute` = '.(int)$attribute['id_attribute'].' AND g.`id_lang` = '.(int)Context::getContext()->language->id.' AND g.`url_name` IS NOT NULL AND g.`url_name` != \'\''); if (empty($group)) { $group = Db::getInstance()->executeS(' SELECT g.`id_attribute_group`, g.`name` as `group` FROM `'._DB_PREFIX_.'attribute_group_lang` g LEFT JOIN `'._DB_PREFIX_.'attribute` a ON (a.`id_attribute_group` = g.`id_attribute_group`) WHERE a.`id_attribute` = '.(int)$attribute['id_attribute'].' AND g.`id_lang` = '.(int)Context::getContext()->language->id.' AND g.`name` IS NOT NULL'); } $result[] = array_merge($attribute, $group[0]); } $values_not_custom = Db::getInstance()->executeS(' SELECT DISTINCT a.`id_attribute`, a.`id_attribute_group`, al.`name` as `attribute`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' '.Shop::addSqlAssociation('attribute', 'pac').' WHERE pa.`id_product` = '.(int)$id_product.' AND a.`id_attribute` NOT IN('.implode(', ', $tab_id_attribute).')'); $result = array_merge($values_not_custom, $result); } else { $result = Db::getInstance()->executeS(' SELECT DISTINCT a.`id_attribute`, a.`id_attribute_group`, al.`name` as `attribute`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' '.Shop::addSqlAssociation('attribute', 'pac').' WHERE pa.`id_product` = '.(int)$id_product); } } else { $result = Db::getInstance()->executeS(' SELECT DISTINCT a.`id_attribute`, a.`id_attribute_group`, al.`name` as `attribute`, agl.`name` as `group` FROM `'._DB_PREFIX_.'attribute` a LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'attribute_group_lang` agl ON (a.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = '.(int)Context::getContext()->language->id.') LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON (a.`id_attribute` = pac.`id_attribute`) LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (pac.`id_product_attribute` = pa.`id_product_attribute`) '.Shop::addSqlAssociation('product_attribute', 'pa').' '.Shop::addSqlAssociation('attribute', 'pac').' WHERE pa.`id_product` = '.(int)$id_product); } return $result; } /** * Get the combination url anchor of the product * * @param integer $id_product_attribute * @return string */ public function getAnchor($id_product_attribute) { $attributes = Product::getAttributesParams($this->id, $id_product_attribute); $anchor = '#'; foreach ($attributes as &$a) { foreach ($a as &$ $b = str_replace('-', '_', Tools::link_rewrite($); $anchor .= '/'.$a['group'].'-'.$a['name']; } return $anchor; } /** * Gets the name of a given product, in the given lang * * @since 1.5.0 * @param int $id_product * @param int $id_product_attribute Optional * @param int $id_lang Optional * @return string */ public static function getProductName($id_product, $id_product_attribute = null, $id_lang = null) { // use the lang in the context if $id_lang is not defined if (!$id_lang) $id_lang = (int)Context::getContext()->language->id; // creates the query object $query = new DbQuery(); // selects different names, if it is a combination if ($id_product_attribute) $query->select('IFNULL(CONCAT(pl.name, \' : \', GROUP_CONCAT(DISTINCT agl.`name`, \' - \', al.name SEPARATOR \', \')),pl.name) as name'); else $query->select('DISTINCT pl.name as name'); // adds joins & where clauses for combinations if ($id_product_attribute) { $query->from('product_attribute', 'pa'); $query->join(Shop::addSqlAssociation('product_attribute', 'pa')); $query->innerJoin('product_lang', 'pl', 'pl.id_product = pa.id_product AND pl.id_lang = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl')); $query->leftJoin('product_attribute_combination', 'pac', 'pac.id_product_attribute = pa.id_product_attribute'); $query->leftJoin('attribute', 'atr', 'atr.id_attribute = pac.id_attribute'); $query->leftJoin('attribute_lang', 'al', 'al.id_attribute = atr.id_attribute AND al.id_lang = '.(int)$id_lang); $query->leftJoin('attribute_group_lang', 'agl', 'agl.id_attribute_group = atr.id_attribute_group AND agl.id_lang = '.(int)$id_lang); $query->where('pa.id_product = '.(int)$id_product.' AND pa.id_product_attribute = '.(int)$id_product_attribute); } else // or just adds a 'where' clause for a simple product { $query->from('product_lang', 'pl'); $query->where('pl.id_product = '.(int)$id_product); $query->where('pl.id_lang = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl')); } return Db::getInstance()->getValue($query); } public function addWs($autodate = true, $null_values = false) { $success = $this->add($autodate, $null_values); if ($success && Configuration::get('PS_SEARCH_INDEXATION')) Search::indexation(false, $this->id); return $success; } public function updateWs($null_values = false) { $success = parent::update($null_values); if ($success && Configuration::get('PS_SEARCH_INDEXATION')) Search::indexation(false, $this->id); return $success; } /** * For a given product, returns its real quantity * * @since 1.5.0 * @param int $id_product * @param int $id_product_attribute * @param int $id_warehouse * @param int $id_shop * @return int real_quantity */ public static function getRealQuantity($id_product, $id_product_attribute = 0, $id_warehouse = 0, $id_shop = null) { static $manager = null; if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && is_null($manager)) $manager = StockManagerFactory::getManager(); if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT') && Product::usesAdvancedStockManagement($id_product) && StockAvailable::dependsOnStock($id_product, $id_shop)) return $manager->getProductRealQuantities($id_product, $id_product_attribute, $id_warehouse, true); else return StockAvailable::getQuantityAvailableByProduct($id_product, $id_product_attribute, $id_shop); } /** * For a given product, tells if it uses the advanced stock management * * @since 1.5.0 * @param int $id_product * @return bool */ public static function usesAdvancedStockManagement($id_product) { $query = new DbQuery; $query->select('product_shop.advanced_stock_management'); $query->from('product', 'p'); $query->join(Shop::addSqlAssociation('product', 'p')); $query->where('p.id_product = '.(int)$id_product); return (bool)Db::getInstance()->getValue($query); } /** * This method allows to flush price cache * @static * @since 1.5.0 */ public static function flushPriceCache() { self::$_prices = array(); self::$_pricesLevel2 = array(); } /** * Get list of parent categories * * @since 1.5.0 * @param int $id_lang * @return array */ public function getParentCategories($id_lang = null) { if (!$id_lang) $id_lang = Context::getContext()->language->id; $interval = Category::getInterval($this->id_category_default); $sql = new DbQuery(); $sql->from('category', 'c'); $sql->leftJoin('category_lang', 'cl', 'c.id_category = cl.id_category AND id_lang = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl')); $sql->where('c.nleft <= '.(int)$interval['nleft'].' AND c.nright >= '.(int)$interval['nright']); $sql->orderBy('c.nleft'); return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); } /** * Fill the variables used for stock management */ public function loadStockData() { if (Validate::isLoadedObject($this)) { // By default, the product quantity correspond to the available quantity to sell in the current shop $this->quantity = StockAvailable::getQuantityAvailableByProduct($this->id, 0); $this->out_of_stock = StockAvailable::outOfStock($this->id); $this->depends_on_stock = StockAvailable::dependsOnStock($this->id); if (Context::getContext()->shop->getContext() == Shop::CONTEXT_GROUP && Context::getContext()->shop->getContextShopGroup()->share_stock == 1) $this->advanced_stock_management = $this->useAdvancedStockManagement(); } } public function useAdvancedStockManagement() { return Db::getInstance()->getValue(' SELECT `advanced_stock_management` FROM '._DB_PREFIX_.'product_shop WHERE id_product='.(int)$this->id.Shop::addSqlRestriction() ); } public function setAdvancedStockManagement($value) { $this->advanced_stock_management = (int)$value; if (Context::getContext()->shop->getContext() == Shop::CONTEXT_GROUP && Context::getContext()->shop->getContextShopGroup()->share_stock == 1) Db::getInstance()->execute(' UPDATE `'._DB_PREFIX_.'product_shop` SET `advanced_stock_management`='.(int)$value.' WHERE id_product='.(int)$this->id.Shop::addSqlRestriction() ); else $this->save(); } /** * get the default category according to the shop */ public function getDefaultCategory() { $default_category = Db::getInstance()->getValue(' SELECT product_shop.`id_category_default` FROM `'._DB_PREFIX_.'product` p '.Shop::addSqlAssociation('product', 'p').' WHERE p.`id_product` = '.(int)$this->id); if (!$default_category) return array('id_category_default' => Context::getContext()->shop->id_category); else return $default_category; } public static function getShopsByProduct($id_product) { return Db::getInstance()->executeS(' SELECT `id_shop` FROM `'._DB_PREFIX_.'product_shop` WHERE `id_product` = '.(int)$id_product); } /** * Remove all downloadable files for product and its attributes * * @return bool */ public function deleteDownload() { $result = true; $collection_download = new Collection('ProductDownload'); $collection_download->where('id_product', '=', $this->id); foreach ($collection_download as $product_download) $result &= $product_download->delete(true); return $result; } /** * @deprecated 1.5.0.10 * @see Product::getAttributeCombinations() * @param int $id_lang */ public function getAttributeCombinaisons($id_lang) { Tools::displayAsDeprecated('Use Product::getAttributeCombinations($id_lang)'); return $this->getAttributeCombinations($id_lang); } /** * @deprecated 1.5.0.10 * @see Product::deleteAttributeCombination() * @param int $id_product_attribute */ public function deleteAttributeCombinaison($id_product_attribute) { Tools::displayAsDeprecated('Use Product::deleteAttributeCombination($id_product_attribute)'); return $this->deleteAttributeCombination($id_product_attribute); } /** * Get the product type (simple, virtual, pack) * @since in 1.5.0 * * @return int */ public function getType() { if (!$this->id) return Product::PTYPE_SIMPLE; if (Pack::isPack($this->id)) return Product::PTYPE_PACK; if ($this->is_virtual) return Product::PTYPE_VIRTUAL; return Product::PTYPE_SIMPLE; } public function hasAttributesInOtherShops() { return (bool)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' SELECT pa.id_product_attribute FROM `'._DB_PREFIX_.'product_attribute` pa LEFT JOIN `'._DB_PREFIX_.'product_attribute_shop` pas ON (pa.`id_product_attribute` = pas.`id_product_attribute`) WHERE pa.`id_product` = '.(int)$this->id ); } public static function getIdTaxRulesGroupMostUsed() { return Db::getInstance()->getValue(' SELECT id_tax_rules_group FROM ( SELECT COUNT(*) n, product_shop.id_tax_rules_group FROM '._DB_PREFIX_.'product p '.Shop::addSqlAssociation('product', 'p').' JOIN '._DB_PREFIX_.'tax_rules_group trg ON (product_shop.id_tax_rules_group = trg.id_tax_rules_group) WHERE trg.active = 1 GROUP BY product_shop.id_tax_rules_group ORDER BY n DESC LIMIT 1 ) most_used' ); } /** * For a given ean13 reference, returns the corresponding id * * @param string $ean13 * @return int id */ public static function getIdByEan13($ean13) { if (empty($ean13)) return 0; if(!Validate::isEan13($ean13)) return 0; $query = new DbQuery(); $query->select('p.id_product'); $query->from('product', 'p'); $query->where('p.ean13 = \''.pSQL($ean13).'\''); return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query); } public function getWsType() { $type_information = array( Product::PTYPE_SIMPLE => 'simple', Product::PTYPE_PACK => 'pack', Product::PTYPE_VIRTUAL => 'virtual', ); return $type_information[$this->getType()]; } public function getWsProductBundle() { return Db::getInstance()->executeS('SELECT id_product_item as id, quantity FROM '._DB_PREFIX_.'pack where id_product_pack = '.(int)$this->id); } } Link to comment Share on other sites More sharing options...
math_php Posted March 13, 2014 Share Posted March 13, 2014 Hello Do you have declaration differences between the method of ObjectModel->validateField() and Product->validateField() ? In class product you have public function validateField($field, $value, $id_lang = null) does it look exactly the same in classes/ObjectModel.php ? Regards Link to comment Share on other sites More sharing options...
CLance Posted March 13, 2014 Author Share Posted March 13, 2014 Hello Do you have declaration differences between the method of ObjectModel->validateField() and Product->validateField() ? In class product you have public function validateField($field, $value, $id_lang = null) does it look exactly the same in classes/ObjectModel.php ? Regards I didnt do alteration in classes. I did some customisation in tpl and css. Here is the code for classes/ObjectModel.php <?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 [email protected] 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 <[email protected]> * @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 */ abstract class ObjectModelCore { /** * List of field types */ const TYPE_INT = 1; const TYPE_BOOL = 2; const TYPE_STRING = 3; const TYPE_FLOAT = 4; const TYPE_DATE = 5; const TYPE_HTML = 6; const TYPE_NOTHING = 7; /** * List of data to format */ const FORMAT_COMMON = 1; const FORMAT_LANG = 2; const FORMAT_SHOP = 3; /** * List of association types */ const HAS_ONE = 1; const HAS_MANY = 2; /** @var integer Object id */ public $id; /** @var integer lang id */ protected $id_lang = null; protected $id_shop = null; public $id_shop_list = null; protected $get_shop_from_context = true; protected static $fieldsRequiredDatabase = null; /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['table'] property instead */ protected $table; /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['primary'] property instead */ protected $identifier; /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsRequired = array(); /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsSize = array(); /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsValidate = array(); /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsRequiredLang = array(); /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsSizeLang = array(); /** * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead */ protected $fieldsValidateLang = array(); /** * @deprecated 1.5.0 */ protected $tables = array(); /** @var array tables */ protected $webserviceParameters = array(); /** @var string path to image directory. Used for image deletion. */ protected $image_dir = null; /** @var string file type of image files. Used for image deletion. */ protected $image_format = 'jpg'; /** * @var array Contain object definition * @since 1.5.0 */ public static $definition = array(); /** * @var array Contain current object definition */ protected $def; /** * @var array List of specific fields to update (all fields if null) */ protected $update_fields = null; /** * @var Db An instance of the db in order to avoid calling Db::getInstance() thousands of time */ protected static $db = false; /** * Returns object validation rules (fields validity) * * @param string $class Child class name for static use (optional) * @return array Validation rules (fields validity) */ public static function getValidationRules($class = __CLASS__) { $object = new $class(); return array( 'required' => $object->fieldsRequired, 'size' => $object->fieldsSize, 'validate' => $object->fieldsValidate, 'requiredLang' => $object->fieldsRequiredLang, 'sizeLang' => $object->fieldsSizeLang, 'validateLang' => $object->fieldsValidateLang, ); } /** * Build object * * @param int $id Existing object id in order to load object (optional) * @param int $id_lang Required if object is multilingual (optional) * @param int $id_shop ID shop for objects with multishop on langs */ public function __construct($id = null, $id_lang = null, $id_shop = null) { if (!ObjectModel::$db) ObjectModel::$db = Db::getInstance(); $this->def = ObjectModel::getDefinition($this); $this->setDefinitionRetrocompatibility(); if ($id_lang !== null) $this->id_lang = (Language::getLanguage($id_lang) !== false) ? $id_lang : Configuration::get('PS_LANG_DEFAULT'); if ($id_shop && $this->isMultishop()) { $this->id_shop = (int)$id_shop; $this->get_shop_from_context = false; } if ($this->isMultishop() && !$this->id_shop) $this->id_shop = Context::getContext()->shop->id; if (!Validate::isTableOrIdentifier($this->def['primary']) || !Validate::isTableOrIdentifier($this->def['table'])) throw new PrestaShopException('Identifier or table format not valid for class '.get_class($this)); if ($id) { // Load object from database if object id is present $cache_id = 'objectmodel_'.$this->def['classname'].'_'.(int)$id.'_'.(int)$this->id_shop.'_'.(int)$id_lang; if (!Cache::isStored($cache_id)) { $sql = new DbQuery(); $sql->from($this->def['table'], 'a'); $sql->where('a.'.$this->def['primary'].' = '.(int)$id); // Get lang informations if ($id_lang) { $sql->leftJoin($this->def['table'].'_lang', 'b', 'a.'.$this->def['primary'].' = b.'.$this->def['primary'].' AND b.id_lang = '.(int)$id_lang); if ($this->id_shop && !empty($this->def['multilang_shop'])) $sql->where('b.id_shop = '.$this->id_shop); } // Get shop informations if (Shop::isTableAssociated($this->def['table'])) $sql->leftJoin($this->def['table'].'_shop', 'c', 'a.'.$this->def['primary'].' = c.'.$this->def['primary'].' AND c.id_shop = '.(int)$this->id_shop); if ($object_datas = ObjectModel::$db->getRow($sql)) { if (!$id_lang && isset($this->def['multilang']) && $this->def['multilang']) { $sql = 'SELECT * FROM `'.pSQL(_DB_PREFIX_.$this->def['table']).'_lang` WHERE `'.$this->def['primary'].'` = '.(int)$id .(($this->id_shop && $this->isLangMultishop()) ? ' AND `id_shop` = '.$this->id_shop : ''); if ($object_datas_lang = ObjectModel::$db->executeS($sql)) foreach ($object_datas_lang as $row) foreach ($row as $key => $value) { if (array_key_exists($key, $this) && $key != $this->def['primary']) { if (!isset($object_datas[$key]) || !is_array($object_datas[$key])) $object_datas[$key] = array(); $object_datas[$key][$row['id_lang']] = $value; } } } Cache::store($cache_id, $object_datas); } } else $object_datas = Cache::retrieve($cache_id); if ($object_datas) { $this->id = (int)$id; foreach ($object_datas as $key => $value) if (array_key_exists($key, $this)) $this->{$key} = $value; } } } /** * Prepare fields for ObjectModel class (add, update) * All fields are verified (pSQL, intval...) * * @return array All object fields */ public function getFields() { $this->validateFields(); $fields = $this->formatFields(self::FORMAT_COMMON); // For retro compatibility if (Shop::isTableAssociated($this->def['table'])) $fields = array_merge($fields, $this->getFieldsShop()); // Ensure that we get something to insert if (!$fields && isset($this->id) && Validate::isUnsignedId($this->id)) $fields[$this->def['primary']] = $this->id; return $fields; } /** * Prepare fields for multishop * Fields are not validated here, we considere they are already validated in getFields() method, this * not the best solution but this is the only one possible for retro compatibility. * * @since 1.5.0 * @return array All object fields */ public function getFieldsShop() { $fields = $this->formatFields(self::FORMAT_SHOP); if (!$fields && isset($this->id) && Validate::isUnsignedId($this->id)) $fields[$this->def['primary']] = $this->id; return $fields; } /** * Prepare multilang fields * * @since 1.5.0 * @return array */ public function getFieldsLang() { // Retrocompatibility if (method_exists($this, 'getTranslationsFieldsChild')) return $this->getTranslationsFieldsChild(); $this->validateFieldsLang(); $is_lang_multishop = $this->isLangMultishop(); $fields = array(); if ($this->id_lang === null) foreach (Language::getLanguages(false) as $language) { $fields[$language['id_lang']] = $this->formatFields(self::FORMAT_LANG, $language['id_lang']); $fields[$language['id_lang']]['id_lang'] = $language['id_lang']; if ($this->id_shop && $is_lang_multishop) $fields[$language['id_lang']]['id_shop'] = (int)$this->id_shop; } else { $fields = array($this->id_lang => $this->formatFields(self::FORMAT_LANG, $this->id_lang)); $fields[$this->id_lang]['id_lang'] = $this->id_lang; if ($this->id_shop && $is_lang_multishop) $fields[$this->id_lang]['id_shop'] = (int)$this->id_shop; } return $fields; } /** * @since 1.5.0 * @param int $type FORMAT_COMMON or FORMAT_LANG or FORMAT_SHOP * @param int $id_lang If this parameter is given, only take lang fields * @return array */ protected function formatFields($type, $id_lang = null) { $fields = array(); // Set primary key in fields if (isset($this->id)) $fields[$this->def['primary']] = $this->id; foreach ($this->def['fields'] as $field => $data) { // Only get fields we need for the type // E.g. if only lang fields are filtered, ignore fields without lang => true if (($type == self::FORMAT_LANG && empty($data['lang'])) || ($type == self::FORMAT_SHOP && empty($data['shop'])) || ($type == self::FORMAT_COMMON && (!empty($data['shop']) || !empty($data['lang'])))) continue; if (is_array($this->update_fields)) if ((!empty($data['lang']) || !empty($data['shop'])) && (empty($this->update_fields[$field]) || ($type == self::FORMAT_LANG && empty($this->update_fields[$field][$id_lang])))) continue; // Get field value, if value is multilang and field is empty, use value from default lang $value = $this->$field; if ($type == self::FORMAT_LANG && $id_lang && is_array($value)) { if (!empty($value[$id_lang])) $value = $value[$id_lang]; else if (!empty($data['required'])) $value = $value[Configuration::get('PS_LANG_DEFAULT')]; else $value = ''; } // Format field value $fields[$field] = ObjectModel::formatValue($value, $data['type']); } return $fields; } /** * Format a data * * @param mixed $value * @param int $type */ public static function formatValue($value, $type, $with_quotes = false) { switch ($type) { case self::TYPE_INT : return (int)$value; case self::TYPE_BOOL : return (int)$value; case self::TYPE_FLOAT : return (float)str_replace(',', '.', $value); case self::TYPE_DATE : if (!$value) return '0000-00-00'; if ($with_quotes) return '\''.pSQL($value).'\''; return pSQL($value); case self::TYPE_HTML : if ($with_quotes) return '\''.pSQL($value, true).'\''; return pSQL($value, true); case self::TYPE_NOTHING : return $value; case self::TYPE_STRING : default : if ($with_quotes) return '\''.pSQL($value).'\''; return pSQL($value); } } /** * Save current object to database (add or update) * * @param bool $null_values * @param bool $autodate * @return boolean Insertion result */ public function save($null_values = false, $autodate = true) { return (int)$this->id > 0 ? $this->update($null_values) : $this->add($autodate, $null_values); } /** * Add current object to database * * @param bool $null_values * @param bool $autodate * @return boolean Insertion result */ public function add($autodate = true, $null_values = false) { if (!ObjectModel::$db) ObjectModel::$db = Db::getInstance(); // @hook actionObject*AddBefore Hook::exec('actionObjectAddBefore', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'AddBefore', array('object' => $this)); // Automatically fill dates if ($autodate && property_exists($this, 'date_add')) $this->date_add = date('Y-m-d H:i:s'); if ($autodate && property_exists($this, 'date_upd')) $this->date_upd = date('Y-m-d H:i:s'); if (Shop::isTableAssociated($this->def['table'])) { $id_shop_list = Shop::getContextListShopID(); if (count($this->id_shop_list) > 0) $id_shop_list = $this->id_shop_list; } // Database insertion if (isset($this->id) && !Tools::getValue('forceIDs')) unset($this->id); if (Shop::checkIdShopDefault($this->def['table'])) $this->id_shop_default = min($id_shop_list); if (!$result = ObjectModel::$db->insert($this->def['table'], $this->getFields(), $null_values)) return false; // Get object id in database $this->id = ObjectModel::$db->Insert_ID(); // Database insertion for multishop fields related to the object if (Shop::isTableAssociated($this->def['table'])) { $fields = $this->getFieldsShop(); $fields[$this->def['primary']] = (int)$this->id; foreach ($id_shop_list as $id_shop) { $fields['id_shop'] = (int)$id_shop; $result &= ObjectModel::$db->insert($this->def['table'].'_shop', $fields, $null_values); } } if (!$result) return false; // Database insertion for multilingual fields related to the object if (!empty($this->def['multilang'])) { $fields = $this->getFieldsLang(); if ($fields && is_array($fields)) { $shops = Shop::getCompleteListOfShopsID(); $asso = Shop::getAssoTable($this->def['table'].'_lang'); foreach ($fields as $field) { foreach (array_keys($field) as $key) if (!Validate::isTableOrIdentifier($key)) throw new PrestaShopException('key '.$key.' is not table or identifier, '); $field[$this->def['primary']] = (int)$this->id; if ($asso !== false && $asso['type'] == 'fk_shop') { foreach ($shops as $id_shop) { $field['id_shop'] = (int)$id_shop; $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field); } } else $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field); } } } // @hook actionObject*AddAfter Hook::exec('actionObjectAddAfter', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'AddAfter', array('object' => $this)); return $result; } /** * Duplicate current object to database * * @return new object */ public function duplicateObject() { $definition = ObjectModel::getDefinition($this); $res = Db::getInstance()->getRow(' SELECT * FROM `'._DB_PREFIX_.bqSQL($definition['table']).'` WHERE `'.bqSQL($definition['primary']).'` = '.(int)$this->id ); if (!$res) return false; unset($res[$definition['primary']]); foreach ($res as $field => &$value) if (isset($definition['fields'][$field])) $value = ObjectModel::formatValue($value, $definition['fields'][$field]['type']); if (!Db::getInstance()->insert($definition['table'], $res)) return false; $object_id = Db::getInstance()->Insert_ID(); if (isset($definition['multilang']) && $definition['multilang']) { $result = Db::getInstance()->executeS(' SELECT * FROM `'._DB_PREFIX_.bqSQL($definition['table']).'_lang` WHERE `'.bqSQL($definition['primary']).'` = '.(int)$this->id); if (!$result) return false; foreach ($result as &$row) foreach ($row as $field => &$value) if (isset($definition['fields'][$field])) $value = ObjectModel::formatValue($value, $definition['fields'][$field]['type']); // Keep $row2, you cannot use $row because there is an unexplicated conflict with the previous usage of this variable foreach ($result as $row2) { $row2[$definition['primary']] = (int)$object_id; if (!Db::getInstance()->insert($definition['table'].'_lang', $row2)) return false; } } $object_duplicated = new $definition['classname']((int)$object_id); $object_duplicated->duplicateShops((int)$this->id); return $object_duplicated; } /** * Update current object to database * * @param bool $null_values * @return boolean Update result */ public function update($null_values = false) { if (!ObjectModel::$db) ObjectModel::$db = Db::getInstance(); // @hook actionObject*UpdateBefore Hook::exec('actionObjectUpdateBefore', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'UpdateBefore', array('object' => $this)); $this->clearCache(); // Automatically fill dates if (array_key_exists('date_upd', $this)) $this->date_upd = date('Y-m-d H:i:s'); $id_shop_list = Shop::getContextListShopID(); if (count($this->id_shop_list) > 0) $id_shop_list = $this->id_shop_list; if (Shop::checkIdShopDefault($this->def['table']) && !$this->id_shop_default) $this->id_shop_default = min($id_shop_list); // Database update if (!$result = ObjectModel::$db->update($this->def['table'], $this->getFields(), '`'.pSQL($this->def['primary']).'` = '.(int)$this->id, 0, $null_values)) return false; // Database insertion for multishop fields related to the object if (Shop::isTableAssociated($this->def['table'])) { $fields = $this->getFieldsShop(); $fields[$this->def['primary']] = (int)$this->id; if (is_array($this->update_fields)) { $update_fields = $this->update_fields; $this->update_fields = null; $all_fields = $this->getFieldsShop(); $all_fields[$this->def['primary']] = (int)$this->id; $this->update_fields = $update_fields; } else $all_fields = $fields; foreach ($id_shop_list as $id_shop) { $fields['id_shop'] = (int)$id_shop; $all_fields['id_shop'] = (int)$id_shop; $where = $this->def['primary'].' = '.(int)$this->id.' AND id_shop = '.(int)$id_shop; // A little explanation of what we do here : we want to create multishop entry when update is called, but // only if we are in a shop context (if we are in all context, we just want to update entries that alread exists) $shop_exists = ObjectModel::$db->getValue('SELECT '.$this->def['primary'].' FROM '._DB_PREFIX_.$this->def['table'].'_shop WHERE '.$where); if ($shop_exists) $result &= ObjectModel::$db->update($this->def['table'].'_shop', $fields, $where, 0, $null_values); elseif (Shop::getContext() == Shop::CONTEXT_SHOP) $result &= ObjectModel::$db->insert($this->def['table'].'_shop', $all_fields, $null_values); } } // Database update for multilingual fields related to the object if (isset($this->def['multilang']) && $this->def['multilang']) { $fields = $this->getFieldsLang(); if (is_array($fields)) { foreach ($fields as $field) { foreach (array_keys($field) as $key) if (!Validate::isTableOrIdentifier($key)) throw new PrestaShopException('key '.$key.' is not a valid table or identifier'); // If this table is linked to multishop system, update / insert for all shops from context if ($this->isLangMultishop()) { $id_shop_list = Shop::getContextListShopID(); if (count($this->id_shop_list) > 0) $id_shop_list = $this->id_shop_list; foreach ($id_shop_list as $id_shop) { $field['id_shop'] = (int)$id_shop; $where = pSQL($this->def['primary']).' = '.(int)$this->id .' AND id_lang = '.(int)$field['id_lang'] .' AND id_shop = '.(int)$id_shop; if (ObjectModel::$db->getValue('SELECT COUNT(*) FROM '.pSQL(_DB_PREFIX_.$this->def['table']).'_lang WHERE '.$where)) $result &= ObjectModel::$db->update($this->def['table'].'_lang', $field, $where); else $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field); } } // If this table is not linked to multishop system ... else { $where = pSQL($this->def['primary']).' = '.(int)$this->id .' AND id_lang = '.(int)$field['id_lang']; if (Db::getInstance()->getValue('SELECT COUNT(*) FROM '.pSQL(_DB_PREFIX_.$this->def['table']).'_lang WHERE '.$where)) $result &= ObjectModel::$db->update($this->def['table'].'_lang', $field, $where); else $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field, $null_values); } } } } // @hook actionObject*UpdateAfter Hook::exec('actionObjectUpdateAfter', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'UpdateAfter', array('object' => $this)); return $result; } /** * Delete current object from database * * @return boolean Deletion result */ public function delete() { if (!ObjectModel::$db) ObjectModel::$db = Db::getInstance(); // @hook actionObject*DeleteBefore Hook::exec('actionObjectDeleteBefore', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'DeleteBefore', array('object' => $this)); $this->clearCache(); $result = true; // Remove association to multishop table if (Shop::isTableAssociated($this->def['table'])) { $id_shop_list = Shop::getContextListShopID(); if (count($this->id_shop_list)) $id_shop_list = $this->id_shop_list; $result &= ObjectModel::$db->delete($this->def['table'].'_shop', '`'.$this->def['primary'].'`='.(int)$this->id.' AND id_shop IN ('.implode(', ', $id_shop_list).')'); } // Database deletion $has_multishop_entries = $this->hasMultishopEntries(); if ($result && !$has_multishop_entries) $result &= ObjectModel::$db->delete($this->def['table'], '`'.pSQL($this->def['primary']).'` = '.(int)$this->id); if (!$result) return false; // Database deletion for multilingual fields related to the object if (!empty($this->def['multilang']) && !$has_multishop_entries) $result &= ObjectModel::$db->delete($this->def['table'].'_lang', '`'.pSQL($this->def['primary']).'` = '.(int)$this->id); // @hook actionObject*DeleteAfter Hook::exec('actionObjectDeleteAfter', array('object' => $this)); Hook::exec('actionObject'.get_class($this).'DeleteAfter', array('object' => $this)); return $result; } /** * Delete several objects from database * * @param array $selection * @return bool Deletion result */ public function deleteSelection($selection) { $result = true; foreach ($selection as $id) { $this->id = (int)$id; $result = $result && $this->delete(); } return $result; } /** * Toggle object status in database * * @return boolean Update result */ public function toggleStatus() { // Object must have a variable called 'active' if (!array_key_exists('active', $this)) throw new PrestaShopException('property "active" is missing in object '.get_class($this)); // Update only active field $this->setFieldsToUpdate(array('active' => true)); // Update active status on object $this->active = !(int)$this->active; // Change status to active/inactive return $this->update(false); } /** * @deprecated 1.5.0 (use getFieldsLang()) */ protected function getTranslationsFields($fields_array) { $fields = array(); if ($this->id_lang == null) foreach (Language::getLanguages(false) as $language) $this->makeTranslationFields($fields, $fields_array, $language['id_lang']); else $this->makeTranslationFields($fields, $fields_array, $this->id_lang); return $fields; } /** * @deprecated 1.5.0 */ protected function makeTranslationFields(&$fields, &$fields_array, $id_language) { $fields[$id_language]['id_lang'] = $id_language; $fields[$id_language][$this->def['primary']] = (int)$this->id; if ($this->id_shop && $this->isLangMultishop()) $fields[$id_language]['id_shop'] = (int)$this->id_shop; foreach ($fields_array as $k => $field) { $html = false; $field_name = $field; if (is_array($field)) { $field_name = $k; $html = (isset($field['html'])) ? $field['html'] : false; } /* Check fields validity */ if (!Validate::isTableOrIdentifier($field_name)) throw new PrestaShopException('identifier is not table or identifier : '.$field_name); // Copy the field, or the default language field if it's both required and empty if ((!$this->id_lang && isset($this->{$field_name}[$id_language]) && !empty($this->{$field_name}[$id_language])) || ($this->id_lang && isset($this->$field_name) && !empty($this->$field_name))) $fields[$id_language][$field_name] = $this->id_lang ? pSQL($this->$field_name, $html) : pSQL($this->{$field_name}[$id_language], $html); else if (in_array($field_name, $this->fieldsRequiredLang)) $fields[$id_language][$field_name] = pSQL($this->id_lang ? $this->$field_name : $this->{$field_name}[Configuration::get('PS_LANG_DEFAULT')], $html); else $fields[$id_language][$field_name] = ''; } } /** * Check for fields validity before database interaction * * @param bool $die * @param bool $error_return * @return bool|string */ public function validateFields($die = true, $error_return = false) { foreach ($this->def['fields'] as $field => $data) { if (!empty($data['lang'])) continue; if (is_array($this->update_fields) && empty($this->update_fields[$field])) continue; $message = $this->validateField($field, $this->$field); if ($message !== true) { if ($die) throw new PrestaShopException($message); return $error_return ? $message : false; } } return true; } /** * Check for multilingual fields validity before database interaction * * @param bool $die * @param bool $error_return * @return bool|string */ public function validateFieldsLang($die = true, $error_return = false) { foreach ($this->def['fields'] as $field => $data) { if (empty($data['lang'])) continue; $values = $this->$field; // If the object has not been loaded in multilanguage, then the value is the one for the current language of the object if (!is_array($values)) $values = array($this->id_lang => $values); // The value for the default must always be set, so we put an empty string if it does not exists if (!isset($values[Configuration::get('PS_LANG_DEFAULT')])) $values[Configuration::get('PS_LANG_DEFAULT')] = ''; foreach ($values as $id_lang => $value) { if (is_array($this->update_fields) && empty($this->update_fields[$field][$id_lang])) continue; $message = $this->validateField($field, $value, $id_lang); if ($message !== true) { if ($die) throw new PrestaShopException($message); return $error_return ? $message : false; } } } return true; } /** * Validate a single field * * @since 1.5.0 * @param string $field Field name * @param mixed $value Field value * @param int $id_lang * @return bool|string */ public function validateField($field, $value, $id_lang = null, $skip = array(), $human_errors = false) { $this->cacheFieldsRequiredDatabase(); $data = $this->def['fields'][$field]; // Check if field is required $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array(); if (!$id_lang || $id_lang == Configuration::get('PS_LANG_DEFAULT')) if (!in_array('required', $skip) && (!empty($data['required']) || in_array($field, $required_fields))) if (Tools::isEmpty($value)) if ($human_errors) return sprintf(Tools::displayError('The %s field is required.'), $this->displayFieldName($field, get_class($this))); else return 'Property '.get_class($this).'->'.$field.' is empty'; // Default value if (!$value && !empty($data['default'])) { $value = $data['default']; $this->$field = $value; } // Check field values if (!in_array('values', $skip) && !empty($data['values']) && is_array($data['values']) && !in_array($value, $data['values'])) return 'Property '.get_class($this).'->'.$field.' has bad value (allowed values are: '.implode(', ', $data['values']).')'; // Check field size if (!in_array('size', $skip) && !empty($data['size'])) { $size = $data['size']; if (!is_array($data['size'])) $size = array('min' => 0, 'max' => $data['size']); $length = Tools::strlen($value); if ($length < $size['min'] || $length > $size['max']) { if ($human_errors) { if (isset($data['lang']) && $data['lang']) { $language = new Language((int)$id_lang); return sprintf(Tools::displayError('The field %1$s (%2$s) is too long (%3$d chars max, html chars including).'), $this->displayFieldName($field, get_class($this)), $language->name, $size['max']); } else return sprintf(Tools::displayError('The %1$s field is too long (%2$d chars max).'), $this->displayFieldName($field, get_class($this)), $size['max']); } else return 'Property '.get_class($this).'->'.$field.' length ('.$length.') must be between '.$size['min'].' and '.$size['max']; } } // Check field validator if (!in_array('validate', $skip) && !empty($data['validate'])) { if (!method_exists('Validate', $data['validate'])) throw new PrestaShopException('Validation function not found. '.$data['validate']); if (!empty($value)) { $res = true; if (Tools::strtolower($data['validate']) == 'iscleanhtml') { if (!call_user_func(array('Validate', $data['validate']), $value, (int)Configuration::get('PS_ALLOW_HTML_IFRAME'))) $res = false; } else { if (!call_user_func(array('Validate', $data['validate']), $value)) $res = false; } if (!$res) { if ($human_errors) return sprintf(Tools::displayError('The %s field is invalid.'), $this->displayFieldName($field, get_class($this))); else return 'Property '.get_class($this).'->'.$field.' is not valid'; } } } return true; } public static function displayFieldName($field, $class = __CLASS__, $htmlentities = true, Context $context = null) { global $_FIELDS; if ($_FIELDS === null && file_exists(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php')) include_once(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php'); $key = $class.'_'.md5($field); return ((is_array($_FIELDS) && array_key_exists($key, $_FIELDS)) ? ($htmlentities ? htmlentities($_FIELDS[$key], ENT_QUOTES, 'utf-8') : $_FIELDS[$key]) : $field); } /** * TODO: refactor rename all calls to this to validateController * @deprecated since 1.5 use validateController instead */ public function validateControler($htmlentities = true) { Tools::displayAsDeprecated(); return $this->validateController($htmlentities); } public function validateController($htmlentities = true) { $this->cacheFieldsRequiredDatabase(); $errors = array(); $required_fields_database = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array(); foreach ($this->def['fields'] as $field => $data) { $value = Tools::getValue($field, $this->{$field}); // Check if field is required by user if (in_array($field, $required_fields_database)) $data['required'] = true; // Checking for required fields if (isset($data['required']) && $data['required'] && empty($value) && $value !== '0') if (!$this->id || $field != 'passwd') $errors[$field] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is required.'); // Checking for maximum fields sizes if (isset($data['size']) && !empty($value) && Tools::strlen($value) > $data['size']) $errors[$field] = sprintf( Tools::displayError('%1$s is too long. Maximum length: %2$d'), self::displayFieldName($field, get_class($this), $htmlentities), $data['size'] ); // Checking for fields validity // Hack for postcode required for country which does not have postcodes if (!empty($value) || $value === '0' || ($field == 'postcode' && $value == '0')) { if (isset($data['validate']) && !Validate::$data['validate']($value) && (!empty($value) || $data['required'])) $errors[$field] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is invalid.'); else { if (isset($data['copy_post']) && !$data['copy_post']) continue; if ($field == 'passwd') { if ($value = Tools::getValue($field)) $this->{$field} = Tools::encrypt($value); } else $this->{$field} = $value; } } } return $errors; } public function getWebserviceParameters($ws_params_attribute_name = null) { $this->cacheFieldsRequiredDatabase(); $default_resource_parameters = array( 'objectSqlId' => $this->def['primary'], 'retrieveData' => array( 'className' => get_class($this), 'retrieveMethod' => 'getWebserviceObjectList', 'params' => array(), 'table' => $this->def['table'], ), 'fields' => array( 'id' => array('sqlId' => $this->def['primary'], 'i18n' => false), ), ); if ($ws_params_attribute_name === null) $ws_params_attribute_name = 'webserviceParameters'; if (!isset($this->{$ws_params_attribute_name}['objectNodeName'])) $default_resource_parameters['objectNodeName'] = $this->def['table']; if (!isset($this->{$ws_params_attribute_name}['objectsNodeName'])) $default_resource_parameters['objectsNodeName'] = $this->def['table'].'s'; if (isset($this->{$ws_params_attribute_name}['associations'])) foreach ($this->{$ws_params_attribute_name}['associations'] as $assoc_name => &$association) { if (!array_key_exists('setter', $association) || (isset($association['setter']) && !$association['setter'])) $association['setter'] = Tools::toCamelCase('set_ws_'.$assoc_name); if (!array_key_exists('getter', $association)) $association['getter'] = Tools::toCamelCase('get_ws_'.$assoc_name); } if (isset($this->{$ws_params_attribute_name}['retrieveData']) && isset($this->{$ws_params_attribute_name}['retrieveData']['retrieveMethod'])) unset($default_resource_parameters['retrieveData']['retrieveMethod']); $resource_parameters = array_merge_recursive($default_resource_parameters, $this->{$ws_params_attribute_name}); $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)]) ? self::$fieldsRequiredDatabase[get_class($this)] : array()); foreach ($this->def['fields'] as $field_name => $details) { if (!isset($resource_parameters['fields'][$field_name])) $resource_parameters['fields'][$field_name] = array(); $current_field = array(); $current_field['sqlId'] = $field_name; if (isset($details['size'])) $current_field['maxSize'] = $details['size']; if (isset($details['lang'])) $current_field['i18n'] = $details['lang']; else $current_field['i18n'] = false; if ((isset($details['required']) && $details['required'] === true) || in_array($field_name, $required_fields)) $current_field['required'] = true; else $current_field['required'] = false; if (isset($details['validate'])) { $current_field['validateMethod'] = ( array_key_exists('validateMethod', $resource_parameters['fields'][$field_name]) ? array_merge($resource_parameters['fields'][$field_name]['validateMethod'], array($details['validate'])) : array($details['validate']) ); } $resource_parameters['fields'][$field_name] = array_merge($resource_parameters['fields'][$field_name], $current_field); } if (isset($this->date_add)) $resource_parameters['fields']['date_add']['setter'] = false; if (isset($this->date_upd)) $resource_parameters['fields']['date_upd']['setter'] = false; foreach ($resource_parameters['fields'] as $key => $resource_parameters_field) if (!isset($resource_parameters_field['sqlId'])) $resource_parameters['fields'][$key]['sqlId'] = $key; return $resource_parameters; } public function getWebserviceObjectList($sql_join, $sql_filter, $sql_sort, $sql_limit) { $assoc = Shop::getAssoTable($this->def['table']); $class_name = WebserviceRequest::$ws_current_classname; $vars = get_class_vars($class_name); if ($assoc !== false) { if ($assoc['type'] !== 'fk_shop') { $multi_shop_join = ' LEFT JOIN `'._DB_PREFIX_.bqSQL($this->def['table']).'_'.bqSQL($assoc['type']).'` AS `multi_shop_'.bqSQL($this->def['table']).'` ON (main.`'.bqSQL($this->def['primary']).'` = `multi_shop_'.bqSQL($this->def['table']).'`.`'.bqSQL($this->def['primary']).'`)'; $sql_filter = 'AND `multi_shop_'.bqSQL($this->def['table']).'`.id_shop = '.Context::getContext()->shop->id.' '.$sql_filter; $sql_join = $multi_shop_join.' '.$sql_join; } else { $vars = get_class_vars($class_name); foreach ($vars['shopIDs'] as $id_shop) $or[] = '(main.id_shop = '.(int)$id_shop.(isset($this->def['fields']['id_shop_group']) ? ' OR (id_shop = 0 AND id_shop_group='.(int)Shop::getGroupFromShop((int)$id_shop).')' : '').')'; $prepend = ''; if (count($or)) $prepend = 'AND ('.implode('OR', $or).')'; $sql_filter = $prepend.' '.$sql_filter; } } $query = ' SELECT DISTINCT main.`'.bqSQL($this->def['primary']).'` FROM `'._DB_PREFIX_.bqSQL($this->def['table']).'` AS main '.$sql_join.' WHERE 1 '.$sql_filter.' '.($sql_sort != '' ? $sql_sort : '').' '.($sql_limit != '' ? $sql_limit : ''); return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query); } public function validateFieldsRequiredDatabase($htmlentities = true) { $this->cacheFieldsRequiredDatabase(); $errors = array(); $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array(); foreach ($this->def['fields'] as $field => $data) { if (!in_array($field, $required_fields)) continue; if (!method_exists('Validate', $data['validate'])) throw new PrestaShopException('Validation function not found. '.$data['validate']); $value = Tools::getValue($field); if (empty($value)) $errors[$field] = sprintf(Tools::displayError('The field %s is required.'), self::displayFieldName($field, get_class($this), $htmlentities)); } return $errors; } public function getFieldsRequiredDatabase($all = false) { return Db::getInstance()->executeS(' SELECT id_required_field, object_name, field_name FROM '._DB_PREFIX_.'required_field '.(!$all ? 'WHERE object_name = \''.pSQL(get_class($this)).'\'' : '')); } public function cacheFieldsRequiredDatabase() { if (!is_array(self::$fieldsRequiredDatabase)) { $fields = $this->getfieldsRequiredDatabase(true); if ($fields) foreach ($fields as $row) self::$fieldsRequiredDatabase[$row['object_name']][(int)$row['id_required_field']] = pSQL($row['field_name']); else self::$fieldsRequiredDatabase = array(); } } public function addFieldsRequiredDatabase($fields) { if (!is_array($fields)) return false; if (!Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'required_field WHERE object_name = \''.get_class($this).'\'')) return false; foreach ($fields as $field) if (!Db::getInstance()->insert('required_field', array('object_name' => get_class($this), 'field_name' => pSQL($field)))) return false; return true; } public function clearCache($all = false) { if ($all) Cache::clean('objectmodel_'.$this->def['classname'].'_*'); elseif ($this->id) Cache::clean('objectmodel_'.$this->def['classname'].'_'.(int)$this->id.'_*'); } /** * Check if current object is associated to a shop * * @since 1.5.0 * @param int $id_shop * @return bool */ public function isAssociatedToShop($id_shop = null) { if ($id_shop === null) $id_shop = Context::getContext()->shop->id; $cache_id = 'objectmodel_shop_'.$this->def['classname'].'_'.(int)$this->id.'-'.(int)$id_shop; if (!Cache::isStored($cache_id)) { $sql = 'SELECT id_shop FROM `'.pSQL(_DB_PREFIX_.$this->def['table']).'_shop` WHERE `'.$this->def['primary'].'` = '.(int)$this->id.' AND id_shop = '.(int)$id_shop; Cache::store($cache_id, (bool)Db::getInstance()->getValue($sql)); } return Cache::retrieve($cache_id); } /** * This function associate an item to its context * * @param int|array $id_shops * @return boolean */ public function associateTo($id_shops) { if (!$this->id) return; if (!is_array($id_shops)) $id_shops = array($id_shops); $data = array(); foreach ($id_shops as $id_shop) { if (!$this->isAssociatedToShop($id_shop)) $data[] = array( $this->def['primary'] => (int)$this->id, 'id_shop' => (int)$id_shop, ); } if ($data) return Db::getInstance()->insert($this->def['table'].'_shop', $data); return true; } /** * Get the list of associated id_shop * * @since 1.5.0 * @return array */ public function getAssociatedShops() { if (!Shop::isTableAssociated($this->def['table'])) return array(); $list = array(); $sql = 'SELECT id_shop FROM `'._DB_PREFIX_.$this->def['table'].'_shop` WHERE `'.$this->def['primary'].'` = '.(int)$this->id; foreach (Db::getInstance()->executeS($sql) as $row) $list[] = $row['id_shop']; return $list; } /** * @since 1.5.0 */ public function duplicateShops($id) { if (!Shop::isTableAssociated($this->def['table'])) return false; $sql = 'SELECT id_shop FROM '._DB_PREFIX_.$this->def['table'].'_shop WHERE '.$this->def['primary'].' = '.(int)$id; if ($results = Db::getInstance()->executeS($sql)) { $ids = array(); foreach ($results as $row) $ids[] = $row['id_shop']; return $this->associateTo($ids); } return false; } /** * Check if there is more than one entries in associated shop table for current entity * * @since 1.5.0 * @return bool */ public function hasMultishopEntries() { if (!Shop::isTableAssociated($this->def['table']) || !Shop::isFeatureActive()) return false; return (bool)Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.$this->def['table'].'_shop` WHERE `'.$this->def['primary'].'` = '.(int)$this->id); } public function isMultishop() { return Shop::isTableAssociated($this->def['table']) || !empty($this->def['multilang_shop']); } public function isMultiShopField($field) { return (isset($this->def['fields'][$field]) && isset($this->def['fields'][$field]['shop']) && $this->def['fields'][$field]['shop']); } public function isLangMultishop() { return !empty($this->def['multilang']) && !empty($this->def['multilang_shop']); } /** * Update a table and splits the common datas and the shop datas * * @since 1.5.0 * @param string $classname * @param array $data * @param string $where * @param string $specific_where Only executed for common table * @return bool */ public static function updateMultishopTable($classname, $data, $where = '', $specific_where = '') { $def = ObjectModel::getDefinition($classname); $update_data = array(); foreach ($data as $field => $value) { if (!isset($def['fields'][$field])) continue; if (!empty($def['fields'][$field]['shop'])) { $update_data[] = "a.$field = '$value'"; $update_data[] = "{$def['table']}_shop.$field = '$value'"; } else $update_data[] = "a.$field = '$value'"; } $sql = 'UPDATE '._DB_PREFIX_.$def['table'].' a '.Shop::addSqlAssociation($def['table'], 'a', true, null, true).' SET '.implode(', ', $update_data). (!empty($where) ? ' WHERE '.$where : ''); return Db::getInstance()->execute($sql); } /** * Delete images associated with the object * * @return bool success */ public function deleteImage($force_delete = false) { if (!$this->id) return false; if ($force_delete || !$this->hasMultishopEntries()) { /* Deleting object images and thumbnails (cache) */ if ($this->image_dir) { if (file_exists($this->image_dir.$this->id.'.'.$this->image_format) && !unlink($this->image_dir.$this->id.'.'.$this->image_format)) return false; } if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format) && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format)) return false; if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format) && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format)) return false; $types = ImageType::getImagesTypes(); foreach ($types as $image_type) if (file_exists($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format) && !unlink($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format)) return false; } return true; } /** * Specify if an ObjectModel is already in database * * @param int $id_entity * @param string $table * @return boolean */ public static function existsInDatabase($id_entity, $table) { $row = Db::getInstance()->getRow(' SELECT `id_'.$table.'` as id FROM `'._DB_PREFIX_.$table.'` e WHERE e.`id_'.$table.'` = '.(int)$id_entity ); return isset($row['id']); } /** * This method is allow to know if a entity is currently used * @since 1.5.0.1 * @param string $table name of table linked to entity * @param bool $has_active_column true if the table has an active column * @return bool */ public static function isCurrentlyUsed($table = null, $has_active_column = false) { if ($table === null) $table = self::$definition['table']; $query = new DbQuery(); $query->select('`id_'.pSQL($table).'`'); $query->from($table); if ($has_active_column) $query->where('`active` = 1'); return (bool)Db::getInstance()->getValue($query); } /** * Fill an object with given data. Data must be an array with this syntax: array(objProperty => value, objProperty2 => value, etc.) * * @since 1.5.0 * @param array $data * @param int $id_lang */ public function hydrate(array $data, $id_lang = null) { $this->id_lang = $id_lang; if (isset($data[$this->def['primary']])) $this->id = $data[$this->def['primary']]; foreach ($data as $key => $value) if (array_key_exists($key, $this)) $this->$key = $value; } /** * Fill (hydrate) a list of objects in order to get a collection of these objects * * @since 1.5.0 * @param string $class Class of objects to hydrate * @param array $datas List of data (multi-dimensional array) * @param int $id_lang * @return array */ public static function hydrateCollection($class, array $datas, $id_lang = null) { if (!class_exists($class)) throw new PrestaShopException("Class '$class' not found"); $collection = array(); $rows = array(); if ($datas) { $definition = ObjectModel::getDefinition($class); if (!array_key_exists($definition['primary'], $datas[0])) throw new PrestaShopException("Identifier '{$definition['primary']}' not found for class '$class'"); foreach ($datas as $row) { // Get object common properties $id = $row[$definition['primary']]; if (!isset($rows[$id])) $rows[$id] = $row; // Get object lang properties if (isset($row['id_lang']) && !$id_lang) foreach ($definition['fields'] as $field => $data) if (!empty($data['lang'])) { if (!is_array($rows[$id][$field])) $rows[$id][$field] = array(); $rows[$id][$field][$row['id_lang']] = $row[$field]; } } } // Hydrate objects foreach ($rows as $row) { $obj = new $class; $obj->hydrate($row, $id_lang); $collection[] = $obj; } return $collection; } /** * Get object definition * * @param string $class Name of object * @param string $field Name of field if we want the definition of one field only * @return array */ public static function getDefinition($class, $field = null) { if (is_object($class)) $class = get_class($class); if ($field === null) $cache_id = 'objectmodel_def_'.$class; if ($field !== null || !Cache::isStored($cache_id)) { $reflection = new ReflectionClass($class); $definition = $reflection->getStaticPropertyValue('definition'); $definition['classname'] = $class; if (!empty($definition['multilang'])) $definition['associations'][Collection::LANG_ALIAS] = array( 'type' => self::HAS_MANY, 'field' => $definition['primary'], 'foreign_field' => $definition['primary'], ); if ($field) return isset($definition['fields'][$field]) ? $definition['fields'][$field] : null; Cache::store($cache_id, $definition); return $definition; } return Cache::retrieve($cache_id); } /** * Retrocompatibility for classes without $definition static * Remove this in 1.6 ! * * @since 1.5.0 */ protected function setDefinitionRetrocompatibility() { // Retrocompatibility with $table property ($definition['table']) if (isset($this->def['table'])) $this->table = $this->def['table']; else $this->def['table'] = $this->table; // Retrocompatibility with $identifier property ($definition['primary']) if (isset($this->def['primary'])) $this->identifier = $this->def['primary']; else $this->def['primary'] = $this->identifier; // Check multilang retrocompatibility if (method_exists($this, 'getTranslationsFieldsChild')) $this->def['multilang'] = true; // Retrocompatibility with $fieldsValidate, $fieldsRequired and $fieldsSize properties ($definition['fields']) if (isset($this->def['fields'])) { foreach ($this->def['fields'] as $field => $data) { $suffix = (isset($data['lang']) && $data['lang']) ? 'Lang' : ''; if (isset($data['validate'])) $this->{'fieldsValidate'.$suffix}[$field] = $data['validate']; if (isset($data['required']) && $data['required']) $this->{'fieldsRequired'.$suffix}[] = $field; if (isset($data['size'])) $this->{'fieldsSize'.$suffix}[$field] = $data['size']; } } else { $this->def['fields'] = array(); $suffixs = array('', 'Lang'); foreach($suffixs as $suffix) { foreach ($this->{'fieldsValidate'.$suffix} as $field => $validate) { $this->def['fields'][$field]['validate'] = $validate; if ($suffix == 'Lang') $this->def['fields'][$field]['lang'] = true; } foreach ($this->{'fieldsRequired'.$suffix} as $field) { $this->def['fields'][$field]['required'] = true; if ($suffix == 'Lang') $this->def['fields'][$field]['lang'] = true; } foreach ($this->{'fieldsSize'.$suffix} as $field => $size) { $this->def['fields'][$field]['size'] = $size; if ($suffix == 'Lang') $this->def['fields'][$field]['lang'] = true; } } } } /** * Return the field value for the specified language if the field is multilang, else the field value. * * @param $field_name * @param null $id_lang * @return mixed * @throws PrestaShopException * @since 1.5 */ public function getFieldByLang($field_name, $id_lang = null) { $definition = ObjectModel::getDefinition($this); // Is field in definition? if ($definition && isset($definition['fields'][$field_name])) { $field = $definition['fields'][$field_name]; // Is field multilang? if (isset($field['lang']) && $field['lang']) { if (is_array($this->{$field_name})) return $this->{$field_name}[$id_lang ? $id_lang : Context::getContext()->language->id]; } return $this->{$field_name}; } else throw new PrestaShopException('Could not load field from definition.'); } /** * Set a list of specific fields to update * array(field1 => true, field2 => false, langfield1 => array(1 => true, 2 => false)) * * @since 1.5.0 * @param array $fields */ public function setFieldsToUpdate(array $fields) { $this->update_fields = $fields; } } Is it same? Link to comment Share on other sites More sharing options...
math_php Posted March 13, 2014 Share Posted March 13, 2014 No, can you point the differences ? Product : public function validateField($field, $value, $id_lang = null) ObjectModel : public function validateField($field, $value, $id_lang = null, $skip = array(), $human_errors = false) Now you can modify product to be the same as objectModel but I think It will add trouble or show up other trouble. You should go for a neat new clean install of prestashop. Regards Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now