Advertisement
Guest User

ObjectModel.php

a guest
Nov 26th, 2013
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 53.00 KB | None | 0 0
  1. <?php
  2. /*
  3. * 2007-2013 PrestaShop
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@prestashop.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18. * versions in the future. If you wish to customize PrestaShop for your
  19. * needs please refer to http://www.prestashop.com for more information.
  20. *
  21. *  @author PrestaShop SA <contact@prestashop.com>
  22. *  @copyright  2007-2013 PrestaShop SA
  23. *  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  24. *  International Registered Trademark & Property of PrestaShop SA
  25. */
  26.  
  27. abstract class ObjectModelCore
  28. {
  29.     /**
  30.      * List of field types
  31.      */
  32.     const TYPE_INT = 1;
  33.     const TYPE_BOOL = 2;
  34.     const TYPE_STRING = 3;
  35.     const TYPE_FLOAT = 4;
  36.     const TYPE_DATE = 5;
  37.     const TYPE_HTML = 6;
  38.     const TYPE_NOTHING = 7;
  39.  
  40.     /**
  41.      * List of data to format
  42.      */
  43.     const FORMAT_COMMON = 1;
  44.     const FORMAT_LANG = 2;
  45.     const FORMAT_SHOP = 3;
  46.  
  47.     /**
  48.      * List of association types
  49.      */
  50.     const HAS_ONE = 1;
  51.     const HAS_MANY = 2;
  52.  
  53.     /** @var integer Object id */
  54.     public $id;
  55.  
  56.     /** @var integer lang id */
  57.     protected $id_lang = null;
  58.  
  59.     protected $id_shop = null;
  60.  
  61.     public $id_shop_list = null;
  62.  
  63.     protected $get_shop_from_context = true;
  64.  
  65.     protected static $fieldsRequiredDatabase = null;
  66.  
  67.     /**
  68.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['table'] property instead
  69.      */
  70.     protected $table;
  71.  
  72.     /**
  73.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['primary'] property instead
  74.      */
  75.     protected $identifier;
  76.  
  77.     /**
  78.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  79.      */
  80.     protected $fieldsRequired = array();
  81.  
  82.     /**
  83.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  84.      */
  85.     protected $fieldsSize = array();
  86.  
  87.     /**
  88.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  89.      */
  90.     protected $fieldsValidate = array();
  91.  
  92.     /**
  93.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  94.      */
  95.     protected $fieldsRequiredLang = array();
  96.  
  97.     /**
  98.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  99.      */
  100.     protected $fieldsSizeLang = array();
  101.  
  102.     /**
  103.      * @deprecated 1.5.0 This property shouldn't be overloaded anymore in class, use static $definition['fields'] property instead
  104.      */
  105.     protected $fieldsValidateLang = array();
  106.  
  107.     /**
  108.      * @deprecated 1.5.0
  109.      */
  110.     protected $tables = array();
  111.  
  112.     /** @var array tables */
  113.     protected $webserviceParameters = array();
  114.  
  115.     /** @var  string path to image directory. Used for image deletion. */
  116.     protected $image_dir = null;
  117.  
  118.     /** @var string file type of image files. Used for image deletion. */
  119.     protected $image_format = 'jpg';
  120.  
  121.     /**
  122.      * @var array Contain object definition
  123.      * @since 1.5.0
  124.      */
  125.     public static $definition = array();
  126.  
  127.     /**
  128.      * @var array Contain current object definition
  129.      */
  130.     protected $def;
  131.  
  132.     /**
  133.      * @var array List of specific fields to update (all fields if null)
  134.      */
  135.     protected $update_fields = null;
  136.    
  137.     /**
  138.      * @var Db An instance of the db in order to avoid calling Db::getInstance() thousands of time
  139.      */
  140.     protected static $db = false;
  141.  
  142.     /**
  143.      * Returns object validation rules (fields validity)
  144.      *
  145.      * @param string $class Child class name for static use (optional)
  146.      * @return array Validation rules (fields validity)
  147.      */
  148.     public static function getValidationRules($class = __CLASS__)
  149.     {
  150.         $object = new $class();
  151.         return array(
  152.             'required' => $object->fieldsRequired,
  153.             'size' => $object->fieldsSize,
  154.             'validate' => $object->fieldsValidate,
  155.             'requiredLang' => $object->fieldsRequiredLang,
  156.             'sizeLang' => $object->fieldsSizeLang,
  157.             'validateLang' => $object->fieldsValidateLang,
  158.         );
  159.     }
  160.  
  161.     /**
  162.      * Build object
  163.      *
  164.      * @param int $id Existing object id in order to load object (optional)
  165.      * @param int $id_lang Required if object is multilingual (optional)
  166.      * @param int $id_shop ID shop for objects with multishop on langs
  167.      */
  168.     public function __construct($id = null, $id_lang = null, $id_shop = null)
  169.     {
  170.         if (!ObjectModel::$db)
  171.             ObjectModel::$db = Db::getInstance();
  172.  
  173.         $this->def = ObjectModel::getDefinition($this);
  174.         $this->setDefinitionRetrocompatibility();
  175.  
  176.         if ($id_lang !== null)
  177.             $this->id_lang = (Language::getLanguage($id_lang) !== false) ? $id_lang : Configuration::get('PS_LANG_DEFAULT');
  178.  
  179.         if ($id_shop && $this->isMultishop())
  180.         {
  181.             $this->id_shop = (int)$id_shop;
  182.             $this->get_shop_from_context = false;
  183.         }
  184.  
  185.         if ($this->isMultishop() && !$this->id_shop)
  186.             $this->id_shop = Context::getContext()->shop->id;
  187.  
  188.         if (!Validate::isTableOrIdentifier($this->def['primary']) || !Validate::isTableOrIdentifier($this->def['table']))
  189.             throw new PrestaShopException('Identifier or table format not valid for class '.get_class($this));
  190.  
  191.         if ($id)
  192.         {
  193.             // Load object from database if object id is present
  194.             $cache_id = 'objectmodel_'.$this->def['classname'].'_'.(int)$id.'_'.(int)$this->id_shop.'_'.(int)$id_lang;
  195.             if (!Cache::isStored($cache_id))
  196.             {
  197.                 $sql = new DbQuery();
  198.                 $sql->from($this->def['table'], 'a');
  199.                 $sql->where('a.'.$this->def['primary'].' = '.(int)$id);
  200.  
  201.                 // Get lang informations
  202.                 if ($id_lang)
  203.                 {
  204.                     $sql->leftJoin($this->def['table'].'_lang', 'b', 'a.'.$this->def['primary'].' = b.'.$this->def['primary'].' AND b.id_lang = '.(int)$id_lang);
  205.                     if ($this->id_shop && !empty($this->def['multilang_shop']))
  206.                         $sql->where('b.id_shop = '.$this->id_shop);
  207.                 }
  208.  
  209.                 // Get shop informations
  210.                 if (Shop::isTableAssociated($this->def['table']))
  211.                     $sql->leftJoin($this->def['table'].'_shop', 'c', 'a.'.$this->def['primary'].' = c.'.$this->def['primary'].' AND c.id_shop = '.(int)$this->id_shop);
  212.                 if ($object_datas = ObjectModel::$db->getRow($sql))
  213.                 {
  214.                     if (!$id_lang && isset($this->def['multilang']) && $this->def['multilang'])
  215.                     {
  216.                         $sql = 'SELECT * FROM `'.pSQL(_DB_PREFIX_.$this->def['table']).'_lang`
  217.                                 WHERE `'.$this->def['primary'].'` = '.(int)$id
  218.                                 .(($this->id_shop && $this->isLangMultishop()) ? ' AND `id_shop` = '.$this->id_shop : '');
  219.                         if ($object_datas_lang = ObjectModel::$db->executeS($sql))
  220.                             foreach ($object_datas_lang as $row)
  221.                                 foreach ($row as $key => $value)
  222.                                 {
  223.                                     if (array_key_exists($key, $this) && $key != $this->def['primary'])
  224.                                     {
  225.                                         if (!isset($object_datas[$key]) || !is_array($object_datas[$key]))
  226.                                             $object_datas[$key] = array();
  227.                                         $object_datas[$key][$row['id_lang']] = $value;
  228.                                     }
  229.                                 }
  230.                     }
  231.                     Cache::store($cache_id, $object_datas);
  232.                 }
  233.             }
  234.             else
  235.                 $object_datas = Cache::retrieve($cache_id);
  236.  
  237.             if ($object_datas)
  238.             {
  239.                 $this->id = (int)$id;
  240.                 foreach ($object_datas as $key => $value)
  241.                     if (array_key_exists($key, $this))
  242.                         $this->{$key} = $value;
  243.             }
  244.         }
  245.     }
  246.  
  247.     /**
  248.      * Prepare fields for ObjectModel class (add, update)
  249.      * All fields are verified (pSQL, intval...)
  250.      *
  251.      * @return array All object fields
  252.      */
  253.     public function getFields()
  254.     {
  255.         $this->validateFields();
  256.         $fields = $this->formatFields(self::FORMAT_COMMON);
  257.  
  258.         // For retro compatibility
  259.         if (Shop::isTableAssociated($this->def['table']))
  260.             $fields = array_merge($fields, $this->getFieldsShop());
  261.  
  262.         // Ensure that we get something to insert
  263.         if (!$fields && isset($this->id) && Validate::isUnsignedId($this->id))
  264.             $fields[$this->def['primary']] = $this->id;
  265.         return $fields;
  266.     }
  267.  
  268.     /**
  269.      * Prepare fields for multishop
  270.      * Fields are not validated here, we considere they are already validated in getFields() method, this
  271.      * not the best solution but this is the only one possible for retro compatibility.
  272.      *
  273.      * @since 1.5.0
  274.      * @return array All object fields
  275.      */
  276.     public function getFieldsShop()
  277.     {
  278.         $fields = $this->formatFields(self::FORMAT_SHOP);
  279.         if (!$fields && isset($this->id) && Validate::isUnsignedId($this->id))
  280.             $fields[$this->def['primary']] = $this->id;
  281.         return $fields;
  282.     }
  283.  
  284.     /**
  285.      * Prepare multilang fields
  286.      *
  287.      * @since 1.5.0
  288.      * @return array
  289.      */
  290.     public function getFieldsLang()
  291.     {
  292.         // Retrocompatibility
  293.         if (method_exists($this, 'getTranslationsFieldsChild'))
  294.             return $this->getTranslationsFieldsChild();
  295.  
  296.         $this->validateFieldsLang();
  297.         $is_lang_multishop = $this->isLangMultishop();
  298.  
  299.         $fields = array();
  300.         if ($this->id_lang === null)
  301.             foreach (Language::getLanguages(false) as $language)
  302.             {
  303.                 $fields[$language['id_lang']] = $this->formatFields(self::FORMAT_LANG, $language['id_lang']);
  304.                 $fields[$language['id_lang']]['id_lang'] = $language['id_lang'];
  305.                 if ($this->id_shop && $is_lang_multishop)
  306.                     $fields[$language['id_lang']]['id_shop'] = (int)$this->id_shop;
  307.             }
  308.         else
  309.         {
  310.             $fields = array($this->id_lang => $this->formatFields(self::FORMAT_LANG, $this->id_lang));
  311.             $fields[$this->id_lang]['id_lang'] = $this->id_lang;
  312.             if ($this->id_shop && $is_lang_multishop)
  313.                 $fields[$this->id_lang]['id_shop'] = (int)$this->id_shop;
  314.         }
  315.  
  316.         return $fields;
  317.     }
  318.  
  319.     /**
  320.      * @since 1.5.0
  321.      * @param int $type FORMAT_COMMON or FORMAT_LANG or FORMAT_SHOP
  322.      * @param int $id_lang If this parameter is given, only take lang fields
  323.      * @return array
  324.      */
  325.     protected function formatFields($type, $id_lang = null)
  326.     {
  327.         $fields = array();
  328.  
  329.         // Set primary key in fields
  330.         if (isset($this->id))
  331.             $fields[$this->def['primary']] = $this->id;
  332.  
  333.         foreach ($this->def['fields'] as $field => $data)
  334.         {
  335.             // Only get fields we need for the type
  336.             // E.g. if only lang fields are filtered, ignore fields without lang => true
  337.             if (($type == self::FORMAT_LANG && empty($data['lang']))
  338.                 || ($type == self::FORMAT_SHOP && empty($data['shop']))
  339.                 || ($type == self::FORMAT_COMMON && (!empty($data['shop']) || !empty($data['lang']))))
  340.                 continue;
  341.  
  342.             if (is_array($this->update_fields))
  343.                 if ((!empty($data['lang']) || !empty($data['shop'])) && (empty($this->update_fields[$field]) || ($type == self::FORMAT_LANG && empty($this->update_fields[$field][$id_lang]))))
  344.                     continue;
  345.  
  346.             // Get field value, if value is multilang and field is empty, use value from default lang
  347.             $value = $this->$field;
  348.             if ($type == self::FORMAT_LANG && $id_lang && is_array($value))
  349.             {
  350.                 if (!empty($value[$id_lang]))
  351.                     $value = $value[$id_lang];
  352.                 else if (!empty($data['required']))
  353.                     $value = $value[Configuration::get('PS_LANG_DEFAULT')];
  354.                 else
  355.                     $value = '';
  356.             }
  357.  
  358.             // Format field value
  359.             $fields[$field] = ObjectModel::formatValue($value, $data['type']);
  360.         }
  361.  
  362.         return $fields;
  363.     }
  364.  
  365.     /**
  366.      * Format a data
  367.      *
  368.      * @param mixed $value
  369.      * @param int $type
  370.      */
  371.     public static function formatValue($value, $type, $with_quotes = false)
  372.     {
  373.         switch ($type)
  374.         {
  375.             case self::TYPE_INT :
  376.                 return (int)$value;
  377.  
  378.             case self::TYPE_BOOL :
  379.                 return (int)$value;
  380.  
  381.             case self::TYPE_FLOAT :
  382.                 return (float)str_replace(',', '.', $value);
  383.  
  384.             case self::TYPE_DATE :
  385.                 if (!$value)
  386.                     return '0000-00-00';
  387.  
  388.                 if ($with_quotes)
  389.                     return '\''.pSQL($value).'\'';
  390.                 return pSQL($value);
  391.  
  392.             case self::TYPE_HTML :
  393.                 if ($with_quotes)
  394.                     return '\''.pSQL($value, true).'\'';
  395.                 return pSQL($value, true);
  396.  
  397.             case self::TYPE_NOTHING :
  398.                 return $value;
  399.  
  400.             case self::TYPE_STRING :
  401.             default :
  402.                 if ($with_quotes)
  403.                     return '\''.pSQL($value).'\'';
  404.                 return pSQL($value);
  405.         }
  406.     }
  407.  
  408.     /**
  409.      * Save current object to database (add or update)
  410.      *
  411.      * @param bool $null_values
  412.      * @param bool $autodate
  413.      * @return boolean Insertion result
  414.      */
  415.     public function save($null_values = false, $autodate = true)
  416.     {
  417.         return (int)$this->id > 0 ? $this->update($null_values) : $this->add($autodate, $null_values);
  418.     }
  419.  
  420.     /**
  421.      * Add current object to database
  422.      *
  423.      * @param bool $null_values
  424.      * @param bool $autodate
  425.      * @return boolean Insertion result
  426.      */
  427.     public function add($autodate = true, $null_values = false)
  428.     {
  429.         if (!ObjectModel::$db)
  430.             ObjectModel::$db = Db::getInstance();
  431.  
  432.         // @hook actionObject*AddBefore
  433.         Hook::exec('actionObjectAddBefore', array('object' => $this));
  434.         Hook::exec('actionObject'.get_class($this).'AddBefore', array('object' => $this));
  435.  
  436.         // Automatically fill dates
  437.         if ($autodate && property_exists($this, 'date_add'))
  438.             $this->date_add = date('Y-m-d H:i:s');
  439.         if ($autodate && property_exists($this, 'date_upd'))
  440.             $this->date_upd = date('Y-m-d H:i:s');
  441.  
  442.            
  443.         if (Shop::isTableAssociated($this->def['table']))
  444.         {
  445.             $id_shop_list = Shop::getContextListShopID();
  446.             if (count($this->id_shop_list) > 0)
  447.                 $id_shop_list = $this->id_shop_list;
  448.         }
  449.        
  450.         // Database insertion
  451.         if (isset($this->id) && !Tools::getValue('forceIDs'))
  452.             unset($this->id);
  453.         if (Shop::checkIdShopDefault($this->def['table']))
  454.             $this->id_shop_default = min($id_shop_list);
  455.         if (!$result = ObjectModel::$db->insert($this->def['table'], $this->getFields(), $null_values))
  456.             return false;
  457.  
  458.         // Get object id in database
  459.         $this->id = ObjectModel::$db->Insert_ID();
  460.  
  461.         // Database insertion for multishop fields related to the object
  462.         if (Shop::isTableAssociated($this->def['table']))
  463.         {
  464.             $fields = $this->getFieldsShop();
  465.             $fields[$this->def['primary']] = (int)$this->id;
  466.  
  467.             foreach ($id_shop_list as $id_shop)
  468.             {
  469.                 $fields['id_shop'] = (int)$id_shop;
  470.                 $result &= ObjectModel::$db->insert($this->def['table'].'_shop', $fields, $null_values);
  471.             }
  472.         }
  473.  
  474.         if (!$result)
  475.             return false;
  476.  
  477.         // Database insertion for multilingual fields related to the object
  478.         if (!empty($this->def['multilang']))
  479.         {
  480.             $fields = $this->getFieldsLang();
  481.             if ($fields && is_array($fields))
  482.             {
  483.                 $shops = Shop::getCompleteListOfShopsID();
  484.                 $asso = Shop::getAssoTable($this->def['table'].'_lang');
  485.                 foreach ($fields as $field)
  486.                 {
  487.                     foreach (array_keys($field) as $key)
  488.                         if (!Validate::isTableOrIdentifier($key))
  489.                             throw new PrestaShopException('key '.$key.' is not table or identifier, ');
  490.                     $field[$this->def['primary']] = (int)$this->id;
  491.  
  492.                     if ($asso !== false && $asso['type'] == 'fk_shop')
  493.                     {
  494.                         foreach ($shops as $id_shop)
  495.                         {
  496.                             $field['id_shop'] = (int)$id_shop;
  497.                             $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field);
  498.                         }
  499.                     }
  500.                     else
  501.                         $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field);
  502.                 }
  503.             }
  504.         }
  505.  
  506.         // @hook actionObject*AddAfter
  507.         Hook::exec('actionObjectAddAfter', array('object' => $this));
  508.         Hook::exec('actionObject'.get_class($this).'AddAfter', array('object' => $this));
  509.  
  510.         return $result;
  511.     }
  512.    
  513.     /**
  514.      * Duplicate current object to database
  515.      *
  516.      * @return new object
  517.      */
  518.     public function duplicateObject()
  519.     {
  520.         $definition = ObjectModel::getDefinition($this);
  521.  
  522.         $res = Db::getInstance()->getRow('
  523.                     SELECT *
  524.                     FROM `'._DB_PREFIX_.bqSQL($definition['table']).'`
  525.                     WHERE `'.bqSQL($definition['primary']).'` = '.(int)$this->id
  526.                 );
  527.         if (!$res)
  528.             return false;
  529.         unset($res[$definition['primary']]);
  530.         foreach ($res as $field => &$value)
  531.             if (isset($definition['fields'][$field]))
  532.                 $value = ObjectModel::formatValue($value, $definition['fields'][$field]['type']);
  533.  
  534.         if (!Db::getInstance()->insert($definition['table'], $res))
  535.             return false;
  536.        
  537.         $object_id = Db::getInstance()->Insert_ID();
  538.  
  539.         if (isset($definition['multilang']) && $definition['multilang'])
  540.         {
  541.             $result = Db::getInstance()->executeS('
  542.             SELECT *
  543.             FROM `'._DB_PREFIX_.bqSQL($definition['table']).'_lang`
  544.             WHERE `'.bqSQL($definition['primary']).'` = '.(int)$this->id);
  545.             if (!$result)
  546.                 return false;
  547.    
  548.             foreach ($result as &$row)
  549.                 foreach ($row as $field => &$value)
  550.                     if (isset($definition['fields'][$field]))
  551.                         $value = ObjectModel::formatValue($value, $definition['fields'][$field]['type']);
  552.            
  553.             // Keep $row2, you cannot use $row because there is an unexplicated conflict with the previous usage of this variable
  554.             foreach ($result as $row2)
  555.             {
  556.                 $row2[$definition['primary']] = (int)$object_id;
  557.                 if (!Db::getInstance()->insert($definition['table'].'_lang', $row2))
  558.                     return false;
  559.             }
  560.         }
  561.    
  562.         $object_duplicated = new $definition['classname']((int)$object_id);
  563.         $object_duplicated->duplicateShops((int)$this->id);
  564.        
  565.         return $object_duplicated;
  566.     }
  567.  
  568.     /**
  569.      * Update current object to database
  570.      *
  571.      * @param bool $null_values
  572.      * @return boolean Update result
  573.      */
  574.     public function update($null_values = false)
  575.     {
  576.         if (!ObjectModel::$db)
  577.             ObjectModel::$db = Db::getInstance();
  578.  
  579.         // @hook actionObject*UpdateBefore
  580.         Hook::exec('actionObjectUpdateBefore', array('object' => $this));
  581.         Hook::exec('actionObject'.get_class($this).'UpdateBefore', array('object' => $this));
  582.  
  583.         $this->clearCache();
  584.  
  585.         // Automatically fill dates
  586.         if (array_key_exists('date_upd', $this))
  587.             $this->date_upd = date('Y-m-d H:i:s');
  588.            
  589.         $id_shop_list = Shop::getContextListShopID();
  590.         if (count($this->id_shop_list) > 0)
  591.             $id_shop_list = $this->id_shop_list;
  592.  
  593.         if (Shop::checkIdShopDefault($this->def['table']) && !$this->id_shop_default)
  594.             $this->id_shop_default = min($id_shop_list);
  595.         // Database update
  596.         if (!$result = ObjectModel::$db->update($this->def['table'], $this->getFields(), '`'.pSQL($this->def['primary']).'` = '.(int)$this->id, 0, $null_values))
  597.             return false;
  598.  
  599.         // Database insertion for multishop fields related to the object
  600.         if (Shop::isTableAssociated($this->def['table']))
  601.         {
  602.             $fields = $this->getFieldsShop();
  603.             $fields[$this->def['primary']] = (int)$this->id;
  604.             if (is_array($this->update_fields))
  605.             {
  606.                 $update_fields = $this->update_fields;
  607.                 $this->update_fields = null;
  608.                 $all_fields = $this->getFieldsShop();
  609.                 $all_fields[$this->def['primary']] = (int)$this->id;
  610.                 $this->update_fields = $update_fields;
  611.             }
  612.             else
  613.                 $all_fields = $fields;
  614.  
  615.             foreach ($id_shop_list as $id_shop)
  616.             {
  617.                 $fields['id_shop'] = (int)$id_shop;
  618.                 $all_fields['id_shop'] = (int)$id_shop;
  619.                 $where = $this->def['primary'].' = '.(int)$this->id.' AND id_shop = '.(int)$id_shop;
  620.  
  621.                 // A little explanation of what we do here : we want to create multishop entry when update is called, but
  622.                 // only if we are in a shop context (if we are in all context, we just want to update entries that alread exists)
  623.                 $shop_exists = ObjectModel::$db->getValue('SELECT '.$this->def['primary'].' FROM '._DB_PREFIX_.$this->def['table'].'_shop WHERE '.$where);
  624.                 if ($shop_exists)
  625.                     $result &= ObjectModel::$db->update($this->def['table'].'_shop', $fields, $where, 0, $null_values);
  626.                 elseif (Shop::getContext() == Shop::CONTEXT_SHOP)
  627.                     $result &= ObjectModel::$db->insert($this->def['table'].'_shop', $all_fields, $null_values);
  628.             }
  629.         }
  630.  
  631.         // Database update for multilingual fields related to the object
  632.         if (isset($this->def['multilang']) && $this->def['multilang'])
  633.         {
  634.             $fields = $this->getFieldsLang();
  635.             if (is_array($fields))
  636.             {
  637.                 foreach ($fields as $field)
  638.                 {
  639.                     foreach (array_keys($field) as $key)
  640.                         if (!Validate::isTableOrIdentifier($key))
  641.                             throw new PrestaShopException('key '.$key.' is not a valid table or identifier');
  642.  
  643.                     // If this table is linked to multishop system, update / insert for all shops from context
  644.                     if ($this->isLangMultishop())
  645.                     {
  646.                         $id_shop_list = Shop::getContextListShopID();
  647.                         if (count($this->id_shop_list) > 0)
  648.                             $id_shop_list = $this->id_shop_list;
  649.                         foreach ($id_shop_list as $id_shop)
  650.                         {
  651.                             $field['id_shop'] = (int)$id_shop;
  652.                             $where = pSQL($this->def['primary']).' = '.(int)$this->id
  653.                                         .' AND id_lang = '.(int)$field['id_lang']
  654.                                         .' AND id_shop = '.(int)$id_shop;
  655.  
  656.                             if (ObjectModel::$db->getValue('SELECT COUNT(*) FROM '.pSQL(_DB_PREFIX_.$this->def['table']).'_lang WHERE '.$where))
  657.                                 $result &= ObjectModel::$db->update($this->def['table'].'_lang', $field, $where);
  658.                             else
  659.                                 $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field);
  660.                         }
  661.                     }
  662.                     // If this table is not linked to multishop system ...
  663.                     else
  664.                     {
  665.                         $where = pSQL($this->def['primary']).' = '.(int)$this->id
  666.                                     .' AND id_lang = '.(int)$field['id_lang'];
  667.                         if (Db::getInstance()->getValue('SELECT COUNT(*) FROM '.pSQL(_DB_PREFIX_.$this->def['table']).'_lang WHERE '.$where))
  668.                             $result &= ObjectModel::$db->update($this->def['table'].'_lang', $field, $where);
  669.                         else
  670.                             $result &= ObjectModel::$db->insert($this->def['table'].'_lang', $field, $null_values);
  671.                     }
  672.                 }
  673.             }
  674.         }
  675.  
  676.         // @hook actionObject*UpdateAfter
  677.         Hook::exec('actionObjectUpdateAfter', array('object' => $this));
  678.         Hook::exec('actionObject'.get_class($this).'UpdateAfter', array('object' => $this));
  679.  
  680.         return $result;
  681.     }
  682.  
  683.     /**
  684.      * Delete current object from database
  685.      *
  686.      * @return boolean Deletion result
  687.      */
  688.     public function delete()
  689.     {
  690.         if (!ObjectModel::$db)
  691.             ObjectModel::$db = Db::getInstance();
  692.  
  693.         // @hook actionObject*DeleteBefore
  694.         Hook::exec('actionObjectDeleteBefore', array('object' => $this));
  695.         Hook::exec('actionObject'.get_class($this).'DeleteBefore', array('object' => $this));
  696.  
  697.         $this->clearCache();
  698.         $result = true;
  699.         // Remove association to multishop table
  700.         if (Shop::isTableAssociated($this->def['table']))
  701.         {
  702.             $id_shop_list = Shop::getContextListShopID();
  703.             if (count($this->id_shop_list))
  704.                 $id_shop_list = $this->id_shop_list;
  705.  
  706.             $result &= ObjectModel::$db->delete($this->def['table'].'_shop', '`'.$this->def['primary'].'`='.(int)$this->id.' AND id_shop IN ('.implode(', ', $id_shop_list).')');
  707.         }
  708.  
  709.         // Database deletion
  710.         $has_multishop_entries = $this->hasMultishopEntries();
  711.         if ($result && !$has_multishop_entries)
  712.             $result &= ObjectModel::$db->delete($this->def['table'], '`'.pSQL($this->def['primary']).'` = '.(int)$this->id);
  713.  
  714.         if (!$result)
  715.             return false;
  716.  
  717.         // Database deletion for multilingual fields related to the object
  718.         if (!empty($this->def['multilang']) && !$has_multishop_entries)
  719.             $result &= ObjectModel::$db->delete($this->def['table'].'_lang', '`'.pSQL($this->def['primary']).'` = '.(int)$this->id);
  720.  
  721.         // @hook actionObject*DeleteAfter
  722.         Hook::exec('actionObjectDeleteAfter', array('object' => $this));
  723.         Hook::exec('actionObject'.get_class($this).'DeleteAfter', array('object' => $this));
  724.  
  725.         return $result;
  726.     }
  727.  
  728.     /**
  729.      * Delete several objects from database
  730.      *
  731.      * @param array $selection
  732.      * @return bool Deletion result
  733.      */
  734.     public function deleteSelection($selection)
  735.     {
  736.         $result = true;
  737.         foreach ($selection as $id)
  738.         {
  739.             $this->id = (int)$id;
  740.             $result = $result && $this->delete();
  741.         }
  742.         return $result;
  743.     }
  744.  
  745.     /**
  746.      * Toggle object status in database
  747.      *
  748.      * @return boolean Update result
  749.      */
  750.     public function toggleStatus()
  751.     {
  752.         // Object must have a variable called 'active'
  753.         if (!array_key_exists('active', $this))
  754.             throw new PrestaShopException('property "active" is missing in object '.get_class($this));
  755.  
  756.         // Update only active field
  757.         $this->setFieldsToUpdate(array('active' => true));
  758.  
  759.         // Update active status on object
  760.         $this->active = !(int)$this->active;
  761.  
  762.         // Change status to active/inactive
  763.         return $this->update(false);
  764.     }
  765.  
  766.     /**
  767.      * @deprecated 1.5.0 (use getFieldsLang())
  768.      */
  769.     protected function getTranslationsFields($fields_array)
  770.     {
  771.         $fields = array();
  772.  
  773.         if ($this->id_lang == null)
  774.             foreach (Language::getLanguages(false) as $language)
  775.                 $this->makeTranslationFields($fields, $fields_array, $language['id_lang']);
  776.         else
  777.             $this->makeTranslationFields($fields, $fields_array, $this->id_lang);
  778.  
  779.         return $fields;
  780.     }
  781.  
  782.     /**
  783.      * @deprecated 1.5.0
  784.      */
  785.     protected function makeTranslationFields(&$fields, &$fields_array, $id_language)
  786.     {
  787.         $fields[$id_language]['id_lang'] = $id_language;
  788.         $fields[$id_language][$this->def['primary']] = (int)$this->id;
  789.         if ($this->id_shop && $this->isLangMultishop())
  790.             $fields[$id_language]['id_shop'] = (int)$this->id_shop;
  791.         foreach ($fields_array as $k => $field)
  792.         {
  793.             $html = false;
  794.             $field_name = $field;
  795.             if (is_array($field))
  796.             {
  797.                 $field_name = $k;
  798.                 $html = (isset($field['html'])) ? $field['html'] : false;
  799.             }
  800.  
  801.             /* Check fields validity */
  802.             if (!Validate::isTableOrIdentifier($field_name))
  803.                 throw new PrestaShopException('identifier is not table or identifier : '.$field_name);
  804.  
  805.             // Copy the field, or the default language field if it's both required and empty
  806.             if ((!$this->id_lang && isset($this->{$field_name}[$id_language]) && !empty($this->{$field_name}[$id_language]))
  807.             || ($this->id_lang && isset($this->$field_name) && !empty($this->$field_name)))
  808.                 $fields[$id_language][$field_name] = $this->id_lang ? pSQL($this->$field_name, $html) : pSQL($this->{$field_name}[$id_language], $html);
  809.             else if (in_array($field_name, $this->fieldsRequiredLang))
  810.                 $fields[$id_language][$field_name] = pSQL($this->id_lang ? $this->$field_name : $this->{$field_name}[Configuration::get('PS_LANG_DEFAULT')], $html);
  811.             else
  812.                 $fields[$id_language][$field_name] = '';
  813.         }
  814.     }
  815.  
  816.     /**
  817.      * Check for fields validity before database interaction
  818.      *
  819.      * @param bool $die
  820.      * @param bool $error_return
  821.      * @return bool|string
  822.      */
  823.     public function validateFields($die = true, $error_return = false)
  824.     {
  825.         foreach ($this->def['fields'] as $field => $data)
  826.         {
  827.             if (!empty($data['lang']))
  828.                 continue;
  829.  
  830.             if (is_array($this->update_fields) && empty($this->update_fields[$field]))
  831.                 continue;
  832.  
  833.             $message = $this->validateField($field, $this->$field);
  834.             if ($message !== true)
  835.             {
  836.                 if ($die)
  837.                     throw new PrestaShopException($message);
  838.                 return $error_return ? $message : false;
  839.             }
  840.         }
  841.  
  842.         return true;
  843.     }
  844.  
  845.     /**
  846.      * Check for multilingual fields validity before database interaction
  847.      *
  848.      * @param bool $die
  849.      * @param bool $error_return
  850.      * @return bool|string
  851.      */
  852.     public function validateFieldsLang($die = true, $error_return = false)
  853.     {
  854.         foreach ($this->def['fields'] as $field => $data)
  855.         {
  856.             if (empty($data['lang']))
  857.                 continue;
  858.  
  859.             $values = $this->$field;
  860.            
  861.             // If the object has not been loaded in multilanguage, then the value is the one for the current language of the object
  862.             if (!is_array($values))
  863.                 $values = array($this->id_lang => $values);
  864.  
  865.             // The value for the default must always be set, so we put an empty string if it does not exists
  866.             if (!isset($values[Configuration::get('PS_LANG_DEFAULT')]))
  867.                 $values[Configuration::get('PS_LANG_DEFAULT')] = '';
  868.  
  869.             foreach ($values as $id_lang => $value)
  870.             {
  871.                 if (is_array($this->update_fields) && empty($this->update_fields[$field][$id_lang]))
  872.                     continue;
  873.  
  874.                 $message = $this->validateField($field, $value, $id_lang);
  875.                 if ($message !== true)
  876.                 {
  877.                     if ($die)
  878.                         throw new PrestaShopException($message);
  879.                     return $error_return ? $message : false;
  880.                 }
  881.             }
  882.         }
  883.  
  884.         return true;
  885.     }
  886.  
  887.     /**
  888.      * Validate a single field
  889.      *
  890.      * @since 1.5.0
  891.      * @param string $field Field name
  892.      * @param mixed $value Field value
  893.      * @param int $id_lang
  894.      * @return bool|string
  895.      */
  896.     public function validateField($field, $value, $id_lang = null, $skip = array(), $human_errors = false)
  897.     {
  898.         $this->cacheFieldsRequiredDatabase();
  899.         $data = $this->def['fields'][$field];
  900.  
  901.         // Check if field is required
  902.         $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();
  903.         if (!$id_lang || $id_lang == Configuration::get('PS_LANG_DEFAULT'))
  904.             if (!in_array('required', $skip) && (!empty($data['required']) || in_array($field, $required_fields)))
  905.                 if (Tools::isEmpty($value))
  906.                     if ($human_errors)
  907.                         return sprintf(Tools::displayError('The %s field is required.'), $this->displayFieldName($field, get_class($this)));
  908.                     else
  909.                         return 'Property '.get_class($this).'->'.$field.' is empty';
  910.  
  911.         // Default value
  912.         if (!$value && !empty($data['default']))
  913.         {
  914.             $value = $data['default'];
  915.             $this->$field = $value;
  916.         }
  917.  
  918.         // Check field values
  919.         if (!in_array('values', $skip) && !empty($data['values']) && is_array($data['values']) && !in_array($value, $data['values']))
  920.                 return 'Property '.get_class($this).'->'.$field.' has bad value (allowed values are: '.implode(', ', $data['values']).')';
  921.  
  922.         // Check field size
  923.         if (!in_array('size', $skip) && !empty($data['size']))
  924.         {
  925.             $size = $data['size'];
  926.             if (!is_array($data['size']))
  927.                 $size = array('min' => 0, 'max' => $data['size']);
  928.  
  929.             $length = Tools::strlen($value);
  930.             if ($length < $size['min'] || $length > $size['max'])
  931.             {
  932.                 if ($human_errors)
  933.                 {
  934.                     if (isset($data['lang']) && $data['lang'])
  935.                     {
  936.                         $language = new Language((int)$id_lang);
  937.                         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']);
  938.                     }
  939.                     else
  940.                         return sprintf(Tools::displayError('The %1$s field is too long (%2$d chars max).'), $this->displayFieldName($field, get_class($this)), $size['max']);
  941.                 }
  942.                 else
  943.                     return 'Property '.get_class($this).'->'.$field.' length ('.$length.') must be between '.$size['min'].' and '.$size['max'];
  944.             }
  945.         }
  946.  
  947.         // Check field validator
  948.         if (!in_array('validate', $skip) && !empty($data['validate']))
  949.         {
  950.             if (!method_exists('Validate', $data['validate']))
  951.                 throw new PrestaShopException('Validation function not found. '.$data['validate']);
  952.  
  953.             if (!empty($value))
  954.             {
  955.                 $res = true;
  956.                 if (Tools::strtolower($data['validate']) == 'iscleanhtml')
  957.                 {
  958.                     if (!call_user_func(array('Validate', $data['validate']), $value, (int)Configuration::get('PS_ALLOW_HTML_IFRAME')))
  959.                         $res = false;
  960.                 }
  961.                 else
  962.                 {
  963.                     if (!call_user_func(array('Validate', $data['validate']), $value))
  964.                         $res = false;
  965.                 }
  966.                 if (!$res)
  967.                 {
  968.                     if ($human_errors)
  969.                             return sprintf(Tools::displayError('The %s field is invalid.'), $this->displayFieldName($field, get_class($this)));
  970.                     else
  971.                         return 'Property '.get_class($this).'->'.$field.' is not valid';
  972.                 }
  973.             }
  974.         }
  975.  
  976.         return true;
  977.     }
  978.  
  979.     public static function displayFieldName($field, $class = __CLASS__, $htmlentities = true, Context $context = null)
  980.     {
  981.         global $_FIELDS;
  982.  
  983.         if ($_FIELDS === null && file_exists(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php'))
  984.             include_once(_PS_TRANSLATIONS_DIR_.Context::getContext()->language->iso_code.'/fields.php');
  985.  
  986.         $key = $class.'_'.md5($field);
  987.         return ((is_array($_FIELDS) && array_key_exists($key, $_FIELDS)) ? ($htmlentities ? htmlentities($_FIELDS[$key], ENT_QUOTES, 'utf-8') : $_FIELDS[$key]) : $field);
  988.     }
  989.  
  990.     /**
  991.     * TODO: refactor rename all calls to this to validateController
  992.     * @deprecated since 1.5 use validateController instead
  993.     */
  994.     public function validateControler($htmlentities = true)
  995.     {
  996.         Tools::displayAsDeprecated();
  997.         return $this->validateController($htmlentities);
  998.     }
  999.  
  1000.     public function validateController($htmlentities = true)
  1001.     {
  1002.         $this->cacheFieldsRequiredDatabase();
  1003.         $errors = array();
  1004.         $required_fields_database = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();
  1005.         foreach ($this->def['fields'] as $field => $data)
  1006.         {
  1007.             $value = Tools::getValue($field, $this->{$field});     
  1008.             // Check if field is required by user
  1009.             if (in_array($field, $required_fields_database))
  1010.                 $data['required'] = true;
  1011.            
  1012.             // Checking for required fields
  1013.             if (isset($data['required']) && $data['required'] && empty($value) && $value !== '0')
  1014.                 if (!$this->id || $field != 'passwd')
  1015.                     $errors[$field] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is required.');
  1016.  
  1017.             // Checking for maximum fields sizes
  1018.             if (isset($data['size']) && !empty($value) && Tools::strlen($value) > $data['size'])
  1019.                 $errors[$field] = sprintf(
  1020.                     Tools::displayError('%1$s is too long. Maximum length: %2$d'),
  1021.                     self::displayFieldName($field, get_class($this), $htmlentities),
  1022.                     $data['size']
  1023.                 );
  1024.  
  1025.             // Checking for fields validity
  1026.             // Hack for postcode required for country which does not have postcodes
  1027.             if (!empty($value) || $value === '0' || ($field == 'postcode' && $value == '0'))
  1028.             {
  1029.                 if (isset($data['validate']) && !Validate::$data['validate']($value) && (!empty($value) || $data['required']))
  1030.                     $errors[$field] = '<b>'.self::displayFieldName($field, get_class($this), $htmlentities).'</b> '.Tools::displayError('is invalid.');
  1031.                 else
  1032.                 {
  1033.                     if (isset($data['copy_post']) && !$data['copy_post'])
  1034.                         continue;
  1035.                     if ($field == 'passwd')
  1036.                     {
  1037.                         if ($value = Tools::getValue($field))
  1038.                             $this->{$field} = Tools::encrypt($value);
  1039.                     }
  1040.                     else
  1041.                         $this->{$field} = $value;
  1042.                 }
  1043.             }
  1044.         }
  1045.         return $errors;
  1046.     }
  1047.  
  1048.     public function getWebserviceParameters($ws_params_attribute_name = null)
  1049.     {
  1050.         $this->cacheFieldsRequiredDatabase();
  1051.         $default_resource_parameters = array(
  1052.             'objectSqlId' => $this->def['primary'],
  1053.             'retrieveData' => array(
  1054.                 'className' => get_class($this),
  1055.                 'retrieveMethod' => 'getWebserviceObjectList',
  1056.                 'params' => array(),
  1057.                 'table' => $this->def['table'],
  1058.             ),
  1059.             'fields' => array(
  1060.                 'id' => array('sqlId' => $this->def['primary'], 'i18n' => false),
  1061.             ),
  1062.         );
  1063.  
  1064.         if ($ws_params_attribute_name === null)
  1065.             $ws_params_attribute_name = 'webserviceParameters';
  1066.  
  1067.         if (!isset($this->{$ws_params_attribute_name}['objectNodeName']))
  1068.             $default_resource_parameters['objectNodeName'] = $this->def['table'];
  1069.         if (!isset($this->{$ws_params_attribute_name}['objectsNodeName']))
  1070.             $default_resource_parameters['objectsNodeName'] = $this->def['table'].'s';
  1071.  
  1072.         if (isset($this->{$ws_params_attribute_name}['associations']))
  1073.             foreach ($this->{$ws_params_attribute_name}['associations'] as $assoc_name => &$association)
  1074.             {
  1075.                 if (!array_key_exists('setter', $association) || (isset($association['setter']) && !$association['setter']))
  1076.                     $association['setter'] = Tools::toCamelCase('set_ws_'.$assoc_name);
  1077.                 if (!array_key_exists('getter', $association))
  1078.                     $association['getter'] = Tools::toCamelCase('get_ws_'.$assoc_name);
  1079.             }
  1080.  
  1081.         if (isset($this->{$ws_params_attribute_name}['retrieveData']) && isset($this->{$ws_params_attribute_name}['retrieveData']['retrieveMethod']))
  1082.             unset($default_resource_parameters['retrieveData']['retrieveMethod']);
  1083.  
  1084.         $resource_parameters = array_merge_recursive($default_resource_parameters, $this->{$ws_params_attribute_name});
  1085.  
  1086.         $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)]) ? self::$fieldsRequiredDatabase[get_class($this)] : array());
  1087.         foreach ($this->def['fields'] as $field_name => $details)
  1088.         {
  1089.             if (!isset($resource_parameters['fields'][$field_name]))
  1090.                 $resource_parameters['fields'][$field_name] = array();
  1091.             $current_field = array();
  1092.             $current_field['sqlId'] = $field_name;
  1093.             if (isset($details['size']))
  1094.                 $current_field['maxSize'] = $details['size'];
  1095.             if (isset($details['lang']))
  1096.                 $current_field['i18n'] = $details['lang'];
  1097.             else
  1098.                 $current_field['i18n'] = false;
  1099.             if ((isset($details['required']) && $details['required'] === true) || in_array($field_name, $required_fields))
  1100.                 $current_field['required'] = true;
  1101.             else
  1102.                 $current_field['required'] = false;
  1103.             if (isset($details['validate']))
  1104.             {
  1105.                 $current_field['validateMethod'] = (
  1106.                                 array_key_exists('validateMethod', $resource_parameters['fields'][$field_name]) ?
  1107.                                 array_merge($resource_parameters['fields'][$field_name]['validateMethod'], array($details['validate'])) :
  1108.                                 array($details['validate'])
  1109.                             );
  1110.             }
  1111.             $resource_parameters['fields'][$field_name] = array_merge($resource_parameters['fields'][$field_name], $current_field);
  1112.         }
  1113.         if (isset($this->date_add))
  1114.             $resource_parameters['fields']['date_add']['setter'] = false;
  1115.         if (isset($this->date_upd))
  1116.             $resource_parameters['fields']['date_upd']['setter'] = false;
  1117.         foreach ($resource_parameters['fields'] as $key => $resource_parameters_field)
  1118.             if (!isset($resource_parameters_field['sqlId']))
  1119.                 $resource_parameters['fields'][$key]['sqlId'] = $key;
  1120.         return $resource_parameters;
  1121.     }
  1122.  
  1123.     public function getWebserviceObjectList($sql_join, $sql_filter, $sql_sort, $sql_limit)
  1124.     {
  1125.         $assoc = Shop::getAssoTable($this->def['table']);
  1126.         $class_name = WebserviceRequest::$ws_current_classname;
  1127.         $vars = get_class_vars($class_name);
  1128.         if ($assoc !== false)
  1129.         {
  1130.             if ($assoc['type'] !== 'fk_shop')
  1131.             {
  1132.                 $multi_shop_join = ' LEFT JOIN `'._DB_PREFIX_.bqSQL($this->def['table']).'_'.bqSQL($assoc['type']).'`
  1133.                                         AS `multi_shop_'.bqSQL($this->def['table']).'`
  1134.                                         ON (main.`'.bqSQL($this->def['primary']).'` = `multi_shop_'.bqSQL($this->def['table']).'`.`'.bqSQL($this->def['primary']).'`)';
  1135.                 $sql_filter = 'AND `multi_shop_'.bqSQL($this->def['table']).'`.id_shop = '.Context::getContext()->shop->id.' '.$sql_filter;
  1136.                 $sql_join = $multi_shop_join.' '.$sql_join;
  1137.             }
  1138.             else
  1139.             {
  1140.                 $vars = get_class_vars($class_name);
  1141.                 foreach ($vars['shopIDs'] as $id_shop)
  1142.                     $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).')' : '').')';
  1143.                
  1144.                 $prepend = '';
  1145.                 if (count($or))
  1146.                     $prepend = 'AND ('.implode('OR', $or).')';
  1147.                 $sql_filter = $prepend.' '.$sql_filter;
  1148.             }
  1149.         }
  1150.         $query = '
  1151.         SELECT DISTINCT main.`'.bqSQL($this->def['primary']).'` FROM `'._DB_PREFIX_.bqSQL($this->def['table']).'` AS main
  1152.         '.$sql_join.'
  1153.         WHERE 1 '.$sql_filter.'
  1154.         '.($sql_sort != '' ? $sql_sort : '').'
  1155.         '.($sql_limit != '' ? $sql_limit : '');
  1156.         return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($query);
  1157.     }
  1158.  
  1159.     public function validateFieldsRequiredDatabase($htmlentities = true)
  1160.     {
  1161.         $this->cacheFieldsRequiredDatabase();
  1162.         $errors = array();
  1163.         $required_fields = (isset(self::$fieldsRequiredDatabase[get_class($this)])) ? self::$fieldsRequiredDatabase[get_class($this)] : array();
  1164.  
  1165.         foreach ($this->def['fields'] as $field => $data)
  1166.         {
  1167.             if (!in_array($field, $required_fields))
  1168.                 continue;
  1169.  
  1170.             if (!method_exists('Validate', $data['validate']))
  1171.                 throw new PrestaShopException('Validation function not found. '.$data['validate']);
  1172.  
  1173.             $value = Tools::getValue($field);
  1174.  
  1175.             if (empty($value))
  1176.                 $errors[$field] = sprintf(Tools::displayError('The field %s is required.'), self::displayFieldName($field, get_class($this), $htmlentities));
  1177.         }
  1178.  
  1179.         return $errors;
  1180.     }
  1181.  
  1182.     public function getFieldsRequiredDatabase($all = false)
  1183.     {
  1184.         return Db::getInstance()->executeS('
  1185.         SELECT id_required_field, object_name, field_name
  1186.         FROM '._DB_PREFIX_.'required_field
  1187.         '.(!$all ? 'WHERE object_name = \''.pSQL(get_class($this)).'\'' : ''));
  1188.     }
  1189.    
  1190.     public function cacheFieldsRequiredDatabase()
  1191.     {
  1192.         if (!is_array(self::$fieldsRequiredDatabase))
  1193.         {
  1194.             $fields = $this->getfieldsRequiredDatabase(true);
  1195.             if ($fields)
  1196.                 foreach ($fields as $row)
  1197.                     self::$fieldsRequiredDatabase[$row['object_name']][(int)$row['id_required_field']] = pSQL($row['field_name']);
  1198.             else
  1199.                 self::$fieldsRequiredDatabase = array();
  1200.         }
  1201.     }
  1202.  
  1203.     public function addFieldsRequiredDatabase($fields)
  1204.     {
  1205.         if (!is_array($fields))
  1206.             return false;
  1207.  
  1208.         if (!Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'required_field WHERE object_name = \''.get_class($this).'\''))
  1209.             return false;
  1210.  
  1211.         foreach ($fields as $field)
  1212.             if (!Db::getInstance()->insert('required_field', array('object_name' => get_class($this), 'field_name' => pSQL($field))))
  1213.                 return false;
  1214.         return true;
  1215.     }
  1216.  
  1217.     public function clearCache($all = false)
  1218.     {
  1219.         if ($all)
  1220.             Cache::clean('objectmodel_'.$this->def['classname'].'_*');
  1221.         elseif ($this->id)
  1222.             Cache::clean('objectmodel_'.$this->def['classname'].'_'.(int)$this->id.'_*');
  1223.     }
  1224.  
  1225.     /**
  1226.      * Check if current object is associated to a shop
  1227.      *
  1228.      * @since 1.5.0
  1229.      * @param int $id_shop
  1230.      * @return bool
  1231.      */
  1232.     public function isAssociatedToShop($id_shop = null)
  1233.     {
  1234.         if ($id_shop === null)
  1235.             $id_shop = Context::getContext()->shop->id;
  1236.  
  1237.         $cache_id = 'objectmodel_shop_'.$this->def['classname'].'_'.(int)$this->id.'-'.(int)$id_shop;
  1238.         if (!Cache::isStored($cache_id))
  1239.         {
  1240.             $sql = 'SELECT id_shop
  1241.                     FROM `'.pSQL(_DB_PREFIX_.$this->def['table']).'_shop`
  1242.                     WHERE `'.$this->def['primary'].'` = '.(int)$this->id.'
  1243.                         AND id_shop = '.(int)$id_shop;
  1244.             Cache::store($cache_id, (bool)Db::getInstance()->getValue($sql));
  1245.         }
  1246.         return Cache::retrieve($cache_id);
  1247.     }
  1248.  
  1249.     /**
  1250.      * This function associate an item to its context
  1251.      *
  1252.      * @param int|array $id_shops
  1253.      * @return boolean
  1254.      */
  1255.     public function associateTo($id_shops)
  1256.     {
  1257.         if (!$this->id)
  1258.             return;
  1259.  
  1260.         if (!is_array($id_shops))
  1261.             $id_shops = array($id_shops);
  1262.  
  1263.         $data = array();
  1264.         foreach ($id_shops as $id_shop)
  1265.         {
  1266.             if (!$this->isAssociatedToShop($id_shop))
  1267.                 $data[] = array(
  1268.                     $this->def['primary'] => (int)$this->id,
  1269.                     'id_shop' => (int)$id_shop,
  1270.                 );
  1271.         }
  1272.  
  1273.         if ($data)
  1274.             return Db::getInstance()->insert($this->def['table'].'_shop', $data);
  1275.         return true;
  1276.     }
  1277.  
  1278.     /**
  1279.      * Get the list of associated id_shop
  1280.      *
  1281.      * @since 1.5.0
  1282.      * @return array
  1283.      */
  1284.     public function getAssociatedShops()
  1285.     {
  1286.         if (!Shop::isTableAssociated($this->def['table']))
  1287.             return array();
  1288.  
  1289.         $list = array();
  1290.         $sql = 'SELECT id_shop FROM `'._DB_PREFIX_.$this->def['table'].'_shop` WHERE `'.$this->def['primary'].'` = '.(int)$this->id;
  1291.         foreach (Db::getInstance()->executeS($sql) as $row)
  1292.             $list[] = $row['id_shop'];
  1293.         return $list;
  1294.     }
  1295.  
  1296.     /**
  1297.      * @since 1.5.0
  1298.      */
  1299.     public function duplicateShops($id)
  1300.     {
  1301.         if (!Shop::isTableAssociated($this->def['table']))
  1302.             return false;
  1303.  
  1304.         $sql = 'SELECT id_shop
  1305.                 FROM '._DB_PREFIX_.$this->def['table'].'_shop
  1306.                 WHERE '.$this->def['primary'].' = '.(int)$id;
  1307.         if ($results = Db::getInstance()->executeS($sql))
  1308.         {
  1309.             $ids = array();
  1310.             foreach ($results as $row)
  1311.                 $ids[] = $row['id_shop'];
  1312.             return $this->associateTo($ids);
  1313.         }
  1314.  
  1315.         return false;
  1316.     }
  1317.  
  1318.     /**
  1319.      * Check if there is more than one entries in associated shop table for current entity
  1320.      *
  1321.      * @since 1.5.0
  1322.      * @return bool
  1323.      */
  1324.     public function hasMultishopEntries()
  1325.     {
  1326.         if (!Shop::isTableAssociated($this->def['table']) || !Shop::isFeatureActive())
  1327.             return false;
  1328.         return (bool)Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.$this->def['table'].'_shop` WHERE `'.$this->def['primary'].'` = '.(int)$this->id);
  1329.     }
  1330.  
  1331.     public function isMultishop()
  1332.     {
  1333.         return Shop::isTableAssociated($this->def['table']) || !empty($this->def['multilang_shop']);
  1334.     }
  1335.    
  1336.     public function isMultiShopField($field)
  1337.     {
  1338.         return (isset($this->def['fields'][$field]) && isset($this->def['fields'][$field]['shop']) && $this->def['fields'][$field]['shop']);
  1339.     }
  1340.  
  1341.     public function isLangMultishop()
  1342.     {
  1343.         return !empty($this->def['multilang']) && !empty($this->def['multilang_shop']);
  1344.     }
  1345.  
  1346.     /**
  1347.      * Update a table and splits the common datas and the shop datas
  1348.      *
  1349.      * @since 1.5.0
  1350.      * @param string $classname
  1351.      * @param array $data
  1352.      * @param string $where
  1353.      * @param string $specific_where Only executed for common table
  1354.      * @return bool
  1355.      */
  1356.     public static function updateMultishopTable($classname, $data, $where = '', $specific_where = '')
  1357.     {
  1358.         $def = ObjectModel::getDefinition($classname);
  1359.         $update_data = array();
  1360.         foreach ($data as $field => $value)
  1361.         {
  1362.             if (!isset($def['fields'][$field]))
  1363.                 continue;
  1364.  
  1365.             if (!empty($def['fields'][$field]['shop']))
  1366.             {
  1367.                 $update_data[] = "a.$field = '$value'";
  1368.                 $update_data[] = "{$def['table']}_shop.$field = '$value'";
  1369.             }
  1370.             else
  1371.                 $update_data[] = "a.$field = '$value'";
  1372.         }
  1373.  
  1374.         $sql = 'UPDATE '._DB_PREFIX_.$def['table'].' a
  1375.                 '.Shop::addSqlAssociation($def['table'], 'a', true, null, true).'
  1376.                 SET '.implode(', ', $update_data).
  1377.                 (!empty($where) ? ' WHERE '.$where : '');
  1378.         return Db::getInstance()->execute($sql);
  1379.     }
  1380.  
  1381.     /**
  1382.      * Delete images associated with the object
  1383.      *
  1384.      * @return bool success
  1385.      */
  1386.     public function deleteImage($force_delete = false)
  1387.     {
  1388.         if (!$this->id)
  1389.             return false;
  1390.        
  1391.         if ($force_delete || !$this->hasMultishopEntries())
  1392.         {
  1393.             /* Deleting object images and thumbnails (cache) */
  1394.             if ($this->image_dir)
  1395.             {
  1396.                 if (file_exists($this->image_dir.$this->id.'.'.$this->image_format)
  1397.                     && !unlink($this->image_dir.$this->id.'.'.$this->image_format))
  1398.                     return false;
  1399.             }
  1400.             if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format)
  1401.                 && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'.'.$this->image_format))
  1402.                 return false;
  1403.             if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format)
  1404.                 && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'.'.$this->image_format))
  1405.                 return false;
  1406.    
  1407.             $types = ImageType::getImagesTypes();
  1408.             foreach ($types as $image_type)
  1409.                 if (file_exists($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format)
  1410.                 && !unlink($this->image_dir.$this->id.'-'.stripslashes($image_type['name']).'.'.$this->image_format))
  1411.                     return false;
  1412.         }
  1413.         return true;
  1414.     }
  1415.  
  1416.     /**
  1417.      * Specify if an ObjectModel is already in database
  1418.      *
  1419.      * @param int $id_entity
  1420.      * @param string $table
  1421.      * @return boolean
  1422.      */
  1423.     public static function existsInDatabase($id_entity, $table)
  1424.     {
  1425.         $row = Db::getInstance()->getRow('
  1426.             SELECT `id_'.$table.'` as id
  1427.             FROM `'._DB_PREFIX_.$table.'` e
  1428.             WHERE e.`id_'.$table.'` = '.(int)$id_entity
  1429.         );
  1430.  
  1431.         return isset($row['id']);
  1432.     }
  1433.  
  1434.     /**
  1435.      * This method is allow to know if a entity is currently used
  1436.      * @since 1.5.0.1
  1437.      * @param string $table name of table linked to entity
  1438.      * @param bool $has_active_column true if the table has an active column
  1439.      * @return bool
  1440.      */
  1441.     public static function isCurrentlyUsed($table = null, $has_active_column = false)
  1442.     {
  1443.         if ($table === null)
  1444.             $table = self::$definition['table'];
  1445.  
  1446.         $query = new DbQuery();
  1447.         $query->select('`id_'.pSQL($table).'`');
  1448.         $query->from($table);
  1449.         if ($has_active_column)
  1450.             $query->where('`active` = 1');
  1451.         return (bool)Db::getInstance()->getValue($query);
  1452.     }
  1453.  
  1454.     /**
  1455.      * Fill an object with given data. Data must be an array with this syntax: array(objProperty => value, objProperty2 => value, etc.)
  1456.      *
  1457.      * @since 1.5.0
  1458.      * @param array $data
  1459.      * @param int $id_lang
  1460.      */
  1461.     public function hydrate(array $data, $id_lang = null)
  1462.     {
  1463.         $this->id_lang = $id_lang;
  1464.         if (isset($data[$this->def['primary']]))
  1465.             $this->id = $data[$this->def['primary']];
  1466.         foreach ($data as $key => $value)
  1467.             if (array_key_exists($key, $this))
  1468.                 $this->$key = $value;
  1469.     }
  1470.  
  1471.     /**
  1472.      * Fill (hydrate) a list of objects in order to get a collection of these objects
  1473.      *
  1474.      * @since 1.5.0
  1475.      * @param string $class Class of objects to hydrate
  1476.      * @param array $datas List of data (multi-dimensional array)
  1477.      * @param int $id_lang
  1478.      * @return array
  1479.      */
  1480.     public static function hydrateCollection($class, array $datas, $id_lang = null)
  1481.     {
  1482.         if (!class_exists($class))
  1483.             throw new PrestaShopException("Class '$class' not found");
  1484.  
  1485.         $collection = array();
  1486.         $rows = array();
  1487.         if ($datas)
  1488.         {
  1489.             $definition = ObjectModel::getDefinition($class);
  1490.             if (!array_key_exists($definition['primary'], $datas[0]))
  1491.                 throw new PrestaShopException("Identifier '{$definition['primary']}' not found for class '$class'");
  1492.  
  1493.             foreach ($datas as $row)
  1494.             {
  1495.                 // Get object common properties
  1496.                 $id = $row[$definition['primary']];
  1497.                 if (!isset($rows[$id]))
  1498.                     $rows[$id] = $row;
  1499.  
  1500.                 // Get object lang properties
  1501.                 if (isset($row['id_lang']) && !$id_lang)
  1502.                     foreach ($definition['fields'] as $field => $data)
  1503.                         if (!empty($data['lang']))
  1504.                         {
  1505.                             if (!is_array($rows[$id][$field]))
  1506.                                 $rows[$id][$field] = array();
  1507.                             $rows[$id][$field][$row['id_lang']] = $row[$field];
  1508.                         }
  1509.             }
  1510.         }
  1511.  
  1512.         // Hydrate objects
  1513.         foreach ($rows as $row)
  1514.         {
  1515.             $obj = new $class;
  1516.             $obj->hydrate($row, $id_lang);
  1517.             $collection[] = $obj;
  1518.         }
  1519.         return $collection;
  1520.     }
  1521.  
  1522.     /**
  1523.      * Get object definition
  1524.      *
  1525.      * @param string $class Name of object
  1526.      * @param string $field Name of field if we want the definition of one field only
  1527.      * @return array
  1528.      */
  1529.     public static function getDefinition($class, $field = null)
  1530.     {
  1531.         if (is_object($class))
  1532.             $class = get_class($class);
  1533.  
  1534.         if ($field === null)
  1535.             $cache_id = 'objectmodel_def_'.$class;
  1536.  
  1537.         if ($field !== null || !Cache::isStored($cache_id))
  1538.         {
  1539.             $reflection = new ReflectionClass($class);
  1540.             $definition = $reflection->getStaticPropertyValue('definition');
  1541.  
  1542.             $definition['classname'] = $class;
  1543.  
  1544.             if (!empty($definition['multilang']))
  1545.                 $definition['associations'][Collection::LANG_ALIAS] = array(
  1546.                     'type' => self::HAS_MANY,
  1547.                     'field' => $definition['primary'],
  1548.                     'foreign_field' => $definition['primary'],
  1549.                 );
  1550.        
  1551.             if ($field)
  1552.                 return isset($definition['fields'][$field]) ? $definition['fields'][$field] : null;
  1553.  
  1554.             Cache::store($cache_id, $definition);
  1555.             return $definition;
  1556.         }
  1557.  
  1558.         return Cache::retrieve($cache_id);
  1559.     }
  1560.  
  1561.     /**
  1562.      * Retrocompatibility for classes without $definition static
  1563.      * Remove this in 1.6 !
  1564.      *
  1565.      * @since 1.5.0
  1566.      */
  1567.     protected function setDefinitionRetrocompatibility()
  1568.     {
  1569.         // Retrocompatibility with $table property ($definition['table'])
  1570.         if (isset($this->def['table']))
  1571.             $this->table = $this->def['table'];
  1572.         else
  1573.             $this->def['table'] = $this->table;
  1574.  
  1575.         // Retrocompatibility with $identifier property ($definition['primary'])
  1576.         if (isset($this->def['primary']))
  1577.             $this->identifier = $this->def['primary'];
  1578.         else
  1579.             $this->def['primary'] = $this->identifier;
  1580.  
  1581.         // Check multilang retrocompatibility
  1582.         if (method_exists($this, 'getTranslationsFieldsChild'))
  1583.             $this->def['multilang'] = true;
  1584.  
  1585.         // Retrocompatibility with $fieldsValidate, $fieldsRequired and $fieldsSize properties ($definition['fields'])
  1586.         if (isset($this->def['fields']))
  1587.         {
  1588.             foreach ($this->def['fields'] as $field => $data)
  1589.             {
  1590.                 $suffix = (isset($data['lang']) && $data['lang']) ? 'Lang' : '';
  1591.                 if (isset($data['validate']))
  1592.                     $this->{'fieldsValidate'.$suffix}[$field] = $data['validate'];
  1593.                 if (isset($data['required']) && $data['required'])
  1594.                     $this->{'fieldsRequired'.$suffix}[] = $field;
  1595.                 if (isset($data['size']))
  1596.                     $this->{'fieldsSize'.$suffix}[$field] = $data['size'];
  1597.             }
  1598.         }
  1599.         else
  1600.         {
  1601.             $this->def['fields'] = array();
  1602.             $suffixs = array('', 'Lang');
  1603.             foreach($suffixs as $suffix)
  1604.             {
  1605.                 foreach ($this->{'fieldsValidate'.$suffix} as $field => $validate)
  1606.                 {
  1607.                     $this->def['fields'][$field]['validate'] = $validate;
  1608.                     if ($suffix == 'Lang')
  1609.                         $this->def['fields'][$field]['lang'] = true;
  1610.                 }
  1611.                 foreach ($this->{'fieldsRequired'.$suffix} as $field)
  1612.                 {
  1613.                     $this->def['fields'][$field]['required'] = true;
  1614.                     if ($suffix == 'Lang')
  1615.                         $this->def['fields'][$field]['lang'] = true;
  1616.                 }
  1617.                 foreach ($this->{'fieldsSize'.$suffix} as $field => $size)
  1618.                 {
  1619.                     $this->def['fields'][$field]['size'] = $size;
  1620.                     if ($suffix == 'Lang')
  1621.                         $this->def['fields'][$field]['lang'] = true;
  1622.                 }
  1623.             }
  1624.         }
  1625.     }
  1626.  
  1627.     /**
  1628.      * Return the field value for the specified language if the field is multilang, else the field value.
  1629.      *
  1630.      * @param $field_name
  1631.      * @param null $id_lang
  1632.      * @return mixed
  1633.      * @throws PrestaShopException
  1634.      * @since 1.5
  1635.      */
  1636.     public function getFieldByLang($field_name, $id_lang = null)
  1637.     {
  1638.         $definition = ObjectModel::getDefinition($this);
  1639.         // Is field in definition?
  1640.         if ($definition && isset($definition['fields'][$field_name]))
  1641.         {
  1642.             $field = $definition['fields'][$field_name];
  1643.             // Is field multilang?
  1644.             if (isset($field['lang']) && $field['lang'])
  1645.             {
  1646.                 if (is_array($this->{$field_name}))
  1647.                     return $this->{$field_name}[$id_lang ? $id_lang : Context::getContext()->language->id];
  1648.             }
  1649.             return $this->{$field_name};
  1650.         }
  1651.         else
  1652.             throw new PrestaShopException('Could not load field from definition.');
  1653.     }
  1654.  
  1655.     /**
  1656.      * Set a list of specific fields to update
  1657.      * array(field1 => true, field2 => false, langfield1 => array(1 => true, 2 => false))
  1658.      *
  1659.      * @since 1.5.0
  1660.      * @param array $fields
  1661.      */
  1662.     public function setFieldsToUpdate(array $fields)
  1663.     {
  1664.         $this->update_fields = $fields;
  1665.     }
  1666. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement